Core/Entities: Phasing rewrite
This commit is contained in:
@@ -730,14 +730,7 @@ namespace Game.Chat
|
||||
else if (target.GetDBPhase() < 0)
|
||||
handler.SendSysMessage($"Target creature's PhaseGroup in DB: {Math.Abs(target.GetDBPhase())}");
|
||||
|
||||
string phases = "";
|
||||
foreach (uint phase in target.GetPhases())
|
||||
phases += phase + " ";
|
||||
|
||||
if (!string.IsNullOrEmpty(phases))
|
||||
handler.SendSysMessage("Target's current phases: {0}", phases);
|
||||
else
|
||||
handler.SendSysMessage("Target is not phased");
|
||||
PhasingHandler.PrintToChat(handler, target.GetPhaseShift());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1171,17 +1164,18 @@ namespace Game.Chat
|
||||
if (args.Empty())
|
||||
return false;
|
||||
|
||||
List<uint> terrainswap = new List<uint>();
|
||||
List<uint> phaseId = new List<uint>();
|
||||
List<uint> worldMapSwap = new List<uint>();
|
||||
PhaseShift phaseShift = new PhaseShift();
|
||||
|
||||
if (uint.TryParse(args.NextString(), out uint terrain))
|
||||
terrainswap.Add(terrain);
|
||||
phaseShift.AddVisibleMapId(terrain, null);
|
||||
|
||||
if (uint.TryParse(args.NextString(), out uint phase))
|
||||
phaseId.Add(phase);
|
||||
phaseShift.AddPhase(phase, PhaseFlags.None, null);
|
||||
|
||||
handler.GetSession().SendSetPhaseShift(phaseId, terrainswap, worldMapSwap);
|
||||
if (uint.TryParse(args.NextString(), out uint map))
|
||||
phaseShift.AddUiWorldMapAreaIdSwap(map);
|
||||
|
||||
PhasingHandler.SendToPlayer(handler.GetSession().GetPlayer(), phaseShift);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -477,7 +477,7 @@ namespace Game.Chat
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
obj.CopyPhaseFrom(player);
|
||||
PhasingHandler.InheritPhaseShift(obj, player);
|
||||
|
||||
if (spawntimeSecs != 0)
|
||||
{
|
||||
@@ -616,6 +616,13 @@ namespace Game.Chat
|
||||
obj.SetByteValue(GameObjectFields.Bytes1, (byte)objectType, (byte)objectState);
|
||||
else if (objectType == 4)
|
||||
obj.SendCustomAnim(objectState);
|
||||
else if (objectType == 5)
|
||||
{
|
||||
if (objectState < 0 || objectState > (uint)GameObjectDestructibleState.Rebuilding)
|
||||
return false;
|
||||
|
||||
obj.SetDestructibleState((GameObjectDestructibleState)objectState);
|
||||
}
|
||||
|
||||
handler.SendSysMessage("Set gobject type {0} state {1}", objectType, objectState);
|
||||
return true;
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace Game.Chat.Commands
|
||||
player.SaveRecallPosition();
|
||||
|
||||
Map map = Global.MapMgr.CreateBaseMap(mapId);
|
||||
float z = Math.Max(map.GetHeight(x, y, MapConst.MaxHeight), map.GetWaterLevel(x, y));
|
||||
float z = Math.Max(map.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), map.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y));
|
||||
|
||||
player.TeleportTo(mapId, x, y, z, player.GetOrientation());
|
||||
return true;
|
||||
@@ -298,7 +298,7 @@ namespace Game.Chat.Commands
|
||||
player.SaveRecallPosition();
|
||||
|
||||
Map map = Global.MapMgr.CreateBaseMap(mapId);
|
||||
z = Math.Max(map.GetHeight(x, y, MapConst.MaxHeight), map.GetWaterLevel(x, y));
|
||||
z = Math.Max(map.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), map.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y));
|
||||
|
||||
player.TeleportTo(mapId, x, y, z, 0.0f);
|
||||
return true;
|
||||
@@ -446,7 +446,7 @@ namespace Game.Chat.Commands
|
||||
else
|
||||
player.SaveRecallPosition();
|
||||
|
||||
float z = Math.Max(map.GetHeight(x, y, MapConst.MaxHeight), map.GetWaterLevel(x, y));
|
||||
float z = Math.Max(map.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), map.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y));
|
||||
|
||||
player.TeleportTo(zoneEntry.ContinentID, x, y, z, player.GetOrientation());
|
||||
return true;
|
||||
@@ -495,7 +495,7 @@ namespace Game.Chat.Commands
|
||||
return false;
|
||||
}
|
||||
Map map = Global.MapMgr.CreateBaseMap(mapId);
|
||||
z = Math.Max(map.GetHeight(x, y, MapConst.MaxHeight), map.GetWaterLevel(x, y));
|
||||
z = Math.Max(map.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), map.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y));
|
||||
}
|
||||
|
||||
// stop flight if need
|
||||
|
||||
@@ -321,7 +321,7 @@ namespace Game.Chat
|
||||
{
|
||||
// ... than, it prints information like "is online", where he is, etc...
|
||||
onlineState = "online";
|
||||
phases = string.Join(", ", p.GetPhases());
|
||||
phases = PhasingHandler.FormatPhases(p.GetPhaseShift());
|
||||
|
||||
AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(p.GetAreaId());
|
||||
if (area != null)
|
||||
|
||||
@@ -99,12 +99,16 @@ namespace Game.Chat
|
||||
int gx = (int)(32 - player.GetPositionX() / MapConst.SizeofGrids);
|
||||
int gy = (int)(32 - player.GetPositionY() / MapConst.SizeofGrids);
|
||||
|
||||
float x, y, z;
|
||||
player.GetPosition(out x, out y, out z);
|
||||
|
||||
handler.SendSysMessage("{0:D4}{1:D2}{2:D2}.mmtile", player.GetMapId(), gy, gx);
|
||||
handler.SendSysMessage("gridloc [{0}, {1}]", gx, gy);
|
||||
|
||||
// calculate navmesh tile location
|
||||
Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(handler.GetPlayer().GetMapId());
|
||||
Detour.dtNavMeshQuery navmeshquery = Global.MMapMgr.GetNavMeshQuery(handler.GetPlayer().GetMapId(), player.GetInstanceId());
|
||||
uint terrainMapId = PhasingHandler.GetTerrainMapId(player.GetPhaseShift(), player.GetMap(), x, y);
|
||||
Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(terrainMapId);
|
||||
Detour.dtNavMeshQuery navmeshquery = Global.MMapMgr.GetNavMeshQuery(terrainMapId, player.GetInstanceId());
|
||||
if (navmesh == null || navmeshquery == null)
|
||||
{
|
||||
handler.SendSysMessage("NavMesh not loaded for current map.");
|
||||
@@ -112,8 +116,6 @@ namespace Game.Chat
|
||||
}
|
||||
|
||||
float[] min = navmesh.getParams().orig;
|
||||
float x, y, z;
|
||||
player.GetPosition(out x, out y, out z);
|
||||
float[] location = { y, z, x };
|
||||
float[] extents = { 3.0f, 5.0f, 3.0f };
|
||||
|
||||
@@ -155,9 +157,10 @@ namespace Game.Chat
|
||||
[Command("loadedtiles", RBACPermissions.CommandMmapLoadedtiles)]
|
||||
static bool LoadedTilesCommand(StringArguments args, CommandHandler handler)
|
||||
{
|
||||
uint mapid = handler.GetPlayer().GetMapId();
|
||||
Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(mapid);
|
||||
Detour.dtNavMeshQuery navmeshquery = Global.MMapMgr.GetNavMeshQuery(mapid, handler.GetPlayer().GetInstanceId());
|
||||
Player player = handler.GetSession().GetPlayer();
|
||||
uint terrainMapId = PhasingHandler.GetTerrainMapId(player.GetPhaseShift(), player.GetMap(), player.GetPositionX(), player.GetPositionY());
|
||||
Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(terrainMapId);
|
||||
Detour.dtNavMeshQuery navmeshquery = Global.MMapMgr.GetNavMeshQuery(terrainMapId, handler.GetPlayer().GetInstanceId());
|
||||
if (navmesh == null || navmeshquery == null)
|
||||
{
|
||||
handler.SendSysMessage("NavMesh not loaded for current map.");
|
||||
@@ -180,12 +183,13 @@ namespace Game.Chat
|
||||
[Command("stats", RBACPermissions.CommandMmapStats)]
|
||||
static bool HandleMmapStatsCommand(StringArguments args, CommandHandler handler)
|
||||
{
|
||||
uint mapId = handler.GetPlayer().GetMapId();
|
||||
Player player = handler.GetSession().GetPlayer();
|
||||
uint terrainMapId = PhasingHandler.GetTerrainMapId(player.GetPhaseShift(), player.GetMap(), player.GetPositionX(), player.GetPositionY());
|
||||
handler.SendSysMessage("mmap stats:");
|
||||
handler.SendSysMessage(" global mmap pathfinding is {0}abled", Global.DisableMgr.IsPathfindingEnabled(mapId) ? "En" : "Dis");
|
||||
handler.SendSysMessage(" global mmap pathfinding is {0}abled", Global.DisableMgr.IsPathfindingEnabled(player.GetMapId()) ? "En" : "Dis");
|
||||
handler.SendSysMessage(" {0} maps loaded with {1} tiles overall", Global.MMapMgr.getLoadedMapsCount(), Global.MMapMgr.getLoadedTilesCount());
|
||||
|
||||
Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(handler.GetPlayer().GetMapId());
|
||||
Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(terrainMapId);
|
||||
if (navmesh == null)
|
||||
{
|
||||
handler.SendSysMessage("NavMesh not loaded for current map.");
|
||||
|
||||
@@ -195,8 +195,8 @@ namespace Game.Chat
|
||||
Global.DB2Mgr.Map2ZoneCoordinates(zoneId, ref zoneX, ref zoneY);
|
||||
|
||||
Map map = obj.GetMap();
|
||||
float groundZ = map.GetHeight(obj.GetPhases(), obj.GetPositionX(), obj.GetPositionY(), MapConst.MaxHeight);
|
||||
float floorZ = map.GetHeight(obj.GetPhases(), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ());
|
||||
float groundZ = map.GetHeight(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), MapConst.MaxHeight);
|
||||
float floorZ = map.GetHeight(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ());
|
||||
|
||||
GridCoord gridCoord = GridDefines.ComputeGridCoord(obj.GetPositionX(), obj.GetPositionY());
|
||||
|
||||
@@ -210,13 +210,13 @@ namespace Game.Chat
|
||||
|
||||
if (haveVMap)
|
||||
{
|
||||
if (map.IsOutdoors(obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ()))
|
||||
handler.SendSysMessage("You are outdoors");
|
||||
if (map.IsOutdoors(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ()))
|
||||
handler.SendSysMessage(CypherStrings.GpsPositionOutdoors);
|
||||
else
|
||||
handler.SendSysMessage("You are indoors");
|
||||
handler.SendSysMessage(CypherStrings.GpsPositionIndoors);
|
||||
}
|
||||
else
|
||||
handler.SendSysMessage("no VMAP available for area info");
|
||||
handler.SendSysMessage(CypherStrings.GpsNoVmap);
|
||||
|
||||
string unknown = handler.GetCypherString(CypherStrings.Unknown);
|
||||
|
||||
@@ -224,7 +224,7 @@ namespace Game.Chat
|
||||
mapId, (mapEntry != null ? mapEntry.MapName[handler.GetSessionDbcLocale()] : unknown),
|
||||
zoneId, (zoneEntry != null ? zoneEntry.AreaName[handler.GetSessionDbcLocale()] : unknown),
|
||||
areaId, (areaEntry != null ? areaEntry.AreaName[handler.GetSessionDbcLocale()] : unknown),
|
||||
string.Join(", ", obj.GetPhases()), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ(), obj.GetOrientation());
|
||||
obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ(), obj.GetOrientation());
|
||||
|
||||
Transport transport = obj.GetTransport();
|
||||
if (transport)
|
||||
@@ -237,25 +237,12 @@ namespace Game.Chat
|
||||
zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap, haveMMap);
|
||||
|
||||
LiquidData liquidStatus;
|
||||
ZLiquidStatus status = map.getLiquidStatus(obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ(), MapConst.MapAllLiquidTypes, out liquidStatus);
|
||||
ZLiquidStatus status = map.getLiquidStatus(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ(), MapConst.MapAllLiquidTypes, out liquidStatus);
|
||||
|
||||
if (liquidStatus != null)
|
||||
handler.SendSysMessage(CypherStrings.LiquidStatus, liquidStatus.level, liquidStatus.depth_level, liquidStatus.entry, liquidStatus.type_flags, status);
|
||||
|
||||
if (!obj.GetTerrainSwaps().Empty())
|
||||
{
|
||||
string ss = "";
|
||||
foreach (uint swap in obj.GetTerrainSwaps())
|
||||
ss += swap + " ";
|
||||
handler.SendSysMessage("Target's active terrain swaps: {0}", ss);
|
||||
}
|
||||
if (!obj.GetWorldMapAreaSwaps().Empty())
|
||||
{
|
||||
string ss = "";
|
||||
foreach (uint swap in obj.GetWorldMapAreaSwaps())
|
||||
ss += swap + " ";
|
||||
handler.SendSysMessage("Target's active world map area swaps: {0}", ss);
|
||||
}
|
||||
PhasingHandler.PrintToChat(handler, obj.GetPhaseShift());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -602,7 +589,8 @@ namespace Game.Chat
|
||||
target.GetContactPoint(_player, out x, out y, out z);
|
||||
|
||||
_player.TeleportTo(target.GetMapId(), x, y, z, _player.GetAngle(target), TeleportToOptions.GMMode);
|
||||
_player.CopyPhaseFrom(target, true);
|
||||
PhasingHandler.InheritPhaseShift(_player, target);
|
||||
_player.UpdateObjectVisibility();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -727,7 +715,8 @@ namespace Game.Chat
|
||||
float x, y, z;
|
||||
handler.GetSession().GetPlayer().GetClosePoint(out x, out y, out z, target.GetObjectSize());
|
||||
target.TeleportTo(handler.GetSession().GetPlayer().GetMapId(), x, y, z, target.GetOrientation());
|
||||
target.CopyPhaseFrom(handler.GetSession().GetPlayer(), true);
|
||||
PhasingHandler.InheritPhaseShift(target, handler.GetSession().GetPlayer());
|
||||
target.UpdateObjectVisibility();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1385,7 +1374,6 @@ namespace Game.Chat
|
||||
// Position data print
|
||||
uint mapId;
|
||||
uint areaId;
|
||||
List<uint> phases = new List<uint>();
|
||||
string areaName = handler.GetCypherString(CypherStrings.Unknown);
|
||||
string zoneName = handler.GetCypherString(CypherStrings.Unknown);
|
||||
|
||||
@@ -1417,7 +1405,6 @@ namespace Game.Chat
|
||||
areaId = target.GetAreaId();
|
||||
alive = target.IsAlive() ? handler.GetCypherString(CypherStrings.Yes) : handler.GetCypherString(CypherStrings.No);
|
||||
gender = (Gender)target.GetByteValue(PlayerFields.Bytes3, PlayerFieldOffsets.Bytes3OffsetGender);
|
||||
phases = target.GetPhases();
|
||||
}
|
||||
// get additional information from DB
|
||||
else
|
||||
@@ -1582,9 +1569,9 @@ namespace Game.Chat
|
||||
// Output XII. LANG_PINFO_CHR_ALIVE
|
||||
handler.SendSysMessage(CypherStrings.PinfoChrAlive, alive);
|
||||
|
||||
// Output XIII. LANG_PINFO_CHR_PHASES
|
||||
if (target && !phases.Empty())
|
||||
handler.SendSysMessage(CypherStrings.PinfoChrPhases, string.Join(", ", phases));
|
||||
// Output XIII. phases
|
||||
if (target)
|
||||
PhasingHandler.PrintToChat(handler, target.GetPhaseShift());
|
||||
|
||||
// Output XIV. LANG_PINFO_CHR_MONEY
|
||||
ulong gold = money / MoneyConstants.Gold;
|
||||
|
||||
@@ -497,20 +497,38 @@ namespace Game.Chat
|
||||
return false;
|
||||
|
||||
uint phaseId = args.NextUInt32();
|
||||
if (!CliDB.PhaseStorage.ContainsKey(phaseId))
|
||||
uint visibleMapId = args.NextUInt32();
|
||||
|
||||
if (phaseId != 0 && !CliDB.PhaseStorage.ContainsKey(phaseId))
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.PhaseNotfound);
|
||||
return false;
|
||||
}
|
||||
|
||||
Unit target = handler.getSelectedUnit();
|
||||
if (!target)
|
||||
target = handler.GetSession().GetPlayer();
|
||||
|
||||
target.SetInPhase(phaseId, true, !target.IsInPhase(phaseId));
|
||||
if (visibleMapId != 0)
|
||||
{
|
||||
MapRecord visibleMap = CliDB.MapStorage.LookupByKey(visibleMapId);
|
||||
if (visibleMap == null || visibleMap.ParentMapID != target.GetMapId())
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.PhaseNotfound);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target.IsTypeId(TypeId.Player))
|
||||
target.ToPlayer().SendUpdatePhasing();
|
||||
if (!target.GetPhaseShift().HasVisibleMapId(visibleMapId))
|
||||
PhasingHandler.AddVisibleMapId(target, visibleMapId);
|
||||
else
|
||||
PhasingHandler.RemoveVisibleMapId(target, visibleMapId);
|
||||
}
|
||||
|
||||
if (phaseId != 0)
|
||||
{
|
||||
if (!target.GetPhaseShift().HasPhase(phaseId))
|
||||
PhasingHandler.AddPhase(target, phaseId, true);
|
||||
else
|
||||
PhasingHandler.RemovePhase(target, phaseId, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -87,16 +87,7 @@ namespace Game.Chat
|
||||
if (data != null)
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.NpcinfoPhases, data.phaseId, data.phaseGroup);
|
||||
if (data.phaseGroup != 0)
|
||||
{
|
||||
var _phases = target.GetPhases();
|
||||
if (!_phases.Empty())
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.NpcinfoPhaseIds);
|
||||
foreach (uint phaseId in _phases)
|
||||
handler.SendSysMessage("{0}", phaseId);
|
||||
}
|
||||
}
|
||||
PhasingHandler.PrintToChat(handler, target.GetPhaseShift());
|
||||
}
|
||||
|
||||
handler.SendSysMessage(CypherStrings.NpcinfoArmor, target.GetArmor());
|
||||
@@ -939,8 +930,8 @@ namespace Game.Chat
|
||||
return false;
|
||||
}
|
||||
|
||||
creature.ClearPhases();
|
||||
creature.SetInPhase(phaseId, true, true);
|
||||
PhasingHandler.ResetPhaseShift(creature);
|
||||
PhasingHandler.AddPhase(creature, phaseId, true);
|
||||
creature.SetDBPhase((int)phaseId);
|
||||
|
||||
creature.SaveToDB();
|
||||
@@ -962,12 +953,8 @@ namespace Game.Chat
|
||||
return false;
|
||||
}
|
||||
|
||||
creature.ClearPhases();
|
||||
|
||||
foreach (uint id in Global.DB2Mgr.GetPhasesForGroup((uint)phaseGroupId))
|
||||
creature.SetInPhase(id, false, true); // don't send update here for multiple phases, only send it once after adding all phases
|
||||
|
||||
creature.UpdateObjectVisibility();
|
||||
PhasingHandler.ResetPhaseShift(creature);
|
||||
PhasingHandler.AddPhaseGroup(creature, (uint)phaseGroupId, true);
|
||||
creature.SetDBPhase(-phaseGroupId);
|
||||
|
||||
creature.SaveToDB();
|
||||
@@ -1119,7 +1106,7 @@ namespace Game.Chat
|
||||
if (!creature)
|
||||
return false;
|
||||
|
||||
creature.CopyPhaseFrom(chr);
|
||||
PhasingHandler.InheritPhaseShift(creature, chr);
|
||||
creature.SaveToDB(map.GetId(), 1ul << (int)map.GetSpawnMode());
|
||||
|
||||
ulong db_guid = creature.GetSpawnId();
|
||||
|
||||
@@ -552,7 +552,7 @@ namespace Game.Chat.Commands
|
||||
return false;
|
||||
}
|
||||
|
||||
creature.CopyPhaseFrom(chr);
|
||||
PhasingHandler.InheritPhaseShift(creature, chr);
|
||||
creature.SaveToDB(map.GetId(), 1ul << (int)map.GetSpawnMode());
|
||||
|
||||
ulong dbGuid = creature.GetSpawnId();
|
||||
@@ -772,7 +772,7 @@ namespace Game.Chat.Commands
|
||||
return false;
|
||||
}
|
||||
|
||||
creature.CopyPhaseFrom(chr);
|
||||
PhasingHandler.InheritPhaseShift(creature, chr);
|
||||
creature.SaveToDB(map.GetId(), 1ul << (int)map.GetSpawnMode());
|
||||
|
||||
ulong dbGuid = creature.GetSpawnId();
|
||||
@@ -838,7 +838,7 @@ namespace Game.Chat.Commands
|
||||
return false;
|
||||
}
|
||||
|
||||
creature.CopyPhaseFrom(chr);
|
||||
PhasingHandler.InheritPhaseShift(creature, chr);
|
||||
creature.SaveToDB(map.GetId(), 1ul << (int)map.GetSpawnMode());
|
||||
|
||||
ulong dbGuid = creature.GetSpawnId();
|
||||
@@ -893,7 +893,7 @@ namespace Game.Chat.Commands
|
||||
return false;
|
||||
}
|
||||
|
||||
creature.CopyPhaseFrom(chr);
|
||||
PhasingHandler.InheritPhaseShift(creature, chr);
|
||||
creature.SaveToDB(map.GetId(), 1ul << (int)map.GetSpawnMode());
|
||||
|
||||
ulong dbGuid = creature.GetSpawnId();
|
||||
|
||||
Reference in New Issue
Block a user