Replace NavMap dictionaries with int[] (#27602)
* Replace NavMap dictionaries with int[] * Remove badly named const * Remove unnecessary offset * Prioritize airlocks
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Pinpointer;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
@@ -25,12 +24,6 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
|
||||
if (!state.AllChunks!.Contains(index))
|
||||
component.Chunks.Remove(index);
|
||||
}
|
||||
|
||||
foreach (var beacon in component.Beacons)
|
||||
{
|
||||
if (!state.AllBeacons!.Contains(beacon))
|
||||
component.Beacons.Remove(beacon);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -39,34 +32,19 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
|
||||
if (!state.Chunks.ContainsKey(index))
|
||||
component.Chunks.Remove(index);
|
||||
}
|
||||
|
||||
foreach (var beacon in component.Beacons)
|
||||
{
|
||||
if (!state.Beacons.Contains(beacon))
|
||||
component.Beacons.Remove(beacon);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (origin, chunk) in state.Chunks)
|
||||
{
|
||||
var newChunk = new NavMapChunk(origin);
|
||||
|
||||
for (var i = 0; i < NavMapComponent.Categories; i++)
|
||||
{
|
||||
var newData = chunk[i];
|
||||
|
||||
if (newData == null)
|
||||
continue;
|
||||
|
||||
newChunk.TileData[i] = new(newData);
|
||||
}
|
||||
|
||||
Array.Copy(chunk, newChunk.TileData, chunk.Length);
|
||||
component.Chunks[origin] = newChunk;
|
||||
}
|
||||
|
||||
foreach (var beacon in state.Beacons)
|
||||
component.Beacons.Clear();
|
||||
foreach (var (nuid, beacon) in state.Beacons)
|
||||
{
|
||||
component.Beacons.Add(beacon);
|
||||
component.Beacons[nuid] = beacon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Content.Shared.Atmos;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Pinpointer.UI;
|
||||
|
||||
@@ -71,10 +72,10 @@ public partial class NavMapControl : MapGridControl
|
||||
protected float BackgroundOpacity = 0.9f;
|
||||
private int _targetFontsize = 8;
|
||||
|
||||
protected Dictionary<(int, Vector2i), (int, Vector2i)> HorizLinesLookup = new();
|
||||
protected Dictionary<(int, Vector2i), (int, Vector2i)> HorizLinesLookupReversed = new();
|
||||
protected Dictionary<(int, Vector2i), (int, Vector2i)> VertLinesLookup = new();
|
||||
protected Dictionary<(int, Vector2i), (int, Vector2i)> VertLinesLookupReversed = new();
|
||||
private Dictionary<Vector2i, Vector2i> _horizLines = new();
|
||||
private Dictionary<Vector2i, Vector2i> _horizLinesReversed = new();
|
||||
private Dictionary<Vector2i, Vector2i> _vertLines = new();
|
||||
private Dictionary<Vector2i, Vector2i> _vertLinesReversed = new();
|
||||
|
||||
// Components
|
||||
private NavMapComponent? _navMap;
|
||||
@@ -376,7 +377,7 @@ public partial class NavMapControl : MapGridControl
|
||||
var fontSize = (int) Math.Round(1 / WorldRange * DefaultDisplayedRange * UIScale * _targetFontsize, 0);
|
||||
var font = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Bold.ttf"), fontSize);
|
||||
|
||||
foreach (var beacon in _navMap.Beacons)
|
||||
foreach (var beacon in _navMap.Beacons.Values)
|
||||
{
|
||||
var position = beacon.Position - offset;
|
||||
position = ScalePosition(position with { Y = -position.Y });
|
||||
@@ -485,147 +486,105 @@ public partial class NavMapControl : MapGridControl
|
||||
return;
|
||||
|
||||
// We'll use the following dictionaries to combine collinear wall lines
|
||||
HorizLinesLookup.Clear();
|
||||
HorizLinesLookupReversed.Clear();
|
||||
VertLinesLookup.Clear();
|
||||
VertLinesLookupReversed.Clear();
|
||||
_horizLines.Clear();
|
||||
_horizLinesReversed.Clear();
|
||||
_vertLines.Clear();
|
||||
_vertLinesReversed.Clear();
|
||||
|
||||
const int southMask = (int) AtmosDirection.South << (int) NavMapChunkType.Wall;
|
||||
const int eastMask = (int) AtmosDirection.East << (int) NavMapChunkType.Wall;
|
||||
const int westMask = (int) AtmosDirection.West << (int) NavMapChunkType.Wall;
|
||||
const int northMask = (int) AtmosDirection.North << (int) NavMapChunkType.Wall;
|
||||
|
||||
foreach (var (chunkOrigin, chunk) in _navMap.Chunks)
|
||||
{
|
||||
for (var j = 0; j < NavMapComponent.Categories; j++)
|
||||
for (var i = 0; i < SharedNavMapSystem.ArraySize; i++)
|
||||
{
|
||||
var category = (NavMapChunkType) j;
|
||||
|
||||
if (category != NavMapChunkType.Wall)
|
||||
var tileData = chunk.TileData[i] & SharedNavMapSystem.WallMask;
|
||||
if (tileData == 0)
|
||||
continue;
|
||||
|
||||
var data = chunk.TileData[j];
|
||||
tileData >>= (int) NavMapChunkType.Wall;
|
||||
|
||||
if (data == null)
|
||||
continue;
|
||||
var relativeTile = SharedNavMapSystem.GetTileFromIndex(i);
|
||||
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * _grid.TileSize;
|
||||
|
||||
for (var i = 0; i < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; i++)
|
||||
if (tileData != SharedNavMapSystem.AllDirMask)
|
||||
{
|
||||
var value = (ushort) Math.Pow(2, i);
|
||||
var mask = _navMapSystem.GetCombinedEdgesForChunk(data) & value;
|
||||
|
||||
if (mask == 0x0)
|
||||
continue;
|
||||
|
||||
var relativeTile = SharedNavMapSystem.GetTile(mask);
|
||||
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * _grid.TileSize;
|
||||
|
||||
if (!_navMapSystem.AllTileEdgesAreOccupied(data, relativeTile))
|
||||
{
|
||||
AddRectForThinWall(data, tile);
|
||||
continue;
|
||||
}
|
||||
|
||||
tile = tile with { Y = -tile.Y };
|
||||
|
||||
NavMapChunk? neighborChunk;
|
||||
bool neighbor;
|
||||
|
||||
// North edge
|
||||
if (relativeTile.Y == SharedNavMapSystem.ChunkSize - 1)
|
||||
{
|
||||
_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Up, out neighborChunk);
|
||||
var neighborData = neighborChunk?.TileData[(int) NavMapChunkType.Wall];
|
||||
|
||||
neighbor = neighborData != null &&
|
||||
(neighborData[AtmosDirection.South] & SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + Vector2i.Up);
|
||||
neighbor = (data[AtmosDirection.South] & flag) != 0x0;
|
||||
}
|
||||
|
||||
if (!neighbor)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize),
|
||||
tile + new Vector2i(_grid.TileSize, -_grid.TileSize), HorizLinesLookup,
|
||||
HorizLinesLookupReversed);
|
||||
}
|
||||
|
||||
// East edge
|
||||
if (relativeTile.X == SharedNavMapSystem.ChunkSize - 1)
|
||||
{
|
||||
_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Right, out neighborChunk);
|
||||
var neighborData = neighborChunk?.TileData[(int) NavMapChunkType.Wall];
|
||||
|
||||
neighbor = neighborData != null &&
|
||||
(neighborData[AtmosDirection.West] & SharedNavMapSystem.GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + Vector2i.Right);
|
||||
neighbor = (data[AtmosDirection.West] & flag) != 0x0;
|
||||
}
|
||||
|
||||
if (!neighbor)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(_grid.TileSize, -_grid.TileSize),
|
||||
tile + new Vector2i(_grid.TileSize, 0), VertLinesLookup, VertLinesLookupReversed);
|
||||
}
|
||||
|
||||
// South edge
|
||||
if (relativeTile.Y == 0)
|
||||
{
|
||||
_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Down, out neighborChunk);
|
||||
var neighborData = neighborChunk?.TileData[(int) NavMapChunkType.Wall];
|
||||
|
||||
neighbor = neighborData != null &&
|
||||
(neighborData[AtmosDirection.North] & SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, SharedNavMapSystem.ChunkSize - 1))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + Vector2i.Down);
|
||||
neighbor = (data[AtmosDirection.North] & flag) != 0x0;
|
||||
}
|
||||
|
||||
if (!neighbor)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), HorizLinesLookup,
|
||||
HorizLinesLookupReversed);
|
||||
}
|
||||
|
||||
// West edge
|
||||
if (relativeTile.X == 0)
|
||||
{
|
||||
_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Left, out neighborChunk);
|
||||
var neighborData = neighborChunk?.TileData[(int) NavMapChunkType.Wall];
|
||||
|
||||
neighbor = neighborData != null &&
|
||||
(neighborData[AtmosDirection.East] & SharedNavMapSystem.GetFlag(new Vector2i(SharedNavMapSystem.ChunkSize - 1, relativeTile.Y))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + Vector2i.Left);
|
||||
neighbor = (data[AtmosDirection.East] & flag) != 0x0;
|
||||
}
|
||||
|
||||
if (!neighbor)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, VertLinesLookup,
|
||||
VertLinesLookupReversed);
|
||||
}
|
||||
|
||||
// Add a diagonal line for interiors. Unless there are a lot of double walls, there is no point combining these
|
||||
TileLines.Add((tile + new Vector2(0, -_grid.TileSize), tile + new Vector2(_grid.TileSize, 0)));
|
||||
AddRectForThinWall(tileData, tile);
|
||||
continue;
|
||||
}
|
||||
|
||||
tile = tile with { Y = -tile.Y };
|
||||
NavMapChunk? neighborChunk;
|
||||
|
||||
// North edge
|
||||
var neighborData = 0;
|
||||
if (relativeTile.Y != SharedNavMapSystem.ChunkSize - 1)
|
||||
neighborData = chunk.TileData[i+1];
|
||||
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Up, out neighborChunk))
|
||||
neighborData = neighborChunk.TileData[i + 1 - SharedNavMapSystem.ChunkSize];
|
||||
|
||||
if ((neighborData & southMask) == 0)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize),
|
||||
tile + new Vector2i(_grid.TileSize, -_grid.TileSize), _horizLines,
|
||||
_horizLinesReversed);
|
||||
}
|
||||
|
||||
// East edge
|
||||
neighborData = 0;
|
||||
if (relativeTile.X != SharedNavMapSystem.ChunkSize - 1)
|
||||
neighborData = chunk.TileData[i+SharedNavMapSystem.ChunkSize];
|
||||
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Right, out neighborChunk))
|
||||
neighborData = neighborChunk.TileData[i + SharedNavMapSystem.ChunkSize - SharedNavMapSystem.ArraySize];
|
||||
|
||||
if ((neighborData & westMask) == 0)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(_grid.TileSize, -_grid.TileSize),
|
||||
tile + new Vector2i(_grid.TileSize, 0), _vertLines, _vertLinesReversed);
|
||||
}
|
||||
|
||||
// South edge
|
||||
neighborData = 0;
|
||||
if (relativeTile.Y != 0)
|
||||
neighborData = chunk.TileData[i-1];
|
||||
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Down, out neighborChunk))
|
||||
neighborData = neighborChunk.TileData[i - 1 + SharedNavMapSystem.ChunkSize];
|
||||
|
||||
if ((neighborData & northMask) == 0)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), _horizLines,
|
||||
_horizLinesReversed);
|
||||
}
|
||||
|
||||
// West edge
|
||||
neighborData = 0;
|
||||
if (relativeTile.X != 0)
|
||||
neighborData = chunk.TileData[i-SharedNavMapSystem.ChunkSize];
|
||||
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Left, out neighborChunk))
|
||||
neighborData = neighborChunk.TileData[i - SharedNavMapSystem.ChunkSize + SharedNavMapSystem.ArraySize];
|
||||
|
||||
if ((neighborData & eastMask) == 0)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, _vertLines,
|
||||
_vertLinesReversed);
|
||||
}
|
||||
|
||||
// Add a diagonal line for interiors. Unless there are a lot of double walls, there is no point combining these
|
||||
TileLines.Add((tile + new Vector2(0, -_grid.TileSize), tile + new Vector2(_grid.TileSize, 0)));
|
||||
}
|
||||
}
|
||||
|
||||
// Record the combined lines
|
||||
foreach (var (origin, terminal) in HorizLinesLookup)
|
||||
foreach (var (origin, terminal) in _horizLines)
|
||||
{
|
||||
TileLines.Add((origin.Item2, terminal.Item2));
|
||||
TileLines.Add((origin, terminal));
|
||||
}
|
||||
|
||||
foreach (var (origin, terminal) in VertLinesLookup)
|
||||
foreach (var (origin, terminal) in _vertLines)
|
||||
{
|
||||
TileLines.Add((origin.Item2, terminal.Item2));
|
||||
TileLines.Add((origin, terminal));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -634,28 +593,23 @@ public partial class NavMapControl : MapGridControl
|
||||
if (_navMap == null || _grid == null)
|
||||
return;
|
||||
|
||||
foreach (var (chunkOrigin, chunk) in _navMap.Chunks)
|
||||
foreach (var chunk in _navMap.Chunks.Values)
|
||||
{
|
||||
var data = chunk.TileData[(int) NavMapChunkType.Airlock];
|
||||
|
||||
if (data == null)
|
||||
continue;
|
||||
|
||||
for (var i = 0; i < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; i++)
|
||||
for (var i = 0; i < SharedNavMapSystem.ArraySize; i++)
|
||||
{
|
||||
var value = (int) Math.Pow(2, i);
|
||||
var mask = _navMapSystem.GetCombinedEdgesForChunk(data) & value;
|
||||
|
||||
if (mask == 0x0)
|
||||
var tileData = chunk.TileData[i] & SharedNavMapSystem.AirlockMask;
|
||||
if (tileData == 0)
|
||||
continue;
|
||||
|
||||
var relative = SharedNavMapSystem.GetTile(mask);
|
||||
tileData >>= (int) NavMapChunkType.Airlock;
|
||||
|
||||
var relative = SharedNavMapSystem.GetTileFromIndex(i);
|
||||
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relative) * _grid.TileSize;
|
||||
|
||||
// If the edges of an airlock tile are not all occupied, draw a thin airlock for each edge
|
||||
if (!_navMapSystem.AllTileEdgesAreOccupied(data, relative))
|
||||
if (tileData != SharedNavMapSystem.AllDirMask)
|
||||
{
|
||||
AddRectForThinAirlock(data, tile);
|
||||
AddRectForThinAirlock(tileData, tile);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -669,108 +623,90 @@ public partial class NavMapControl : MapGridControl
|
||||
}
|
||||
}
|
||||
|
||||
private void AddRectForThinWall(Dictionary<AtmosDirection, ushort> tileData, Vector2i tile)
|
||||
private void AddRectForThinWall(int tileData, Vector2i tile)
|
||||
{
|
||||
if (_navMapSystem == null || _grid == null)
|
||||
return;
|
||||
var leftTop = new Vector2(-0.5f, 0.5f - ThinWallThickness);
|
||||
var rightBottom = new Vector2(0.5f, 0.5f);
|
||||
|
||||
var leftTop = new Vector2(-0.5f, -0.5f + ThinWallThickness);
|
||||
var rightBottom = new Vector2(0.5f, -0.5f);
|
||||
|
||||
foreach (var (direction, mask) in tileData)
|
||||
for (var i = 0; i < SharedNavMapSystem.Directions; i++)
|
||||
{
|
||||
var relative = SharedMapSystem.GetChunkRelative(tile, SharedNavMapSystem.ChunkSize);
|
||||
var flag = (ushort) SharedNavMapSystem.GetFlag(relative);
|
||||
|
||||
if ((mask & flag) == 0)
|
||||
var dirMask = 1 << i;
|
||||
if ((tileData & dirMask) == 0)
|
||||
continue;
|
||||
|
||||
var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f);
|
||||
var angle = new Angle(0);
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case AtmosDirection.East: angle = new Angle(MathF.PI * 0.5f); break;
|
||||
case AtmosDirection.South: angle = new Angle(MathF.PI); break;
|
||||
case AtmosDirection.West: angle = new Angle(MathF.PI * -0.5f); break;
|
||||
}
|
||||
|
||||
// TODO NAVMAP
|
||||
// Consider using faster rotation operations, given that these are always 90 degree increments
|
||||
var angle = -((AtmosDirection) dirMask).ToAngle();
|
||||
TileRects.Add((angle.RotateVec(leftTop) + tilePosition, angle.RotateVec(rightBottom) + tilePosition));
|
||||
}
|
||||
}
|
||||
|
||||
private void AddRectForThinAirlock(Dictionary<AtmosDirection, ushort> tileData, Vector2i tile)
|
||||
private void AddRectForThinAirlock(int tileData, Vector2i tile)
|
||||
{
|
||||
if (_navMapSystem == null || _grid == null)
|
||||
return;
|
||||
var leftTop = new Vector2(-0.5f + FullWallInstep, 0.5f - FullWallInstep - ThinDoorThickness);
|
||||
var rightBottom = new Vector2(0.5f - FullWallInstep, 0.5f - FullWallInstep);
|
||||
var centreTop = new Vector2(0f, 0.5f - FullWallInstep - ThinDoorThickness);
|
||||
var centreBottom = new Vector2(0f, 0.5f - FullWallInstep);
|
||||
|
||||
var leftTop = new Vector2(-0.5f + FullWallInstep, -0.5f + FullWallInstep + ThinDoorThickness);
|
||||
var rightBottom = new Vector2(0.5f - FullWallInstep, -0.5f + FullWallInstep);
|
||||
var centreTop = new Vector2(0f, -0.5f + FullWallInstep + ThinDoorThickness);
|
||||
var centreBottom = new Vector2(0f, -0.5f + FullWallInstep);
|
||||
|
||||
foreach (var (direction, mask) in tileData)
|
||||
for (var i = 0; i < SharedNavMapSystem.Directions; i++)
|
||||
{
|
||||
var relative = SharedMapSystem.GetChunkRelative(tile, SharedNavMapSystem.ChunkSize);
|
||||
var flag = (ushort) SharedNavMapSystem.GetFlag(relative);
|
||||
|
||||
if ((mask & flag) == 0)
|
||||
var dirMask = 1 << i;
|
||||
if ((tileData & dirMask) == 0)
|
||||
continue;
|
||||
|
||||
var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f);
|
||||
var angle = new Angle(0);
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case AtmosDirection.East: angle = new Angle(MathF.PI * 0.5f);break;
|
||||
case AtmosDirection.South: angle = new Angle(MathF.PI); break;
|
||||
case AtmosDirection.West: angle = new Angle(MathF.PI * -0.5f); break;
|
||||
}
|
||||
|
||||
var angle = -((AtmosDirection) dirMask).ToAngle();
|
||||
TileRects.Add((angle.RotateVec(leftTop) + tilePosition, angle.RotateVec(rightBottom) + tilePosition));
|
||||
TileLines.Add((angle.RotateVec(centreTop) + tilePosition, angle.RotateVec(centreBottom) + tilePosition));
|
||||
}
|
||||
}
|
||||
|
||||
protected void AddOrUpdateNavMapLine
|
||||
(Vector2i origin,
|
||||
protected void AddOrUpdateNavMapLine(
|
||||
Vector2i origin,
|
||||
Vector2i terminus,
|
||||
Dictionary<(int, Vector2i), (int, Vector2i)> lookup,
|
||||
Dictionary<(int, Vector2i), (int, Vector2i)> lookupReversed,
|
||||
int index = 0)
|
||||
Dictionary<Vector2i, Vector2i> lookup,
|
||||
Dictionary<Vector2i, Vector2i> lookupReversed)
|
||||
{
|
||||
(int, Vector2i) foundTermiusTuple;
|
||||
(int, Vector2i) foundOriginTuple;
|
||||
Vector2i foundTermius;
|
||||
Vector2i foundOrigin;
|
||||
|
||||
if (lookup.TryGetValue((index, terminus), out foundTermiusTuple) &&
|
||||
lookupReversed.TryGetValue((index, origin), out foundOriginTuple))
|
||||
// Does our new line end at the beginning of an existing line?
|
||||
if (lookup.Remove(terminus, out foundTermius))
|
||||
{
|
||||
lookup[foundOriginTuple] = foundTermiusTuple;
|
||||
lookupReversed[foundTermiusTuple] = foundOriginTuple;
|
||||
DebugTools.Assert(lookupReversed[foundTermius] == terminus);
|
||||
|
||||
lookup.Remove((index, terminus));
|
||||
lookupReversed.Remove((index, origin));
|
||||
// Does our new line start at the end of an existing line?
|
||||
if (lookupReversed.Remove(origin, out foundOrigin))
|
||||
{
|
||||
// Our new line just connects two existing lines
|
||||
DebugTools.Assert(lookup[foundOrigin] == origin);
|
||||
lookup[foundOrigin] = foundTermius;
|
||||
lookupReversed[foundTermius] = foundOrigin;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Our new line precedes an existing line, extending it further to the left
|
||||
lookup[origin] = foundTermius;
|
||||
lookupReversed[foundTermius] = origin;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
else if (lookup.TryGetValue((index, terminus), out foundTermiusTuple))
|
||||
// Does our new line start at the end of an existing line?
|
||||
if (lookupReversed.Remove(origin, out foundOrigin))
|
||||
{
|
||||
lookup[(index, origin)] = foundTermiusTuple;
|
||||
lookup.Remove((index, terminus));
|
||||
lookupReversed[foundTermiusTuple] = (index, origin);
|
||||
// Our new line just extends an existing line further to the right
|
||||
DebugTools.Assert(lookup[foundOrigin] == origin);
|
||||
lookup[foundOrigin] = terminus;
|
||||
lookupReversed[terminus] = foundOrigin;
|
||||
return;
|
||||
}
|
||||
|
||||
else if (lookupReversed.TryGetValue((index, origin), out foundOriginTuple))
|
||||
{
|
||||
lookupReversed[(index, terminus)] = foundOriginTuple;
|
||||
lookupReversed.Remove(foundOriginTuple);
|
||||
lookup[foundOriginTuple] = (index, terminus);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
lookup.Add((index, origin), (index, terminus));
|
||||
lookupReversed.Add((index, terminus), (index, origin));
|
||||
}
|
||||
// Completely disconnected line segment.
|
||||
lookup.Add(origin, terminus);
|
||||
lookupReversed.Add(terminus, origin);
|
||||
}
|
||||
|
||||
protected Vector2 GetOffset()
|
||||
|
||||
@@ -5,6 +5,7 @@ using Robust.Client.Graphics;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Map.Components;
|
||||
using System.Numerics;
|
||||
using static Content.Shared.Power.SharedPowerMonitoringConsoleSystem;
|
||||
|
||||
namespace Content.Client.Power;
|
||||
|
||||
@@ -26,6 +27,11 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
||||
public List<PowerMonitoringConsoleLine> PowerCableNetwork = new();
|
||||
public List<PowerMonitoringConsoleLine> FocusCableNetwork = new();
|
||||
|
||||
private Dictionary<Vector2i, Vector2i>[] _horizLines = [new(), new(), new()];
|
||||
private Dictionary<Vector2i, Vector2i>[] _horizLinesReversed = [new(), new(), new()];
|
||||
private Dictionary<Vector2i, Vector2i>[] _vertLines = [new(), new(), new()];
|
||||
private Dictionary<Vector2i, Vector2i>[] _vertLinesReversed = [new(), new(), new()];
|
||||
|
||||
private MapGridComponent? _grid;
|
||||
|
||||
public PowerMonitoringConsoleNavMapControl() : base()
|
||||
@@ -182,28 +188,32 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
||||
if (chunks == null)
|
||||
return decodedOutput;
|
||||
|
||||
// We'll use the following dictionaries to combine collinear power cable lines
|
||||
HorizLinesLookup.Clear();
|
||||
HorizLinesLookupReversed.Clear();
|
||||
VertLinesLookup.Clear();
|
||||
VertLinesLookupReversed.Clear();
|
||||
Array.ForEach(_horizLines, x=> x.Clear());
|
||||
Array.ForEach(_horizLinesReversed, x=> x.Clear());
|
||||
Array.ForEach(_vertLines, x=> x.Clear());
|
||||
Array.ForEach(_vertLinesReversed, x=> x.Clear());
|
||||
|
||||
foreach ((var chunkOrigin, var chunk) in chunks)
|
||||
foreach (var (chunkOrigin, chunk) in chunks)
|
||||
{
|
||||
for (int cableIdx = 0; cableIdx < chunk.PowerCableData.Length; cableIdx++)
|
||||
for (var cableIdx = 0; cableIdx < 3; cableIdx++)
|
||||
{
|
||||
var horizLines = _horizLines[cableIdx];
|
||||
var horizLinesReversed = _horizLinesReversed[cableIdx];
|
||||
var vertLines = _vertLines[cableIdx];
|
||||
var vertLinesReversed = _vertLinesReversed[cableIdx];
|
||||
|
||||
var chunkMask = chunk.PowerCableData[cableIdx];
|
||||
|
||||
for (var chunkIdx = 0; chunkIdx < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; chunkIdx++)
|
||||
for (var chunkIdx = 0; chunkIdx < ChunkSize * ChunkSize; chunkIdx++)
|
||||
{
|
||||
var value = (int) Math.Pow(2, chunkIdx);
|
||||
var value = 1 << chunkIdx;
|
||||
var mask = chunkMask & value;
|
||||
|
||||
if (mask == 0x0)
|
||||
continue;
|
||||
|
||||
var relativeTile = SharedNavMapSystem.GetTile(mask);
|
||||
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * _grid.TileSize;
|
||||
var relativeTile = GetTileFromIndex(chunkIdx);
|
||||
var tile = (chunk.Origin * ChunkSize + relativeTile) * _grid.TileSize;
|
||||
tile = tile with { Y = -tile.Y };
|
||||
|
||||
PowerCableChunk neighborChunk;
|
||||
@@ -212,39 +222,39 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
||||
// Note: we only check the north and east neighbors
|
||||
|
||||
// East
|
||||
if (relativeTile.X == SharedNavMapSystem.ChunkSize - 1)
|
||||
if (relativeTile.X == ChunkSize - 1)
|
||||
{
|
||||
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(1, 0), out neighborChunk) &&
|
||||
(neighborChunk.PowerCableData[cableIdx] & SharedNavMapSystem.GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
|
||||
(neighborChunk.PowerCableData[cableIdx] & GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(1, 0));
|
||||
var flag = GetFlag(relativeTile + new Vector2i(1, 0));
|
||||
neighbor = (chunkMask & flag) != 0x0;
|
||||
}
|
||||
|
||||
if (neighbor)
|
||||
{
|
||||
// Add points
|
||||
AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), HorizLinesLookup, HorizLinesLookupReversed, cableIdx);
|
||||
AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), horizLines, horizLinesReversed);
|
||||
}
|
||||
|
||||
// North
|
||||
if (relativeTile.Y == SharedNavMapSystem.ChunkSize - 1)
|
||||
if (relativeTile.Y == ChunkSize - 1)
|
||||
{
|
||||
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(0, 1), out neighborChunk) &&
|
||||
(neighborChunk.PowerCableData[cableIdx] & SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
|
||||
(neighborChunk.PowerCableData[cableIdx] & GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, 1));
|
||||
var flag = GetFlag(relativeTile + new Vector2i(0, 1));
|
||||
neighbor = (chunkMask & flag) != 0x0;
|
||||
}
|
||||
|
||||
if (neighbor)
|
||||
{
|
||||
// Add points
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, VertLinesLookup, VertLinesLookupReversed, cableIdx);
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, vertLines, vertLinesReversed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,11 +263,25 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
||||
|
||||
var gridOffset = new Vector2(_grid.TileSize * 0.5f, -_grid.TileSize * 0.5f);
|
||||
|
||||
foreach (var (origin, terminal) in HorizLinesLookup)
|
||||
decodedOutput.Add(new PowerMonitoringConsoleLine(origin.Item2 + gridOffset, terminal.Item2 + gridOffset, (PowerMonitoringConsoleLineGroup) origin.Item1));
|
||||
for (var index = 0; index < _horizLines.Length; index++)
|
||||
{
|
||||
var horizLines = _horizLines[index];
|
||||
foreach (var (origin, terminal) in horizLines)
|
||||
{
|
||||
decodedOutput.Add(new PowerMonitoringConsoleLine(origin + gridOffset, terminal + gridOffset,
|
||||
(PowerMonitoringConsoleLineGroup) index));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (origin, terminal) in VertLinesLookup)
|
||||
decodedOutput.Add(new PowerMonitoringConsoleLine(origin.Item2 + gridOffset, terminal.Item2 + gridOffset, (PowerMonitoringConsoleLineGroup) origin.Item1));
|
||||
for (var index = 0; index < _vertLines.Length; index++)
|
||||
{
|
||||
var vertLines = _vertLines[index];
|
||||
foreach (var (origin, terminal) in vertLines)
|
||||
{
|
||||
decodedOutput.Add(new PowerMonitoringConsoleLine(origin + gridOffset, terminal + gridOffset,
|
||||
(PowerMonitoringConsoleLineGroup) index));
|
||||
}
|
||||
}
|
||||
|
||||
return decodedOutput;
|
||||
}
|
||||
|
||||
@@ -12,11 +12,7 @@ using JetBrains.Annotations;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Doors.Components;
|
||||
|
||||
namespace Content.Server.Pinpointer;
|
||||
|
||||
@@ -44,6 +40,10 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
var categories = Enum.GetNames(typeof(NavMapChunkType)).Length - 1; // -1 due to "Invalid" entry.
|
||||
if (Categories != categories)
|
||||
throw new Exception($"{nameof(Categories)} must be equal to the number of chunk types");
|
||||
|
||||
_airtightQuery = GetEntityQuery<AirtightComponent>();
|
||||
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||
_navQuery = GetEntityQuery<NavMapComponent>();
|
||||
@@ -65,15 +65,12 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
|
||||
SubscribeLocalEvent<ConfigurableNavMapBeaconComponent, ExaminedEvent>(OnConfigurableExamined);
|
||||
}
|
||||
|
||||
#region: Initialization event handling
|
||||
private void OnStationInit(StationGridAddedEvent ev)
|
||||
{
|
||||
var comp = EnsureComp<NavMapComponent>(ev.GridId);
|
||||
RefreshGrid(ev.GridId, comp, Comp<MapGridComponent>(ev.GridId));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region: Grid change event handling
|
||||
|
||||
private void OnNavMapSplit(ref GridSplitEvent args)
|
||||
@@ -112,16 +109,30 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
|
||||
var chunk = EnsureChunk(navMap, chunkOrigin);
|
||||
|
||||
// This could be easily replaced in the future to accommodate diagonal tiles
|
||||
if (ev.NewTile.IsSpace(_tileDefManager))
|
||||
UnsetAllEdgesForChunkTile(chunk, tile, NavMapChunkType.Floor);
|
||||
else
|
||||
SetAllEdgesForChunkTile(chunk, tile, NavMapChunkType.Floor);
|
||||
var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
|
||||
ref var tileData = ref chunk.TileData[GetTileIndex(relative)];
|
||||
|
||||
if (!PruneEmpty((ev.NewTile.GridUid, navMap), chunk))
|
||||
if (ev.NewTile.IsSpace(_tileDefManager))
|
||||
{
|
||||
chunk.LastUpdate = _gameTiming.CurTick;
|
||||
Dirty(ev.NewTile.GridUid, navMap);
|
||||
tileData = 0;
|
||||
if (PruneEmpty((ev.NewTile.GridUid, navMap), chunk))
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
tileData = FloorMask;
|
||||
}
|
||||
|
||||
DirtyChunk((ev.NewTile.GridUid, navMap), chunk);
|
||||
}
|
||||
|
||||
private void DirtyChunk(Entity<NavMapComponent> entity, NavMapChunk chunk)
|
||||
{
|
||||
if (chunk.LastUpdate == _gameTiming.CurTick)
|
||||
return;
|
||||
|
||||
chunk.LastUpdate = _gameTiming.CurTick;
|
||||
Dirty(entity);
|
||||
}
|
||||
|
||||
private void OnAirtightChange(ref AirtightChanged args)
|
||||
@@ -137,15 +148,13 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh the affected tile
|
||||
var chunkOrigin = SharedMapSystem.GetChunkIndices(args.Position.Tile, ChunkSize);
|
||||
var (newValue, chunk) = RefreshTileEntityContents(gridUid, navMap, mapGrid, chunkOrigin, args.Position.Tile, setFloor: false);
|
||||
|
||||
var chunk = RefreshTileEntityContents(gridUid, navMap, mapGrid, chunkOrigin, args.Position.Tile);
|
||||
if (!PruneEmpty((gridUid, navMap), chunk))
|
||||
{
|
||||
chunk.LastUpdate = _gameTiming.CurTick;
|
||||
Dirty(gridUid, navMap);
|
||||
}
|
||||
if (newValue == 0 && PruneEmpty((gridUid, navMap), chunk))
|
||||
return;
|
||||
|
||||
DirtyChunk((gridUid, navMap), chunk);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -238,87 +247,63 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
|
||||
|
||||
var chunk = EnsureChunk(component, chunkOrigin);
|
||||
chunk.LastUpdate = _gameTiming.CurTick;
|
||||
|
||||
// Refresh the floor tile
|
||||
SetAllEdgesForChunkTile(chunk, tile, NavMapChunkType.Floor);
|
||||
|
||||
// Refresh the contents of the tile
|
||||
RefreshTileEntityContents(uid, component, mapGrid, chunkOrigin, tile);
|
||||
RefreshTileEntityContents(uid, component, mapGrid, chunkOrigin, tile, setFloor: true);
|
||||
}
|
||||
|
||||
Dirty(uid, component);
|
||||
}
|
||||
|
||||
private NavMapChunk RefreshTileEntityContents(EntityUid uid, NavMapComponent component, MapGridComponent mapGrid, Vector2i chunkOrigin, Vector2i tile)
|
||||
private (int NewVal, NavMapChunk Chunk) RefreshTileEntityContents(EntityUid uid,
|
||||
NavMapComponent component,
|
||||
MapGridComponent mapGrid,
|
||||
Vector2i chunkOrigin,
|
||||
Vector2i tile,
|
||||
bool setFloor)
|
||||
{
|
||||
var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
|
||||
var flag = (ushort) GetFlag(relative);
|
||||
var invFlag = (ushort) ~flag;
|
||||
var chunk = EnsureChunk(component, chunkOrigin);
|
||||
ref var tileData = ref chunk.TileData[GetTileIndex(relative)];
|
||||
|
||||
// Clear stale data from the tile across all entity associated chunks
|
||||
foreach (var category in EntityChunkTypes)
|
||||
{
|
||||
var data = chunk.EnsureType(category);
|
||||
// Clear all data except for floor bits
|
||||
if (setFloor)
|
||||
tileData = FloorMask;
|
||||
else
|
||||
tileData &= FloorMask;
|
||||
|
||||
foreach (var direction in data.Keys)
|
||||
{
|
||||
data[direction] &= invFlag;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the tile data based on what entities are still anchored to the tile
|
||||
var enumerator = _mapSystem.GetAnchoredEntitiesEnumerator(uid, mapGrid, tile);
|
||||
|
||||
while (enumerator.MoveNext(out var ent))
|
||||
{
|
||||
if (!_airtightQuery.TryComp(ent, out var airtight))
|
||||
continue;
|
||||
|
||||
var category = GetAssociatedEntityChunkType(ent.Value);
|
||||
var data = chunk.EnsureType(category);
|
||||
var category = GetEntityType(ent.Value);
|
||||
if (category == NavMapChunkType.Invalid)
|
||||
continue;
|
||||
|
||||
foreach (var direction in data.Keys)
|
||||
{
|
||||
if ((direction & airtight.AirBlockedDirection) > 0)
|
||||
{
|
||||
data[direction] |= flag;
|
||||
}
|
||||
}
|
||||
var directions = (int)airtight.AirBlockedDirection;
|
||||
tileData |= directions << (int) category;
|
||||
}
|
||||
|
||||
// Remove walls that intersect with doors (unless they can both physically fit on the same tile)
|
||||
var wallData = chunk.TileData[(int) NavMapChunkType.Wall];
|
||||
var airlockData = chunk.TileData[(int) NavMapChunkType.Airlock];
|
||||
// TODO NAVMAP why can this even happen?
|
||||
// Is this for blast-doors or something?
|
||||
|
||||
if (wallData != null && airlockData != null)
|
||||
{
|
||||
foreach (var direction in wallData.Keys)
|
||||
{
|
||||
var airlockInvFlag = (ushort) ~airlockData[direction];
|
||||
wallData[direction] &= airlockInvFlag;
|
||||
}
|
||||
}
|
||||
// Shift airlock bits over to the wall bits
|
||||
var shiftedAirlockBits = (tileData & AirlockMask) >> ((int) NavMapChunkType.Airlock - (int) NavMapChunkType.Wall);
|
||||
|
||||
return chunk;
|
||||
// And then mask door bits
|
||||
tileData &= ~shiftedAirlockBits;
|
||||
|
||||
return (tileData, chunk);
|
||||
}
|
||||
|
||||
private bool PruneEmpty(Entity<NavMapComponent> entity, NavMapChunk chunk)
|
||||
{
|
||||
for (var i = 0; i < NavMapComponent.Categories; i++)
|
||||
foreach (var val in chunk.TileData)
|
||||
{
|
||||
var data = chunk.TileData[i];
|
||||
|
||||
if (data == null)
|
||||
continue;
|
||||
|
||||
foreach (var value in data.Values)
|
||||
{
|
||||
if (value != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// TODO NAVMAP SIMD
|
||||
if (val != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
entity.Comp.Chunks.Remove(chunk.Origin);
|
||||
@@ -341,19 +326,12 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
|
||||
if (!_navQuery.TryComp(xform.GridUid, out var navMap))
|
||||
return;
|
||||
|
||||
var netEnt = GetNetEntity(uid);
|
||||
var oldBeacon = navMap.Beacons.FirstOrNull(x => x.NetEnt == netEnt);
|
||||
var changed = false;
|
||||
var meta = MetaData(uid);
|
||||
var changed = navMap.Beacons.Remove(meta.NetEntity);
|
||||
|
||||
if (oldBeacon != null)
|
||||
if (TryCreateNavMapBeaconData(uid, component, xform, meta, out var beaconData))
|
||||
{
|
||||
navMap.Beacons.Remove(oldBeacon.Value);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (TryCreateNavMapBeaconData(uid, component, xform, out var beaconData))
|
||||
{
|
||||
navMap.Beacons.Add(beaconData.Value);
|
||||
navMap.Beacons.Add(meta.NetEntity, beaconData.Value);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.NodeContainer;
|
||||
using Content.Server.NodeContainer.EntitySystems;
|
||||
using Content.Server.NodeContainer.NodeGroups;
|
||||
using Content.Server.NodeContainer.Nodes;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.Nodes;
|
||||
@@ -13,10 +11,8 @@ using Content.Shared.Power;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Server.GameTicking.Components;
|
||||
|
||||
namespace Content.Server.Power.EntitySystems;
|
||||
@@ -163,7 +159,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
|
||||
allChunks = new();
|
||||
|
||||
var tile = _sharedMapSystem.LocalToTile(xform.GridUid.Value, grid, xform.Coordinates);
|
||||
var chunkOrigin = SharedMapSystem.GetChunkIndices(tile, SharedNavMapSystem.ChunkSize);
|
||||
var chunkOrigin = SharedMapSystem.GetChunkIndices(tile, ChunkSize);
|
||||
|
||||
if (!allChunks.TryGetValue(chunkOrigin, out var chunk))
|
||||
{
|
||||
@@ -171,8 +167,8 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
|
||||
allChunks[chunkOrigin] = chunk;
|
||||
}
|
||||
|
||||
var relative = SharedMapSystem.GetChunkRelative(tile, SharedNavMapSystem.ChunkSize);
|
||||
var flag = SharedNavMapSystem.GetFlag(relative);
|
||||
var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
|
||||
var flag = GetFlag(relative);
|
||||
|
||||
if (args.Anchored)
|
||||
chunk.PowerCableData[(int) component.CableType] |= flag;
|
||||
@@ -884,7 +880,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
|
||||
continue;
|
||||
|
||||
var tile = _sharedMapSystem.GetTileRef(gridUid, grid, entXform.Coordinates);
|
||||
var chunkOrigin = SharedMapSystem.GetChunkIndices(tile.GridIndices, SharedNavMapSystem.ChunkSize);
|
||||
var chunkOrigin = SharedMapSystem.GetChunkIndices(tile.GridIndices, ChunkSize);
|
||||
|
||||
if (!allChunks.TryGetValue(chunkOrigin, out var chunk))
|
||||
{
|
||||
@@ -892,8 +888,8 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
|
||||
allChunks[chunkOrigin] = chunk;
|
||||
}
|
||||
|
||||
var relative = SharedMapSystem.GetChunkRelative(tile.GridIndices, SharedNavMapSystem.ChunkSize);
|
||||
var flag = SharedNavMapSystem.GetFlag(relative);
|
||||
var relative = SharedMapSystem.GetChunkRelative(tile.GridIndices, ChunkSize);
|
||||
var flag = GetFlag(relative);
|
||||
|
||||
chunk.PowerCableData[(int) cable.CableType] |= flag;
|
||||
}
|
||||
@@ -910,7 +906,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
|
||||
var xform = Transform(ent);
|
||||
var tile = _sharedMapSystem.GetTileRef(gridUid, grid, xform.Coordinates);
|
||||
var gridIndices = tile.GridIndices;
|
||||
var chunkOrigin = SharedMapSystem.GetChunkIndices(gridIndices, SharedNavMapSystem.ChunkSize);
|
||||
var chunkOrigin = SharedMapSystem.GetChunkIndices(gridIndices, ChunkSize);
|
||||
|
||||
if (!component.FocusChunks.TryGetValue(chunkOrigin, out var chunk))
|
||||
{
|
||||
@@ -918,8 +914,8 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
|
||||
component.FocusChunks[chunkOrigin] = chunk;
|
||||
}
|
||||
|
||||
var relative = SharedMapSystem.GetChunkRelative(gridIndices, SharedNavMapSystem.ChunkSize);
|
||||
var flag = SharedNavMapSystem.GetFlag(relative);
|
||||
var relative = SharedMapSystem.GetChunkRelative(gridIndices, ChunkSize);
|
||||
var flag = GetFlag(relative);
|
||||
|
||||
if (TryComp<CableComponent>(ent, out var cable))
|
||||
chunk.PowerCableData[(int) cable.CableType] |= flag;
|
||||
|
||||
@@ -104,15 +104,14 @@ namespace Content.Shared.Atmos
|
||||
{
|
||||
return direction switch
|
||||
{
|
||||
AtmosDirection.East => Angle.FromDegrees(90),
|
||||
AtmosDirection.North => Angle.FromDegrees(180),
|
||||
AtmosDirection.West => Angle.FromDegrees(270),
|
||||
AtmosDirection.South => Angle.FromDegrees(0),
|
||||
|
||||
AtmosDirection.NorthEast => Angle.FromDegrees(135),
|
||||
AtmosDirection.NorthWest => Angle.FromDegrees(205),
|
||||
AtmosDirection.SouthWest => Angle.FromDegrees(315),
|
||||
AtmosDirection.SouthEast => Angle.FromDegrees(45),
|
||||
AtmosDirection.South => Angle.Zero,
|
||||
AtmosDirection.East => new Angle(MathHelper.PiOver2),
|
||||
AtmosDirection.North => new Angle(Math.PI),
|
||||
AtmosDirection.West => new Angle(-MathHelper.PiOver2),
|
||||
AtmosDirection.NorthEast => new Angle(Math.PI*3/4),
|
||||
AtmosDirection.NorthWest => new Angle(-Math.PI*3/4),
|
||||
AtmosDirection.SouthWest => new Angle(-MathHelper.PiOver4),
|
||||
AtmosDirection.SouthEast => new Angle(MathHelper.PiOver4),
|
||||
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(direction), $"It was {direction}."),
|
||||
};
|
||||
|
||||
@@ -12,8 +12,6 @@ namespace Content.Shared.Pinpointer;
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class NavMapComponent : Component
|
||||
{
|
||||
public const int Categories = 4;
|
||||
|
||||
/*
|
||||
* Don't need DataFields as this can be reconstructed
|
||||
*/
|
||||
@@ -28,62 +26,37 @@ public sealed partial class NavMapComponent : Component
|
||||
/// List of station beacons.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public HashSet<SharedNavMapSystem.NavMapBeacon> Beacons = new();
|
||||
public Dictionary<NetEntity, SharedNavMapSystem.NavMapBeacon> Beacons = new();
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class NavMapChunk
|
||||
public sealed class NavMapChunk(Vector2i origin)
|
||||
{
|
||||
/// <summary>
|
||||
/// The chunk origin
|
||||
/// </summary>
|
||||
public readonly Vector2i Origin;
|
||||
[ViewVariables]
|
||||
public readonly Vector2i Origin = origin;
|
||||
|
||||
/// <summary>
|
||||
/// Array with each entry corresponding to a <see cref="NavMapChunkType"/>.
|
||||
/// Uses a bitmask for tiles, 1 for occupied and 0 for empty. There is a bitmask for each cardinal direction,
|
||||
/// representing each edge of the tile, in case the entities inside it do not entirely fill it
|
||||
/// Array containing the chunk's data. The
|
||||
/// </summary>
|
||||
public Dictionary<AtmosDirection, ushort>?[] TileData;
|
||||
[ViewVariables]
|
||||
public int[] TileData = new int[SharedNavMapSystem.ArraySize];
|
||||
|
||||
/// <summary>
|
||||
/// The last game tick that the chunk was updated
|
||||
/// </summary>
|
||||
[NonSerialized]
|
||||
public GameTick LastUpdate;
|
||||
|
||||
public NavMapChunk(Vector2i origin)
|
||||
{
|
||||
Origin = origin;
|
||||
TileData = new Dictionary<AtmosDirection, ushort>?[NavMapComponent.Categories];
|
||||
}
|
||||
|
||||
public Dictionary<AtmosDirection, ushort> EnsureType(NavMapChunkType chunkType)
|
||||
{
|
||||
var data = TileData[(int) chunkType];
|
||||
|
||||
if (data == null)
|
||||
{
|
||||
data = new Dictionary<AtmosDirection, ushort>()
|
||||
{
|
||||
[AtmosDirection.North] = 0,
|
||||
[AtmosDirection.East] = 0,
|
||||
[AtmosDirection.South] = 0,
|
||||
[AtmosDirection.West] = 0,
|
||||
};
|
||||
|
||||
TileData[(int) chunkType] = data;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
public enum NavMapChunkType : byte
|
||||
{
|
||||
Invalid,
|
||||
Floor,
|
||||
Wall,
|
||||
Airlock,
|
||||
// Update the categories const if you update this.
|
||||
// Values represent bit shift offsets when retrieving data in the tile array.
|
||||
Invalid = byte.MaxValue,
|
||||
Floor = 0, // I believe floors have directional information for diagonal tiles?
|
||||
Wall = SharedNavMapSystem.Directions,
|
||||
Airlock = 2 * SharedNavMapSystem.Directions,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Atmos;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -11,19 +11,22 @@ namespace Content.Shared.Pinpointer;
|
||||
|
||||
public abstract class SharedNavMapSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
public const int Categories = 3;
|
||||
public const int Directions = 4; // Not directly tied to number of atmos directions
|
||||
|
||||
public const byte ChunkSize = 4;
|
||||
public const int ChunkSize = 8;
|
||||
public const int ArraySize = ChunkSize* ChunkSize;
|
||||
|
||||
public readonly NavMapChunkType[] EntityChunkTypes =
|
||||
{
|
||||
NavMapChunkType.Invalid,
|
||||
NavMapChunkType.Wall,
|
||||
NavMapChunkType.Airlock,
|
||||
};
|
||||
public const int AllDirMask = (1 << Directions) - 1;
|
||||
public const int AirlockMask = AllDirMask << (int) NavMapChunkType.Airlock;
|
||||
public const int WallMask = AllDirMask << (int) NavMapChunkType.Wall;
|
||||
public const int FloorMask = AllDirMask << (int) NavMapChunkType.Floor;
|
||||
|
||||
[Robust.Shared.IoC.Dependency] private readonly TagSystem _tagSystem = default!;
|
||||
[Robust.Shared.IoC.Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private readonly string[] _wallTags = ["Wall", "Window"];
|
||||
private EntityQuery<NavMapDoorComponent> _doorQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -31,112 +34,49 @@ public abstract class SharedNavMapSystem : EntitySystem
|
||||
|
||||
// Data handling events
|
||||
SubscribeLocalEvent<NavMapComponent, ComponentGetState>(OnGetState);
|
||||
_doorQuery = GetEntityQuery<NavMapDoorComponent>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetTileIndex(Vector2i relativeTile)
|
||||
{
|
||||
return relativeTile.X * ChunkSize + relativeTile.Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the chunk's tile into a bitflag for the slot.
|
||||
/// Inverse of <see cref="GetTileIndex"/>
|
||||
/// </summary>
|
||||
public static int GetFlag(Vector2i relativeTile)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2i GetTileFromIndex(int index)
|
||||
{
|
||||
return 1 << (relativeTile.X * ChunkSize + relativeTile.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the chunk's tile into a bitflag for the slot.
|
||||
/// </summary>
|
||||
public static Vector2i GetTile(int flag)
|
||||
{
|
||||
var value = Math.Log2(flag);
|
||||
var x = (int) value / ChunkSize;
|
||||
var y = (int) value % ChunkSize;
|
||||
var result = new Vector2i(x, y);
|
||||
|
||||
DebugTools.Assert(GetFlag(result) == flag);
|
||||
|
||||
var x = index / ChunkSize;
|
||||
var y = index % ChunkSize;
|
||||
return new Vector2i(x, y);
|
||||
}
|
||||
|
||||
public void SetAllEdgesForChunkTile(NavMapChunk chunk, Vector2i tile, NavMapChunkType chunkType)
|
||||
public NavMapChunkType GetEntityType(EntityUid uid)
|
||||
{
|
||||
var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
|
||||
var flag = (ushort) GetFlag(relative);
|
||||
var data = chunk.EnsureType(chunkType);
|
||||
if (_doorQuery.HasComp(uid))
|
||||
return NavMapChunkType.Airlock;
|
||||
|
||||
foreach (var direction in data.Keys)
|
||||
{
|
||||
data[direction] |= flag;
|
||||
}
|
||||
if (_tagSystem.HasAnyTag(uid, _wallTags))
|
||||
return NavMapChunkType.Wall;
|
||||
|
||||
return NavMapChunkType.Invalid;
|
||||
}
|
||||
|
||||
public void UnsetAllEdgesForChunkTile(NavMapChunk chunk, Vector2i tile, NavMapChunkType chunkType)
|
||||
{
|
||||
var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
|
||||
var flag = (ushort) GetFlag(relative);
|
||||
var invFlag = (ushort) ~flag;
|
||||
|
||||
var data = chunk.EnsureType(chunkType);
|
||||
|
||||
foreach (var direction in data.Keys)
|
||||
{
|
||||
data[direction] &= invFlag;
|
||||
}
|
||||
}
|
||||
|
||||
public ushort GetCombinedEdgesForChunk(Dictionary<AtmosDirection, ushort> tile)
|
||||
{
|
||||
ushort combined = 0;
|
||||
|
||||
foreach (var value in tile.Values)
|
||||
{
|
||||
combined |= value;
|
||||
}
|
||||
|
||||
return combined;
|
||||
}
|
||||
|
||||
public bool AllTileEdgesAreOccupied(Dictionary<AtmosDirection, ushort> tileData, Vector2i tile)
|
||||
{
|
||||
var flag = (ushort) GetFlag(tile);
|
||||
|
||||
foreach (var value in tileData.Values)
|
||||
{
|
||||
if ((value & flag) == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public NavMapChunkType GetAssociatedEntityChunkType(EntityUid uid)
|
||||
{
|
||||
var category = NavMapChunkType.Invalid;
|
||||
|
||||
if (HasComp<NavMapDoorComponent>(uid))
|
||||
category = NavMapChunkType.Airlock;
|
||||
|
||||
else if (_tagSystem.HasAnyTag(uid, _wallTags))
|
||||
category = NavMapChunkType.Wall;
|
||||
|
||||
return category;
|
||||
}
|
||||
|
||||
protected bool TryCreateNavMapBeaconData(EntityUid uid, NavMapBeaconComponent component, TransformComponent xform, [NotNullWhen(true)] out NavMapBeacon? beaconData)
|
||||
protected bool TryCreateNavMapBeaconData(EntityUid uid, NavMapBeaconComponent component, TransformComponent xform, MetaDataComponent meta, [NotNullWhen(true)] out NavMapBeacon? beaconData)
|
||||
{
|
||||
beaconData = null;
|
||||
|
||||
if (!component.Enabled || xform.GridUid == null || !xform.Anchored)
|
||||
return false;
|
||||
|
||||
string? name = component.Text;
|
||||
var meta = MetaData(uid);
|
||||
|
||||
var name = component.Text;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
name = meta.EntityName;
|
||||
|
||||
beaconData = new NavMapBeacon(meta.NetEntity, component.Color, name, xform.LocalPosition)
|
||||
{
|
||||
LastUpdate = _gameTiming.CurTick
|
||||
};
|
||||
beaconData = new NavMapBeacon(meta.NetEntity, component.Color, name, xform.LocalPosition);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -145,91 +85,36 @@ public abstract class SharedNavMapSystem : EntitySystem
|
||||
|
||||
private void OnGetState(EntityUid uid, NavMapComponent component, ref ComponentGetState args)
|
||||
{
|
||||
var chunks = new Dictionary<Vector2i, Dictionary<AtmosDirection, ushort>?[]>();
|
||||
var beacons = new HashSet<NavMapBeacon>();
|
||||
Dictionary<Vector2i, int[]> chunks;
|
||||
|
||||
// Should this be a full component state or a delta-state?
|
||||
if (args.FromTick <= component.CreationTick)
|
||||
{
|
||||
// Full state
|
||||
chunks = new(component.Chunks.Count);
|
||||
foreach (var (origin, chunk) in component.Chunks)
|
||||
{
|
||||
var sentChunk = new Dictionary<AtmosDirection, ushort>[NavMapComponent.Categories];
|
||||
chunks.Add(origin, sentChunk);
|
||||
|
||||
foreach (var value in Enum.GetValues<NavMapChunkType>())
|
||||
{
|
||||
ref var data = ref chunk.TileData[(int) value];
|
||||
|
||||
if (data == null)
|
||||
continue;
|
||||
|
||||
var chunkDatum = new Dictionary<AtmosDirection, ushort>(data.Count);
|
||||
|
||||
foreach (var (direction, tileData) in data)
|
||||
{
|
||||
chunkDatum[direction] = tileData;
|
||||
}
|
||||
|
||||
sentChunk[(int) value] = chunkDatum;
|
||||
}
|
||||
chunks.Add(origin, chunk.TileData);
|
||||
}
|
||||
|
||||
var beaconQuery = AllEntityQuery<NavMapBeaconComponent, TransformComponent>();
|
||||
|
||||
while (beaconQuery.MoveNext(out var beaconUid, out var beacon, out var xform))
|
||||
{
|
||||
if (xform.GridUid != uid)
|
||||
continue;
|
||||
|
||||
if (!TryCreateNavMapBeaconData(beaconUid, beacon, xform, out var beaconData))
|
||||
continue;
|
||||
|
||||
beacons.Add(beaconData.Value);
|
||||
}
|
||||
|
||||
args.State = new NavMapComponentState(chunks, beacons);
|
||||
args.State = new NavMapComponentState(chunks, component.Beacons);
|
||||
return;
|
||||
}
|
||||
|
||||
chunks = new();
|
||||
foreach (var (origin, chunk) in component.Chunks)
|
||||
{
|
||||
if (chunk.LastUpdate < args.FromTick)
|
||||
continue;
|
||||
|
||||
var sentChunk = new Dictionary<AtmosDirection, ushort>[NavMapComponent.Categories];
|
||||
chunks.Add(origin, sentChunk);
|
||||
|
||||
foreach (var value in Enum.GetValues<NavMapChunkType>())
|
||||
{
|
||||
ref var data = ref chunk.TileData[(int) value];
|
||||
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
if (data == null)
|
||||
continue;
|
||||
|
||||
var chunkDatum = new Dictionary<AtmosDirection, ushort>(data.Count);
|
||||
|
||||
foreach (var (direction, tileData) in data)
|
||||
{
|
||||
chunkDatum[direction] = tileData;
|
||||
}
|
||||
|
||||
sentChunk[(int) value] = chunkDatum;
|
||||
}
|
||||
chunks.Add(origin, chunk.TileData);
|
||||
}
|
||||
|
||||
foreach (var beacon in component.Beacons)
|
||||
{
|
||||
if (beacon.LastUpdate < args.FromTick)
|
||||
continue;
|
||||
|
||||
beacons.Add(beacon);
|
||||
}
|
||||
|
||||
args.State = new NavMapComponentState(chunks, beacons)
|
||||
args.State = new NavMapComponentState(chunks, component.Beacons)
|
||||
{
|
||||
// TODO NAVMAP cache a single AllChunks hashset in the component.
|
||||
// Or maybe just only send them if a chunk gets removed.
|
||||
AllChunks = new(component.Chunks.Keys),
|
||||
AllBeacons = new(component.Beacons)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -238,22 +123,18 @@ public abstract class SharedNavMapSystem : EntitySystem
|
||||
#region: System messages
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
protected sealed class NavMapComponentState : ComponentState, IComponentDeltaState
|
||||
protected sealed class NavMapComponentState(
|
||||
Dictionary<Vector2i, int[]> chunks,
|
||||
Dictionary<NetEntity, NavMapBeacon> beacons)
|
||||
: ComponentState, IComponentDeltaState
|
||||
{
|
||||
public Dictionary<Vector2i, Dictionary<AtmosDirection, ushort>?[]> Chunks = new();
|
||||
public HashSet<NavMapBeacon> Beacons = new();
|
||||
public Dictionary<Vector2i, int[]> Chunks = chunks;
|
||||
public Dictionary<NetEntity, NavMapBeacon> Beacons = beacons;
|
||||
|
||||
// Required to infer deleted/missing chunks for delta states
|
||||
public HashSet<Vector2i>? AllChunks;
|
||||
public HashSet<NavMapBeacon>? AllBeacons;
|
||||
|
||||
public NavMapComponentState(Dictionary<Vector2i, Dictionary<AtmosDirection, ushort>?[]> chunks, HashSet<NavMapBeacon> beacons)
|
||||
{
|
||||
Chunks = chunks;
|
||||
Beacons = beacons;
|
||||
}
|
||||
|
||||
public bool FullState => (AllChunks == null || AllBeacons == null);
|
||||
public bool FullState => AllChunks == null;
|
||||
|
||||
public void ApplyToFullState(IComponentState fullState)
|
||||
{
|
||||
@@ -261,32 +142,24 @@ public abstract class SharedNavMapSystem : EntitySystem
|
||||
var state = (NavMapComponentState) fullState;
|
||||
DebugTools.Assert(state.FullState);
|
||||
|
||||
// Update chunks
|
||||
foreach (var key in state.Chunks.Keys)
|
||||
{
|
||||
if (!AllChunks!.Contains(key))
|
||||
state.Chunks.Remove(key);
|
||||
}
|
||||
|
||||
foreach (var (chunk, data) in Chunks)
|
||||
foreach (var (index, data) in Chunks)
|
||||
{
|
||||
for (var i = 0; i < NavMapComponent.Categories; i++)
|
||||
{
|
||||
var chunkData = data[i];
|
||||
state.Chunks[chunk][i] = chunkData == null ? chunkData : new(chunkData);
|
||||
}
|
||||
if (!state.Chunks.TryGetValue(index, out var stateValue))
|
||||
state.Chunks[index] = stateValue = new int[data.Length];
|
||||
|
||||
Array.Copy(data, stateValue, data.Length);
|
||||
}
|
||||
|
||||
// Update beacons
|
||||
foreach (var beacon in state.Beacons)
|
||||
state.Beacons.Clear();
|
||||
foreach (var (nuid, beacon) in Beacons)
|
||||
{
|
||||
if (!AllBeacons!.Contains(beacon))
|
||||
state.Beacons.Remove(beacon);
|
||||
}
|
||||
|
||||
foreach (var beacon in Beacons)
|
||||
{
|
||||
state.Beacons.Add(beacon);
|
||||
state.Beacons.Add(nuid, beacon);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,56 +169,26 @@ public abstract class SharedNavMapSystem : EntitySystem
|
||||
var state = (NavMapComponentState) fullState;
|
||||
DebugTools.Assert(state.FullState);
|
||||
|
||||
var chunks = new Dictionary<Vector2i, Dictionary<AtmosDirection, ushort>?[]>();
|
||||
var beacons = new HashSet<NavMapBeacon>();
|
||||
|
||||
foreach (var (chunk, data) in Chunks)
|
||||
var chunks = new Dictionary<Vector2i, int[]>(state.Chunks.Count);
|
||||
foreach (var (index, data) in state.Chunks)
|
||||
{
|
||||
for (var i = 0; i < NavMapComponent.Categories; i++)
|
||||
{
|
||||
var chunkData = data[i];
|
||||
state.Chunks[chunk][i] = chunkData == null ? chunkData : new(chunkData);
|
||||
}
|
||||
if (!AllChunks!.Contains(index))
|
||||
continue;
|
||||
|
||||
var newData = chunks[index] = new int[ArraySize];
|
||||
|
||||
if (Chunks.TryGetValue(index, out var updatedData))
|
||||
Array.Copy(newData, updatedData, ArraySize);
|
||||
else
|
||||
Array.Copy(newData, data, ArraySize);
|
||||
}
|
||||
|
||||
foreach (var (chunk, data) in state.Chunks)
|
||||
{
|
||||
if (AllChunks!.Contains(chunk))
|
||||
{
|
||||
var copied = new Dictionary<AtmosDirection, ushort>?[NavMapComponent.Categories];
|
||||
|
||||
for (var i = 0; i < NavMapComponent.Categories; i++)
|
||||
{
|
||||
var chunkData = data[i];
|
||||
copied[i] = chunkData == null ? chunkData : new(chunkData);
|
||||
}
|
||||
|
||||
chunks.TryAdd(chunk, copied);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var beacon in Beacons)
|
||||
{
|
||||
beacons.Add(new NavMapBeacon(beacon.NetEnt, beacon.Color, beacon.Text, beacon.Position));
|
||||
}
|
||||
|
||||
foreach (var beacon in state.Beacons)
|
||||
{
|
||||
if (AllBeacons!.Contains(beacon))
|
||||
{
|
||||
beacons.Add(new NavMapBeacon(beacon.NetEnt, beacon.Color, beacon.Text, beacon.Position));
|
||||
}
|
||||
}
|
||||
|
||||
return new NavMapComponentState(chunks, beacons);
|
||||
return new NavMapComponentState(chunks, new(Beacons));
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public record struct NavMapBeacon(NetEntity NetEnt, Color Color, string Text, Vector2 Position)
|
||||
{
|
||||
public GameTick LastUpdate;
|
||||
}
|
||||
public record struct NavMapBeacon(NetEntity NetEnt, Color Color, string Text, Vector2 Position);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Shared.Power;
|
||||
@@ -5,4 +6,23 @@ namespace Content.Shared.Power;
|
||||
[UsedImplicitly]
|
||||
public abstract class SharedPowerMonitoringConsoleSystem : EntitySystem
|
||||
{
|
||||
// Chunk size is limited as we require ChunkSize^2 <= 32 (number of bits in an int)
|
||||
public const int ChunkSize = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Converts the chunk's tile into a bitflag for the slot.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetFlag(Vector2i relativeTile)
|
||||
{
|
||||
return 1 << (relativeTile.X * ChunkSize + relativeTile.Y);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2i GetTileFromIndex(int index)
|
||||
{
|
||||
var x = index / ChunkSize;
|
||||
var y = index % ChunkSize;
|
||||
return new Vector2i(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,7 +541,15 @@ public sealed class TagSystem : EntitySystem
|
||||
/// </exception>
|
||||
public bool HasAnyTag(TagComponent component, params string[] ids)
|
||||
{
|
||||
return HasAnyTag(component, ids.AsEnumerable());
|
||||
foreach (var id in ids)
|
||||
{
|
||||
AssertValidTag(id);
|
||||
|
||||
if (component.Tags.Contains(id))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user