Core/Phasing: Implemented db spawns in personal phases

Port From (https://github.com/TrinityCore/TrinityCore/commit/8fabe5a3aacf7797f03d074ab8434f445be64955)
This commit is contained in:
hondacrx
2022-01-27 14:01:49 -05:00
parent 6c0e21b4de
commit 7531735669
18 changed files with 555 additions and 123 deletions
+1 -1
View File
@@ -770,7 +770,7 @@ namespace Game.Chat
else if (target.GetDBPhase() < 0)
handler.SendSysMessage($"Target creature's PhaseGroup in DB: {Math.Abs(target.GetDBPhase())}");
PhasingHandler.PrintToChat(handler, target.GetPhaseShift());
PhasingHandler.PrintToChat(handler, target);
return true;
}
@@ -282,9 +282,9 @@ namespace Game.Chat
obj.Relocate(x, y, z, obj.GetOrientation());
// update which cell has this gameobject registered for loading
Global.ObjectMgr.RemoveGameObjectFromGrid(guidLow, obj.GetGameObjectData());
Global.ObjectMgr.RemoveGameObjectFromGrid(obj.GetGameObjectData());
obj.SaveToDB();
Global.ObjectMgr.AddGameObjectToGrid(guidLow, obj.GetGameObjectData());
Global.ObjectMgr.AddGameObjectToGrid(obj.GetGameObjectData());
// Generate a completely new spawn with new guid
// client caches recently deleted objects and brings them back to life
@@ -627,7 +627,7 @@ namespace Game.Chat
return false;
// TODO: is it really necessary to add both the real and DB table guid here ?
Global.ObjectMgr.AddGameObjectToGrid(spawnId, Global.ObjectMgr.GetGameObjectData(spawnId));
Global.ObjectMgr.AddGameObjectToGrid(Global.ObjectMgr.GetGameObjectData(spawnId));
handler.SendSysMessage(CypherStrings.GameobjectAdd, objectId, objectInfo.name, spawnId, player.GetPositionX(), player.GetPositionY(), player.GetPositionZ());
return true;
}
+2 -2
View File
@@ -981,7 +981,7 @@ namespace Game.Chat
if (liquidStatus != null)
handler.SendSysMessage(CypherStrings.LiquidStatus, liquidStatus.level, liquidStatus.depth_level, liquidStatus.entry, liquidStatus.type_flags, status);
PhasingHandler.PrintToChat(handler, obj.GetPhaseShift());
PhasingHandler.PrintToChat(handler, obj);
return true;
}
@@ -1774,7 +1774,7 @@ namespace Game.Chat
// Output XIII. phases
if (target)
PhasingHandler.PrintToChat(handler, target.GetPhaseShift());
PhasingHandler.PrintToChat(handler, target);
// Output XIV. LANG_PINFO_CHR_MONEY
ulong gold = money / MoneyConstants.Gold;
+5 -5
View File
@@ -174,7 +174,7 @@ namespace Game.Chat
if (data != null)
handler.SendSysMessage(CypherStrings.NpcinfoPhases, data.PhaseId, data.PhaseGroup);
PhasingHandler.PrintToChat(handler, target.GetPhaseShift());
PhasingHandler.PrintToChat(handler, target);
handler.SendSysMessage(CypherStrings.NpcinfoArmor, target.GetArmor());
handler.SendSysMessage(CypherStrings.NpcinfoPosition, target.GetPositionX(), target.GetPositionY(), target.GetPositionZ());
@@ -239,9 +239,9 @@ namespace Game.Chat
return false;
}
Global.ObjectMgr.RemoveCreatureFromGrid(lowguid, data);
Global.ObjectMgr.RemoveCreatureFromGrid(data);
data.SpawnPoint.Relocate(player);
Global.ObjectMgr.AddCreatureToGrid(lowguid, data);
Global.ObjectMgr.AddCreatureToGrid(data);
// update position in DB
PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.UPD_CREATURE_POSITION);
@@ -668,7 +668,7 @@ namespace Game.Chat
if (creaturePassenger != null)
{
creaturePassenger.SaveToDB((uint)trans.GetGoInfo().MoTransport.SpawnMap, new List<Difficulty>() { map.GetDifficultyID() });
Global.ObjectMgr.AddCreatureToGrid(guid, data);
Global.ObjectMgr.AddCreatureToGrid(data);
}
return true;
}
@@ -689,7 +689,7 @@ namespace Game.Chat
if (!creature)
return false;
Global.ObjectMgr.AddCreatureToGrid(db_guid, Global.ObjectMgr.GetCreatureData(db_guid));
Global.ObjectMgr.AddCreatureToGrid(Global.ObjectMgr.GetCreatureData(db_guid));
return true;
}
+3 -3
View File
@@ -1204,7 +1204,7 @@ namespace Game.Entities
}
uint mapId = GetTransport() ? (uint)GetTransport().GetGoInfo().MoTransport.SpawnMap : GetMapId();
SaveToDB(mapId, data.spawnDifficulties);
SaveToDB(mapId, data.SpawnDifficulties);
}
public virtual void SaveToDB(uint mapid, List<Difficulty> spawnDifficulties)
@@ -1274,7 +1274,7 @@ namespace Game.Entities
// prevent add data integrity problems
data.movementType = (byte)(m_wanderDistance == 0 && GetDefaultMovementType() == MovementGeneratorType.Random
? MovementGeneratorType.Idle : GetDefaultMovementType());
data.spawnDifficulties = spawnDifficulties;
data.SpawnDifficulties = spawnDifficulties;
data.npcflag = npcflag;
data.unit_flags = unitFlags;
data.unit_flags2 = unitFlags2;
@@ -1299,7 +1299,7 @@ namespace Game.Entities
stmt.AddValue(index++, m_spawnId);
stmt.AddValue(index++, GetEntry());
stmt.AddValue(index++, mapid);
stmt.AddValue(index++, data.spawnDifficulties.Empty() ? "" : string.Join(',', data.spawnDifficulties));
stmt.AddValue(index++, data.SpawnDifficulties.Empty() ? "" : string.Join(',', data.SpawnDifficulties));
stmt.AddValue(index++, data.PhaseId);
stmt.AddValue(index++, data.PhaseGroup);
stmt.AddValue(index++, displayId);
@@ -1018,7 +1018,7 @@ namespace Game.Entities
return;
}
SaveToDB(GetMapId(), data.spawnDifficulties);
SaveToDB(GetMapId(), data.SpawnDifficulties);
}
public void SaveToDB(uint mapid, List<Difficulty> spawnDifficulties)
@@ -1045,7 +1045,7 @@ namespace Game.Entities
data.spawntimesecs = (int)(m_spawnedByDefault ? m_respawnDelayTime : -m_respawnDelayTime);
data.animprogress = GetGoAnimProgress();
data.goState = GetGoState();
data.spawnDifficulties = spawnDifficulties;
data.SpawnDifficulties = spawnDifficulties;
data.artKit = (byte)GetGoArtKit();
if (data.spawnGroupData == null)
data.spawnGroupData = Global.ObjectMgr.GetDefaultSpawnGroup();
@@ -1063,7 +1063,7 @@ namespace Game.Entities
stmt.AddValue(index++, m_spawnId);
stmt.AddValue(index++, GetEntry());
stmt.AddValue(index++, mapid);
stmt.AddValue(index++, data.spawnDifficulties.Empty() ? "" : string.Join(",", data.spawnDifficulties));
stmt.AddValue(index++, data.SpawnDifficulties.Empty() ? "" : string.Join(",", data.SpawnDifficulties));
stmt.AddValue(index++, data.PhaseId);
stmt.AddValue(index++, data.PhaseGroup);
stmt.AddValue(index++, GetPositionX());
@@ -80,7 +80,7 @@ namespace Game.Entities
if (!pos.IsPositionValid())
return;
player.GetMap().LoadGrid(firstCamera.locations.X, firstCamera.locations.Y);
player.GetMap().LoadGridForActiveObject(pos.GetPositionX(), pos.GetPositionY(), player);
m_CinematicObject = player.SummonCreature(1, pos.posX, pos.posY, pos.posZ, 0.0f, TempSummonType.TimedDespawn, 5 * Time.Minute * Time.InMilliseconds);
if (m_CinematicObject)
{
+7
View File
@@ -2153,6 +2153,13 @@ namespace Game.Entities
TeleportTo(m_summon_location);
}
public override void OnPhaseChange()
{
base.OnPhaseChange();
GetMap().UpdatePersonalPhasesForPlayer(this);
}
//GM
public bool IsAcceptWhispers() { return m_ExtraFlags.HasAnyFlag(PlayerExtraFlags.AcceptWhispers); }
public void SetAcceptWhispers(bool on)
+1 -4
View File
@@ -1222,10 +1222,7 @@ namespace Game.Entities
public bool IsPossessed() { return HasUnitState(UnitState.Possessed); }
public void OnPhaseChange()
{
}
public virtual void OnPhaseChange() { }
public uint GetModelForForm(ShapeShiftForm form, uint spellId)
{
+4 -4
View File
@@ -1188,7 +1188,7 @@ namespace Game
CreatureData data = Global.ObjectMgr.GetCreatureData(guid);
if (data != null)
{
Global.ObjectMgr.AddCreatureToGrid(guid, data);
Global.ObjectMgr.AddCreatureToGrid(data);
// Spawn if necessary (loaded grids only)
Map map = Global.MapMgr.CreateBaseMap(data.MapId);
@@ -1212,7 +1212,7 @@ namespace Game
GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid);
if (data != null)
{
Global.ObjectMgr.AddGameObjectToGrid(guid, data);
Global.ObjectMgr.AddGameObjectToGrid(data);
// Spawn if necessary (loaded grids only)
// this base map checked as non-instanced and then only existed
Map map = Global.MapMgr.CreateBaseMap(data.MapId);
@@ -1264,7 +1264,7 @@ namespace Game
CreatureData data = Global.ObjectMgr.GetCreatureData(guid);
if (data != null)
{
Global.ObjectMgr.RemoveCreatureFromGrid(guid, data);
Global.ObjectMgr.RemoveCreatureFromGrid(data);
Global.MapMgr.DoForAllMapsWithMapId(data.MapId, map =>
{
@@ -1292,7 +1292,7 @@ namespace Game
GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid);
if (data != null)
{
Global.ObjectMgr.RemoveGameObjectFromGrid(guid, data);
Global.ObjectMgr.RemoveGameObjectFromGrid(data);
Global.MapMgr.DoForAllMapsWithMapId(data.MapId, map =>
{
+129 -50
View File
@@ -2901,7 +2901,7 @@ namespace Game
}
// they must have a possibility to meet (normal/heroic difficulty)
if (!master.spawnDifficulties.Intersect(slave.spawnDifficulties).Any())
if (!master.SpawnDifficulties.Intersect(slave.SpawnDifficulties).Any())
{
Log.outError(LogFilter.Sql, "LinkedRespawn: Creature '{0}' linking to '{1}' with not corresponding spawnMask", guidLow, linkedGuidLow);
error = true;
@@ -2939,7 +2939,7 @@ namespace Game
}
// they must have a possibility to meet (normal/heroic difficulty)
if (!master.spawnDifficulties.Intersect(slave.spawnDifficulties).Any())
if (!master.SpawnDifficulties.Intersect(slave.SpawnDifficulties).Any())
{
Log.outError(LogFilter.Sql, "LinkedRespawn: Creature '{0}' linking to '{1}' with not corresponding spawnMask", guidLow, linkedGuidLow);
error = true;
@@ -2977,7 +2977,7 @@ namespace Game
}
// they must have a possibility to meet (normal/heroic difficulty)
if (!master.spawnDifficulties.Intersect(slave.spawnDifficulties).Any())
if (!master.SpawnDifficulties.Intersect(slave.SpawnDifficulties).Any())
{
Log.outError(LogFilter.Sql, "LinkedRespawn: Creature '{0}' linking to '{1}' with not corresponding spawnMask", guidLow, linkedGuidLow);
error = true;
@@ -3015,7 +3015,7 @@ namespace Game
}
// they must have a possibility to meet (normal/heroic difficulty)
if (!master.spawnDifficulties.Intersect(slave.spawnDifficulties).Any())
if (!master.SpawnDifficulties.Intersect(slave.SpawnDifficulties).Any())
{
Log.outError(LogFilter.Sql, "LinkedRespawn: Creature '{0}' linking to '{1}' with not corresponding spawnMask", guidLow, linkedGuidLow);
error = true;
@@ -3420,7 +3420,7 @@ namespace Game
data.curhealth = result.Read<uint>(12);
data.curmana = result.Read<uint>(13);
data.movementType = result.Read<byte>(14);
data.spawnDifficulties = ParseSpawnDifficulties(result.Read<string>(15), "creature", guid, data.MapId, spawnMasks.LookupByKey(data.MapId));
data.SpawnDifficulties = ParseSpawnDifficulties(result.Read<string>(15), "creature", guid, data.MapId, spawnMasks.LookupByKey(data.MapId));
short gameEvent = result.Read<short>(16);
uint PoolId = result.Read<uint>(17);
data.npcflag = result.Read<ulong>(18);
@@ -3442,7 +3442,7 @@ namespace Game
continue;
}
if (data.spawnDifficulties.Empty())
if (data.SpawnDifficulties.Empty())
{
Log.outError(LogFilter.Sql, $"Table `creature` has creature (GUID: {guid}) that is not spawned in any difficulty, skipped.");
continue;
@@ -3566,7 +3566,7 @@ namespace Game
// Add to grid if not managed by the game event or pool system
if (gameEvent == 0 && PoolId == 0)
AddCreatureToGrid(guid, data);
AddCreatureToGrid(data);
creatureDataStorage[guid] = data;
count++;
@@ -3575,27 +3575,92 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} creatures in {1} ms", count, Time.GetMSTimeDiffToNow(time));
}
public void AddCreatureToGrid(ulong guid, CreatureData data)
public bool HasPersonalSpawns(uint mapid, Difficulty spawnMode, uint phaseId)
{
foreach (Difficulty difficulty in data.spawnDifficulties)
{
CellCoord cellCoord = GridDefines.ComputeCellCoord(data.SpawnPoint.GetPositionX(), data.SpawnPoint.GetPositionY());
var cellguids = CreateCellObjectGuids(data.MapId, difficulty, cellCoord.GetId());
cellguids.creatures.Add(guid);
}
return mapPersonalObjectGuidsStore.ContainsKey((mapid, spawnMode, phaseId));
}
public void RemoveCreatureFromGrid(ulong guid, CreatureData data)
{
foreach (Difficulty difficulty in data.spawnDifficulties)
{
CellCoord cellCoord = GridDefines.ComputeCellCoord(data.SpawnPoint.GetPositionX(), data.SpawnPoint.GetPositionY());
CellObjectGuids cellguids = GetCellObjectGuids(data.MapId, difficulty, cellCoord.GetId());
if (cellguids == null)
return;
cellguids.creatures.Remove(guid);
public CellObjectGuids GetCellPersonalObjectGuids(uint mapid, Difficulty spawnMode, uint phaseId, uint cell_id)
{
var guids = mapPersonalObjectGuidsStore.LookupByKey((mapid, spawnMode, phaseId));
if (guids != null)
return guids.LookupByKey(cell_id);
return null;
}
void AddSpawnDataToGrid(SpawnData data)
{
uint cellId = GridDefines.ComputeCellCoord(data.SpawnPoint.GetPositionX(), data.SpawnPoint.GetPositionY()).GetId();
bool isPersonalPhase = PhasingHandler.IsPersonalPhase(data.PhaseId);
if (!isPersonalPhase)
{
foreach (Difficulty difficulty in data.SpawnDifficulties)
{
var key = (data.MapId, difficulty);
if (!mapObjectGuidsStore.ContainsKey(key))
mapObjectGuidsStore[key] = new();
if (!mapObjectGuidsStore[key].ContainsKey(cellId))
mapObjectGuidsStore[key][cellId] = new();
mapObjectGuidsStore[key][cellId].AddSpawn(data);
}
}
else
{
foreach (Difficulty difficulty in data.SpawnDifficulties)
{
var key = (data.MapId, difficulty, data.PhaseId);
if (!mapPersonalObjectGuidsStore.ContainsKey(key))
mapPersonalObjectGuidsStore[key] = new();
if (!mapPersonalObjectGuidsStore[key].ContainsKey(cellId))
mapPersonalObjectGuidsStore[key][cellId] = new();
mapPersonalObjectGuidsStore[key][cellId].AddSpawn(data);
}
}
}
void RemoveSpawnDataFromGrid(SpawnData data)
{
uint cellId = GridDefines.ComputeCellCoord(data.SpawnPoint.GetPositionX(), data.SpawnPoint.GetPositionY()).GetId();
bool isPersonalPhase = PhasingHandler.IsPersonalPhase(data.PhaseId);
if (!isPersonalPhase)
{
foreach (Difficulty difficulty in data.SpawnDifficulties)
{
var key = (data.MapId, difficulty);
if (!mapObjectGuidsStore.ContainsKey(key) || !mapObjectGuidsStore[key].ContainsKey(cellId))
continue;
mapObjectGuidsStore[(data.MapId, difficulty)][cellId].RemoveSpawn(data);
}
}
else
{
foreach (Difficulty difficulty in data.SpawnDifficulties)
{
var key = (data.MapId, difficulty, data.PhaseId);
if (!mapPersonalObjectGuidsStore.ContainsKey(key) || !mapPersonalObjectGuidsStore[key].ContainsKey(cellId))
continue;
mapPersonalObjectGuidsStore[key][cellId].RemoveSpawn(data);
}
}
}
public void AddCreatureToGrid(CreatureData data)
{
AddSpawnDataToGrid(data);
}
public void RemoveCreatureFromGrid(CreatureData data)
{
RemoveSpawnDataFromGrid(data);
}
public ulong AddCreatureData(uint entry, uint mapId, Position pos, uint spawntimedelay)
{
CreatureTemplate cInfo = GetCreatureTemplate(entry);
@@ -3625,14 +3690,14 @@ namespace Game
data.curhealth = (uint)(Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, level, cInfo.HealthScalingExpansion, scaling.ContentTuningID, (Class)cInfo.UnitClass) * cInfo.ModHealth * cInfo.ModHealthExtra);
data.curmana = stats.GenerateMana(cInfo);
data.movementType = (byte)cInfo.MovementType;
data.spawnDifficulties.Add(Difficulty.None);
data.SpawnDifficulties.Add(Difficulty.None);
data.dbData = false;
data.npcflag = (uint)cInfo.Npcflag;
data.unit_flags = (uint)cInfo.UnitFlags;
data.dynamicflags = cInfo.DynamicFlags;
data.spawnGroupData = GetLegacySpawnGroup();
AddCreatureToGrid(spawnId, data);
AddCreatureToGrid(data);
// We use spawn coords to spawn
if (!map.Instanceable() && !map.IsRemovalGrid(data.SpawnPoint))
@@ -3721,7 +3786,7 @@ namespace Game
}
// they must have a possibility to meet (normal/heroic difficulty)
if (!master.spawnDifficulties.Intersect(slave.spawnDifficulties).Any())
if (!master.SpawnDifficulties.Intersect(slave.SpawnDifficulties).Any())
{
Log.outError(LogFilter.Sql, "LinkedRespawn: Creature '{0}' linking to '{1}' with not corresponding spawnMask", guidLow, linkedGuidLow);
return false;
@@ -3748,7 +3813,7 @@ namespace Game
CreatureData data = GetCreatureData(spawnId);
if (data != null)
{
RemoveCreatureFromGrid(spawnId, data);
RemoveCreatureFromGrid(data);
OnDeleteSpawnData(data);
}
@@ -4207,8 +4272,8 @@ namespace Game
}
data.goState = (GameObjectState)gostate;
data.spawnDifficulties = ParseSpawnDifficulties(result.Read<string>(14), "gameobject", guid, data.MapId, spawnMasks.LookupByKey(data.MapId));
if (data.spawnDifficulties.Empty())
data.SpawnDifficulties = ParseSpawnDifficulties(result.Read<string>(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.");
continue;
@@ -4324,7 +4389,7 @@ namespace Game
}
if (gameEvent == 0 && PoolId == 0) // if not this is to be managed by GameEvent System or Pool system
AddGameObjectToGrid(guid, data);
AddGameObjectToGrid(data);
gameObjectDataStorage[guid] = data;
++count;
@@ -4498,26 +4563,13 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} GameObjects for quests in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public void AddGameObjectToGrid(ulong guid, GameObjectData data)
public void AddGameObjectToGrid(GameObjectData data)
{
foreach (Difficulty difficulty in data.spawnDifficulties)
{
CellCoord cellCoord = GridDefines.ComputeCellCoord(data.SpawnPoint.GetPositionX(), data.SpawnPoint.GetPositionY());
var cellguids = CreateCellObjectGuids(data.MapId, difficulty, cellCoord.GetId());
cellguids.gameobjects.Add(guid);
}
AddSpawnDataToGrid(data);
}
public void RemoveGameObjectFromGrid(ulong guid, GameObjectData data)
public void RemoveGameObjectFromGrid(GameObjectData data)
{
foreach (Difficulty difficulty in data.spawnDifficulties)
{
CellCoord cellCoord = GridDefines.ComputeCellCoord(data.SpawnPoint.GetPositionX(), data.SpawnPoint.GetPositionY());
CellObjectGuids cellguids = GetCellObjectGuids(data.MapId, difficulty, cellCoord.GetId());
if (cellguids == null)
return;
cellguids.gameobjects.Remove(guid);
}
RemoveSpawnDataFromGrid(data);
}
public ulong AddGameObjectData(uint entry, uint mapId, Position pos, Quaternion rot, uint spawntimedelay)
{
@@ -4538,13 +4590,13 @@ namespace Game
data.rotation = rot;
data.spawntimesecs = (int)spawntimedelay;
data.animprogress = 100;
data.spawnDifficulties.Add(Difficulty.None);
data.SpawnDifficulties.Add(Difficulty.None);
data.goState = GameObjectState.Ready;
data.artKit = (byte)(goinfo.type == GameObjectTypes.ControlZone ? 21 : 0);
data.dbData = false;
data.spawnGroupData = GetLegacySpawnGroup();
AddGameObjectToGrid(spawnId, data);
AddGameObjectToGrid(data);
// Spawn if necessary (loaded grids only)
// We use spawn coords to spawn
@@ -4582,7 +4634,7 @@ namespace Game
GameObjectData data = GetGameObjectData(spawnId);
if (data != null)
{
RemoveGameObjectFromGrid(spawnId, data);
RemoveGameObjectFromGrid(data);
OnDeleteSpawnData(data);
}
@@ -10666,6 +10718,7 @@ namespace Game
//Maps
public Dictionary<uint, GameTele> gameTeleStorage = new();
Dictionary<(uint mapId, Difficulty difficulty), Dictionary<uint, CellObjectGuids>> mapObjectGuidsStore = new();
Dictionary<(uint mapId, Difficulty diffuculty, uint phaseId), Dictionary<uint, CellObjectGuids>> mapPersonalObjectGuidsStore = new();
Dictionary<uint, InstanceTemplate> instanceTemplateStorage = new();
public MultiMap<uint, GraveYardData> GraveYardStorage = new();
List<ushort> _transportMaps = new();
@@ -11064,6 +11117,32 @@ namespace Game
{
public SortedSet<ulong> creatures = new();
public SortedSet<ulong> gameobjects = new();
public void AddSpawn(SpawnData data)
{
switch (data.type)
{
case SpawnObjectType.Creature:
creatures.Add(data.SpawnId);
break;
case SpawnObjectType.GameObject:
gameobjects.Add(data.SpawnId);
break;
}
}
public void RemoveSpawn(SpawnData data)
{
switch (data.type)
{
case SpawnObjectType.Creature:
creatures.Remove(data.SpawnId);
break;
case SpawnObjectType.GameObject:
gameobjects.Remove(data.SpawnId);
break;
}
}
}
public class GameTele
+29 -1
View File
@@ -461,6 +461,9 @@ namespace Game.Maps
EnsureGridLoaded(cell);
Grid grid = GetGrid(cell.GetGridX(), cell.GetGridY());
if (obj.IsPlayer())
GetMultiPersonalPhaseTracker().LoadGrid(obj.GetPhaseShift(), grid, this, cell);
// refresh grid state & timer
if (grid.GetGridState() != GridState.Active)
{
@@ -526,6 +529,11 @@ namespace Game.Maps
EnsureGridLoaded(new Cell(x, y));
}
public void LoadGridForActiveObject(float x, float y, WorldObject obj)
{
EnsureGridLoadedForActiveObject(new Cell(x, y), obj);
}
public virtual bool AddPlayerToMap(Player player, bool initPlayer = true)
{
CellCoord cellCoord = GridDefines.ComputeCellCoord(player.GetPositionX(), player.GetPositionY());
@@ -563,6 +571,12 @@ namespace Game.Maps
return true;
}
public void UpdatePersonalPhasesForPlayer(Player player)
{
Cell cell = new(player.GetPositionX(), player.GetPositionY());
GetMultiPersonalPhaseTracker().OnOwnerPhaseChanged(player, GetGrid(cell.GetGridX(), cell.GetGridY()), this, cell);
}
void InitializeObject(WorldObject obj)
{
if (!obj.IsTypeId(TypeId.Unit) || !obj.IsTypeId(TypeId.GameObject))
@@ -823,6 +837,9 @@ namespace Game.Maps
_weatherUpdateTimer.Reset();
}
// update phase shift objects
GetMultiPersonalPhaseTracker().Update(this, diff);
MoveAllCreaturesInMoveList();
MoveAllGameObjectsInMoveList();
MoveAllAreaTriggersInMoveList();
@@ -930,6 +947,8 @@ namespace Game.Maps
player.UpdateZone(MapConst.InvalidZone, 0);
Global.ScriptMgr.OnPlayerLeaveMap(this, player);
GetMultiPersonalPhaseTracker().MarkAllPhasesForDeletion(player.GetGUID());
player.CombatStop();
bool inWorld = player.IsInWorld;
@@ -955,6 +974,8 @@ namespace Game.Maps
if (obj.IsActiveObject())
RemoveFromActive(obj);
GetMultiPersonalPhaseTracker().UnregisterTrackedObject(obj);
if (!inWorld) // if was in world, RemoveFromWorld() called DestroyForNearbyPlayers()
obj.DestroyForNearbyPlayers(); // previous obj.UpdateObjectVisibility(true)
@@ -1575,6 +1596,9 @@ namespace Game.Maps
RemoveAllObjectsInRemoveList();
// After removing all objects from the map, purge empty tracked phases
GetMultiPersonalPhaseTracker().UnloadGrid(grid);
{
ObjectGridUnloader worker = new();
var visitor = new Visitor(worker, GridMapTypeMask.AllGrid);
@@ -3789,7 +3813,7 @@ namespace Game.Maps
public InstanceMap ToInstanceMap() { return IsDungeon() ? (this as InstanceMap) : null; }
public BattlegroundMap ToBattlegroundMap() { return IsBattlegroundOrArena() ? (this as BattlegroundMap) : null; }
void Balance()
public void Balance()
{
_dynamicTree.Balance();
}
@@ -4118,6 +4142,8 @@ namespace Game.Maps
return map != null;
}
public MultiPersonalPhaseTracker GetMultiPersonalPhaseTracker() { return _multiPersonalPhaseTracker; }
#region Scripts
// Put scripts in the execution queue
@@ -5086,6 +5112,8 @@ namespace Game.Maps
public delegate void FarSpellCallback(Map map);
Queue<FarSpellCallback> _farSpellCallbacks = new();
MultiPersonalPhaseTracker _multiPersonalPhaseTracker;
#endregion
}
+99 -38
View File
@@ -22,16 +22,71 @@ using System.Collections.Generic;
namespace Game.Maps
{
class ObjectGridLoader : Notifier
class ObjectGridLoaderBase : Notifier
{
public ObjectGridLoader(Grid grid, Map map, Cell cell)
internal Cell i_cell;
internal Grid i_grid;
internal Map i_map;
internal uint i_gameObjects;
internal uint i_creatures;
internal uint i_corpses;
internal uint i_areaTriggers;
public ObjectGridLoaderBase(Grid grid, Map map, Cell cell)
{
i_cell = new Cell(cell);
i_grid = grid;
i_map = map;
}
public uint GetLoadedCreatures() { return i_creatures; }
public uint GetLoadedGameObjects() { return i_gameObjects; }
public uint GetLoadedCorpses() { return i_corpses; }
public uint GetLoadedAreaTriggers() { return i_areaTriggers; }
internal void LoadHelper<T>(SortedSet<ulong> guid_set, CellCoord cell, ref uint count, Map map, uint phaseId = 0, ObjectGuid? phaseOwner = null) where T : WorldObject, new()
{
foreach (var guid in guid_set)
{
// Don't spawn at all if there's a respawn timer
if (!map.ShouldBeSpawnedOnGridLoad<T>(guid))
continue;
T obj = new();
if (!obj.LoadFromDB(guid, map, false, phaseOwner.HasValue /*allowDuplicate*/))
{
obj.Dispose();
continue;
}
if (phaseOwner.HasValue)
{
PhasingHandler.InitDbPersonalOwnership(obj.GetPhaseShift(), phaseOwner.Value);
map.GetMultiPersonalPhaseTracker().RegisterTrackedObject(phaseId, phaseOwner.Value, obj);
}
AddObjectHelper(cell, ref count, map, obj);
}
}
void AddObjectHelper<T>(CellCoord cellCoord, ref uint count, Map map, T obj) where T : WorldObject
{
var cell = new Cell(cellCoord);
map.AddToGrid(obj, cell);
obj.AddToWorld();
if (obj.IsCreature())
if (obj.IsActiveObject())
map.AddToActive(obj);
++count;
}
}
class ObjectGridLoader : ObjectGridLoaderBase
{
public ObjectGridLoader(Grid grid, Map map, Cell cell) : base(grid, map, cell) { }
public void LoadN()
{
i_creatures = 0;
@@ -53,7 +108,7 @@ namespace Game.Maps
i_grid.VisitGrid(x, y, visitor);
}
}
Log.outDebug(LogFilter.Maps, "{0} GameObjects, {1} Creatures, and {2} Corpses/Bones loaded for grid {3} on map {4}", i_gameObjects, i_creatures, i_corpses, i_grid.GetGridId(), i_map.GetId());
Log.outDebug(LogFilter.Maps, $"{i_gameObjects} GameObjects, {i_creatures} Creatures, {i_areaTriggers} AreaTrriggers and {i_corpses} Corpses/Bones loaded for grid {i_grid.GetGridId()} on map {i_map.GetId()}");
}
public override void Visit(IList<GameObject> objs)
@@ -85,51 +140,57 @@ namespace Game.Maps
LoadHelper<AreaTrigger>(areaTriggers, cellCoord, ref i_areaTriggers, i_map);
}
}
void LoadHelper<T>(SortedSet<ulong> guid_set, CellCoord cell, ref uint count, Map map) where T : WorldObject, new()
class PersonalPhaseGridLoader : ObjectGridLoaderBase
{
uint _phaseId;
ObjectGuid _phaseOwner;
public PersonalPhaseGridLoader(Grid grid, Map map, Cell cell, ObjectGuid phaseOwner) : base(grid, map, cell)
{
foreach (var guid in guid_set)
_phaseId = 0;
_phaseOwner = phaseOwner;
}
public override void Visit(IList<GameObject> objs)
{
CellCoord cellCoord = i_cell.GetCellCoord();
CellObjectGuids cell_guids = Global.ObjectMgr.GetCellPersonalObjectGuids(i_map.GetId(), i_map.GetDifficultyID(), _phaseId, cellCoord.GetId());
if (cell_guids != null)
LoadHelper<GameObject>(cell_guids.gameobjects, cellCoord, ref i_gameObjects, i_map, _phaseId, _phaseOwner);
}
public override void Visit(IList<Creature> objs)
{
CellCoord cellCoord = i_cell.GetCellCoord();
CellObjectGuids cell_guids = Global.ObjectMgr.GetCellPersonalObjectGuids(i_map.GetId(), i_map.GetDifficultyID(), _phaseId, cellCoord.GetId());
if (cell_guids != null)
LoadHelper<Creature>(cell_guids.creatures, cellCoord, ref i_creatures, i_map, _phaseId, _phaseOwner);
}
public void Load(uint phaseId)
{
_phaseId = phaseId;
i_cell.data.cell_y = 0;
for (uint x = 0; x < MapConst.MaxCells; ++x)
{
// Don't spawn at all if there's a respawn timer
if (!map.ShouldBeSpawnedOnGridLoad<T>(guid))
continue;
T obj = new();
if (!obj.LoadFromDB(guid, map, false, false))
i_cell.data.cell_x = x;
for (uint y = 0; y < MapConst.MaxCells; ++y)
{
obj.Dispose();
continue;
}
i_cell.data.cell_y = y;
AddObjectHelper(cell, ref count, map, obj);
//Load creatures and game objects
var visitor = new Visitor(this, GridMapTypeMask.AllGrid);
i_grid.VisitGrid(x, y, visitor);
}
}
}
void AddObjectHelper<T>(CellCoord cellCoord, ref uint count, Map map, T obj) where T : WorldObject
{
var cell = new Cell(cellCoord);
map.AddToGrid(obj, cell);
obj.AddToWorld();
if (obj.IsCreature())
if (obj.IsActiveObject())
map.AddToActive(obj);
++count;
}
public Cell i_cell;
public Grid i_grid;
public Map i_map;
uint i_gameObjects;
uint i_creatures;
public uint i_corpses;
uint i_areaTriggers;
}
class ObjectWorldLoader : Notifier
{
public ObjectWorldLoader(ObjectGridLoader gloader)
public ObjectWorldLoader(ObjectGridLoaderBase gloader)
{
i_cell = gloader.i_cell;
i_map = gloader.i_map;
+2 -2
View File
@@ -38,14 +38,14 @@ namespace Game.Maps
public uint PhaseGroup;
public int terrainSwapMap;
public int spawntimesecs;
public List<Difficulty> spawnDifficulties;
public List<Difficulty> SpawnDifficulties;
public uint ScriptId;
public SpawnData(SpawnObjectType t) : base(t)
{
SpawnPoint = new Position();
terrainSwapMap = -1;
spawnDifficulties = new List<Difficulty>();
SpawnDifficulties = new List<Difficulty>();
}
public static SpawnObjectType TypeFor<T>()
+220
View File
@@ -0,0 +1,220 @@
/*
* Copyright (C) 2012-2020 CypherCore <http://github.com/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 <http://www.gnu.org/licenses/>.
*/
using Framework.Dynamic;
using Game.Entities;
using Game.Maps;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Game
{
class PersonalPhaseSpawns
{
public static TimeSpan DELETE_TIME_DEFAULT = TimeSpan.FromMinutes(1);
public List<WorldObject> Objects = new();
public List<ushort> Grids = new();
public Optional<TimeSpan> DurationRemaining;
public bool IsEmpty() { return Objects.Empty() && Grids.Empty(); }
}
class PlayerPersonalPhasesTracker
{
Dictionary<uint, PersonalPhaseSpawns> _spawns = new();
public void RegisterTrackedObject(uint phaseId, WorldObject obj)
{
_spawns[phaseId].Objects.Add(obj);
}
public void UnregisterTrackedObject(WorldObject obj)
{
foreach (var spawns in _spawns.Values)
spawns.Objects.Remove(obj);
}
public void OnOwnerPhasesChanged(WorldObject owner)
{
PhaseShift phaseShift = owner.GetPhaseShift();
// Loop over all our tracked phases. If any don't exist - delete them
foreach (var (phaseId, spawns) in _spawns)
if (!spawns.DurationRemaining.HasValue && !phaseShift.HasPhase(phaseId))
spawns.DurationRemaining.Set(PersonalPhaseSpawns.DELETE_TIME_DEFAULT);
// loop over all owner phases. If any exist and marked for deletion - reset delete
foreach (var phaseRef in phaseShift.GetPhases())
{
PersonalPhaseSpawns spawns = _spawns.LookupByKey(phaseRef.Key);
if (spawns != null)
spawns.DurationRemaining.Clear();
}
}
public void MarkAllPhasesForDeletion()
{
foreach (var spawns in _spawns.Values)
spawns.DurationRemaining.Set(PersonalPhaseSpawns.DELETE_TIME_DEFAULT);
}
public void Update(Map map, uint diff)
{
foreach (var itr in _spawns.ToList())
{
if (itr.Value.DurationRemaining.HasValue)
{
itr.Value.DurationRemaining.Value = itr.Value.DurationRemaining.Value - TimeSpan.FromMilliseconds(diff);
if (itr.Value.DurationRemaining.Value <= TimeSpan.Zero)
{
DespawnPhase(map, itr.Value);
_spawns.Remove(itr.Key);
}
}
}
}
public bool IsGridLoadedForPhase(uint gridId, uint phaseId)
{
PersonalPhaseSpawns spawns = _spawns.LookupByKey(phaseId);
if (spawns != null)
return spawns.Grids.Contains((ushort)gridId);
return false;
}
public void SetGridLoadedForPhase(uint gridId, uint phaseId)
{
if (!_spawns.ContainsKey(phaseId))
_spawns[phaseId] = new();
PersonalPhaseSpawns group = _spawns[phaseId];
group.Grids.Add((ushort)gridId);
}
public void SetGridUnloaded(uint gridId)
{
foreach (var itr in _spawns.ToList())
{
itr.Value.Grids.Remove((ushort)gridId);
if (itr.Value.IsEmpty())
_spawns.Remove(itr.Key);
}
}
void DespawnPhase(Map map, PersonalPhaseSpawns spawns)
{
foreach (var obj in spawns.Objects)
map.AddObjectToRemoveList(obj);
spawns.Objects.Clear();
spawns.Grids.Clear();
}
public bool IsEmpty() { return _spawns.Empty(); }
}
public class MultiPersonalPhaseTracker
{
Dictionary<ObjectGuid, PlayerPersonalPhasesTracker> _playerData = new();
public void LoadGrid(PhaseShift phaseShift, Grid grid, Map map, Cell cell)
{
if (!phaseShift.HasPersonalPhase())
return;
PersonalPhaseGridLoader loader = new(grid, map, cell, phaseShift.GetPersonalGuid());
PlayerPersonalPhasesTracker playerTracker = _playerData[phaseShift.GetPersonalGuid()];
foreach (var phaseRef in phaseShift.GetPhases())
{
if (!phaseRef.Value.IsPersonal())
continue;
if (!Global.ObjectMgr.HasPersonalSpawns(map.GetId(), map.GetDifficultyID(), phaseRef.Key))
continue;
if (playerTracker.IsGridLoadedForPhase(grid.GetGridId(), phaseRef.Key))
continue;
Log.outDebug(LogFilter.Maps, $"Loading personal phase objects (phase {phaseRef.Key}) in {cell} for map {map.GetId()} instance {map.GetInstanceId()}");
loader.Load(phaseRef.Key);
playerTracker.SetGridLoadedForPhase(grid.GetGridId(), phaseRef.Key);
}
if (loader.GetLoadedGameObjects() != 0)
map.Balance();
}
public void UnloadGrid(Grid grid)
{
foreach (var itr in _playerData.ToList())
{
itr.Value.SetGridUnloaded(grid.GetGridId());
if (itr.Value.IsEmpty())
_playerData.Remove(itr.Key);
}
}
public void RegisterTrackedObject(uint phaseId, ObjectGuid phaseOwner, WorldObject obj)
{
Cypher.Assert(phaseId != 0);
Cypher.Assert(!phaseOwner.IsEmpty());
Cypher.Assert(obj != null);
_playerData[phaseOwner].RegisterTrackedObject(phaseId, obj);
}
public void UnregisterTrackedObject(WorldObject obj)
{
PlayerPersonalPhasesTracker playerTracker = _playerData.LookupByKey(obj.GetPhaseShift().GetPersonalGuid());
if (playerTracker != null)
playerTracker.UnregisterTrackedObject(obj);
}
public void OnOwnerPhaseChanged(WorldObject phaseOwner, Grid grid, Map map, Cell cell)
{
PlayerPersonalPhasesTracker playerTracker = _playerData.LookupByKey(phaseOwner.GetGUID());
if (playerTracker != null)
playerTracker.OnOwnerPhasesChanged(phaseOwner);
if (grid != null)
LoadGrid(phaseOwner.GetPhaseShift(), grid, map, cell);
}
public void MarkAllPhasesForDeletion(ObjectGuid phaseOwner)
{
PlayerPersonalPhasesTracker playerTracker = _playerData.LookupByKey(phaseOwner);
if (playerTracker != null)
playerTracker.MarkAllPhasesForDeletion();
}
public void Update(Map map, uint diff)
{
foreach (var itr in _playerData.ToList())
{
itr.Value.Update(map, diff);
if (itr.Value.IsEmpty())
_playerData.Remove(itr.Key);
}
}
}
}
+13
View File
@@ -230,6 +230,15 @@ namespace Game
PersonalGuid.Clear();
}
public bool HasPersonalPhase()
{
foreach (PhaseRef phaseRef in GetPhases().Values)
if (phaseRef.IsPersonal())
return true;
return false;
}
public bool HasPhase(uint phaseId) { return Phases.ContainsKey(phaseId); }
public Dictionary<uint, PhaseRef> GetPhases() { return Phases; }
@@ -239,6 +248,8 @@ namespace Game
public bool HasUiWorldMapAreaIdSwap(uint uiWorldMapAreaId) { return UiMapPhaseIds.ContainsKey(uiWorldMapAreaId); }
public Dictionary<uint, UiMapPhaseIdRef> GetUiMapPhaseIds() { return UiMapPhaseIds; }
public ObjectGuid GetPersonalGuid() { return PersonalGuid; }
public PhaseShiftFlags Flags = PhaseShiftFlags.Unphased;
public ObjectGuid PersonalGuid;
public Dictionary<uint, PhaseRef> Phases = new();
@@ -261,6 +272,8 @@ namespace Game
AreaConditions = conditions;
}
public bool IsPersonal() { return Flags.HasFlag(PhaseFlags.Personal); }
public PhaseFlags Flags;
public int References;
public List<Condition> AreaConditions;
+29 -2
View File
@@ -487,6 +487,13 @@ namespace Game
phaseShift.Flags = flags;
}
public static void InitDbPersonalOwnership(PhaseShift phaseShift, ObjectGuid personalGuid)
{
Cypher.Assert(phaseShift.IsDbPhaseShift);
Cypher.Assert(phaseShift.HasPersonalPhase());
phaseShift.PersonalGuid = personalGuid;
}
public static void InitDbVisibleMapId(PhaseShift phaseShift, int visibleMapId)
{
phaseShift.VisibleMapIds.Clear();
@@ -542,9 +549,20 @@ namespace Game
UpdateVisibilityIfNeeded(obj, updateVisibility, true);
}
public static void PrintToChat(CommandHandler chat, PhaseShift phaseShift)
public static void PrintToChat(CommandHandler chat, WorldObject target)
{
chat.SendSysMessage(CypherStrings.PhaseshiftStatus, phaseShift.Flags, phaseShift.PersonalGuid.ToString());
PhaseShift phaseShift = target.GetPhaseShift();
string phaseOwnerName = "N/A";
if (phaseShift.HasPersonalPhase())
{
WorldObject personalGuid = Global.ObjAccessor.GetWorldObject(target, phaseShift.PersonalGuid);
if (personalGuid != null)
phaseOwnerName = personalGuid.GetName();
}
chat.SendSysMessage(CypherStrings.PhaseshiftStatus, phaseShift.Flags, phaseShift.PersonalGuid.ToString(), phaseOwnerName);
if (!phaseShift.Phases.Empty())
{
StringBuilder phases = new();
@@ -592,6 +610,15 @@ namespace Game
return phases.ToString();
}
public static bool IsPersonalPhase(uint phaseId)
{
var phase = CliDB.PhaseStorage.LookupByKey(phaseId);
if (phase != null)
return phase.Flags.HasFlag(PhaseEntryFlags.Personal);
return false;
}
static void UpdateVisibilityIfNeeded(WorldObject obj, bool updateVisibility, bool changed)
{
if (changed && obj.IsInWorld)
+4 -4
View File
@@ -482,7 +482,7 @@ namespace Game
var data = Global.ObjectMgr.GetCreatureData(guid);
if (data != null)
{
Global.ObjectMgr.RemoveCreatureFromGrid(guid, data);
Global.ObjectMgr.RemoveCreatureFromGrid(data);
Map map = Global.MapMgr.FindMap(data.MapId, 0);
if (map != null && !map.Instanceable())
{
@@ -504,7 +504,7 @@ namespace Game
var data = Global.ObjectMgr.GetGameObjectData(guid);
if (data != null)
{
Global.ObjectMgr.RemoveGameObjectFromGrid(guid, data);
Global.ObjectMgr.RemoveGameObjectFromGrid(data);
Map map = Global.MapMgr.FindMap(data.MapId, 0);
if (map != null && !map.Instanceable())
{
@@ -619,7 +619,7 @@ namespace Game
CreatureData data = Global.ObjectMgr.GetCreatureData(obj.guid);
if (data != null)
{
Global.ObjectMgr.AddCreatureToGrid(obj.guid, data);
Global.ObjectMgr.AddCreatureToGrid(data);
// Spawn if necessary (loaded grids only)
Map map = Global.MapMgr.FindMap(data.MapId, 0);
@@ -634,7 +634,7 @@ namespace Game
GameObjectData data = Global.ObjectMgr.GetGameObjectData(obj.guid);
if (data != null)
{
Global.ObjectMgr.AddGameObjectToGrid(obj.guid, data);
Global.ObjectMgr.AddGameObjectToGrid(data);
// Spawn if necessary (loaded grids only)
// this base map checked as non-instanced and then only existed
Map map = Global.MapMgr.FindMap(data.MapId, 0);