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:
Leon Friedrich
2024-05-02 14:51:21 +12:00
committed by GitHub
parent 11a4f9d21f
commit a7e6337cbd
10 changed files with 395 additions and 640 deletions

View File

@@ -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()