using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Robust.Shared.GameObjects
{
///
/// Makes it possible to look this entity up with the snap grid.
///
public class SnapGridComponent : Component, IComponentDebug
{
public const string LogCategory = "go.comp.snapgrid";
public sealed override string Name => "SnapGrid";
private bool IsSet;
[DataField("offset")]
private SnapGridOffset _offset = SnapGridOffset.Center;
[Dependency] private readonly IMapManager _mapManager = default!;
public event Action? OnPositionChanged;
private GridId _lastGrid;
public Vector2i Position { get; private set; }
public SnapGridOffset Offset => _offset;
///
public override void Initialize()
{
base.Initialize();
UpdatePosition();
}
///
protected override void Shutdown()
{
base.Shutdown();
if (IsSet)
{
if (_mapManager.TryGetGrid(_lastGrid, out var grid))
{
grid.RemoveFromSnapGridCell(Position, Offset, this);
return;
}
IsSet = false;
}
}
///
/// Returns an enumerable over all the entities which are one tile over in a certain direction.
///
public IEnumerable GetInDir(Direction dir)
{
if (!_mapManager.TryGetGrid(Owner.Transform.GridID, out var grid))
return Enumerable.Empty();
var pos = SnapGridPosAt(dir);
return grid.GetSnapGridCell(pos, Offset).Select(s => s.Owner);
}
[Pure]
public IEnumerable GetOffset(Vector2i offset)
{
if (!_mapManager.TryGetGrid(Owner.Transform.GridID, out var grid))
return Enumerable.Empty();
var pos = Position + offset;
return grid.GetSnapGridCell(pos, Offset).Select(s => s.Owner);
}
public IEnumerable GetLocal()
{
if (!_mapManager.TryGetGrid(Owner.Transform.GridID, out var grid))
return Enumerable.Empty();
return grid.GetSnapGridCell(Position, Offset).Select(s => s.Owner);
}
public string GetDebugString()
{
return $"ofs/pos: {Offset}/{Position}";
}
public EntityCoordinates DirectionToGrid(Direction direction)
{
if (!_mapManager.TryGetGrid(Owner.Transform.GridID, out var grid))
return Owner.Transform.Coordinates.Offset(direction.ToVec());
var coords = grid.GridTileToLocal(SnapGridPosAt(direction));
return coords;
}
Vector2i SnapGridPosAt(Direction dir, int dist = 1)
{
switch (dir)
{
case Direction.East:
return Position + new Vector2i(dist, 0);
case Direction.SouthEast:
return Position + new Vector2i(dist, -dist);
case Direction.South:
return Position + new Vector2i(0, -dist);
case Direction.SouthWest:
return Position + new Vector2i(-dist, -dist);
case Direction.West:
return Position + new Vector2i(-dist, 0);
case Direction.NorthWest:
return Position + new Vector2i(-dist, dist);
case Direction.North:
return Position + new Vector2i(0, dist);
case Direction.NorthEast:
return Position + new Vector2i(dist, dist);
default:
throw new NotImplementedException();
}
}
public IEnumerable GetCardinalNeighborCells()
{
if (!_mapManager.TryGetGrid(Owner.Transform.GridID, out var grid))
yield break;
foreach (var cell in grid.GetSnapGridCell(Position, Offset))
yield return cell;
foreach (var cell in grid.GetSnapGridCell(Position + new Vector2i(0, 1), Offset))
yield return cell;
foreach (var cell in grid.GetSnapGridCell(Position + new Vector2i(0, -1), Offset))
yield return cell;
foreach (var cell in grid.GetSnapGridCell(Position + new Vector2i(1, 0), Offset))
yield return cell;
foreach (var cell in grid.GetSnapGridCell(Position + new Vector2i(-1, 0), Offset))
yield return cell;
}
public IEnumerable GetCellsInSquareArea(int n = 1)
{
if (!_mapManager.TryGetGrid(Owner.Transform.GridID, out var grid))
yield break;
for (var y = -n; y <= n; ++y)
for (var x = -n; x <= n; ++x)
{
foreach (var cell in grid.GetSnapGridCell(Position + new Vector2i(x, y), Offset))
yield return cell;
}
}
internal void UpdatePosition()
{
if (IsSet)
{
if (!_mapManager.TryGetGrid(_lastGrid, out var lastGrid))
{
Logger.WarningS(LogCategory, "Entity {0} snapgrid didn't find grid {1}. Race condition?", Owner.Uid, Owner.Transform.GridID);
return;
}
lastGrid.RemoveFromSnapGridCell(Position, Offset, this);
}
if (!_mapManager.TryGetGrid(Owner.Transform.GridID, out var grid))
{
// Either a race condition, or we're not on any grids.
return;
}
IsSet = true;
var oldPos = Position;
Position = grid.SnapGridCellFor(Owner.Transform.Coordinates, Offset);
_lastGrid = Owner.Transform.GridID;
grid.AddToSnapGridCell(Position, Offset, this);
if (oldPos != Position)
{
OnPositionChanged?.Invoke();
}
}
}
public enum SnapGridOffset: byte
{
///
/// Center snap grid (wires, pipes, ...).
///
Center,
///
/// Edge snap grid (walls, ...).
///
Edge,
}
}