using System;
using System.Diagnostics.Contracts;
using System.Numerics;
using System.Runtime.CompilerServices;
using Robust.Shared.Map;
namespace Robust.Shared.GameObjects;
public abstract partial class SharedTransformSystem
{
/*
* Helper methods for working with EntityCoordinates / MapCoordinates.
* For grid methods see SharedMapSystem.Coordinates
*/
///
/// Verifies that this set of coordinates can be currently resolved to a location.
///
/// if this set of coordinates can be currently resolved to a location, otherwise .
public bool IsValid(EntityCoordinates coordinates)
{
var entity = coordinates.EntityId;
if (!entity.IsValid() || !Exists(entity))
return false;
if (!float.IsFinite(coordinates.Position.X) || !float.IsFinite(coordinates.Position.Y))
return false;
return true;
}
///
/// Returns a new set of EntityCoordinates local to a new entity.
///
/// The entity that the new coordinates will be local to
/// A new set of EntityCoordinates local to a new entity.
public EntityCoordinates WithEntityId(EntityCoordinates coordinates, EntityUid entity)
{
return entity == coordinates.EntityId
? coordinates
: ToCoordinates(entity, ToMapCoordinates(coordinates));
}
///
/// Converts entity-local coordinates into map terms.
///
public MapCoordinates ToMapCoordinates(EntityCoordinates coordinates, bool logError = true)
{
if (!TryComp(coordinates.EntityId, out TransformComponent? xform))
{
if (logError)
Log.Error($"Attempted to convert coordinates with invalid entity: {coordinates}. Trace: {Environment.StackTrace}");
return MapCoordinates.Nullspace;
}
Vector2 pos = xform._localRotation.RotateVec(coordinates.Position) + xform._localPosition;
while (xform.ParentUid != xform.MapUid && xform.ParentUid.IsValid())
{
xform = XformQuery.GetComponent(xform.ParentUid);
pos = xform._localRotation.RotateVec(pos) + xform._localPosition;
}
return new MapCoordinates(pos, xform.MapID);
}
///
/// Converts entity-local coordinates into map terms.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MapCoordinates ToMapCoordinates(NetCoordinates coordinates)
{
var eCoords = GetCoordinates(coordinates);
return ToMapCoordinates(eCoords);
}
///
/// Converts entity-local coordinates into map terms.
/// The same as ToMapCoordinates(coordinates, logError).Position, but doesn't have to construct the MapCoordinates first.
///
public Vector2 ToWorldPosition(EntityCoordinates coordinates, bool logError = true)
{
if (!TryComp(coordinates.EntityId, out TransformComponent? xform))
{
if (logError)
Log.Error($"Attempted to convert coordinates with invalid entity: {coordinates}. Trace: {Environment.StackTrace}");
return Vector2.Zero;
}
Vector2 pos = xform._localRotation.RotateVec(coordinates.Position) + xform._localPosition;
while (xform.ParentUid != xform.MapUid && xform.ParentUid.IsValid())
{
xform = XformQuery.GetComponent(xform.ParentUid);
pos = xform._localRotation.RotateVec(pos) + xform._localPosition;
}
return pos;
}
///
/// Converts entity-local coordinates into map terms.
/// The same as ToMapCoordinates(coordinates).Position, but doesn't have to construct the MapCoordinates first.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector2 ToWorldPosition(NetCoordinates coordinates)
{
var eCoords = GetCoordinates(coordinates);
return ToWorldPosition(eCoords);
}
///
/// Creates EntityCoordinates given an entity and some MapCoordinates.
///
public EntityCoordinates ToCoordinates(Entity entity, MapCoordinates coordinates)
{
if (!Resolve(entity, ref entity.Comp, false))
{
Log.Error($"Attempted to convert coordinates with invalid entity: {coordinates}. Trace: {Environment.StackTrace}");
return default;
}
if (entity.Comp.MapID != coordinates.MapId)
{
Log.Error($"Attempted to convert map coordinates {coordinates} to entity coordinates on a different map. Entity: {ToPrettyString(entity)}. Trace: {Environment.StackTrace}");
return default;
}
var localPos = Vector2.Transform(coordinates.Position, GetInvWorldMatrix(entity.Comp));
return new EntityCoordinates(entity, localPos);
}
///
/// Creates map-relative given some .
///
[Pure]
public EntityCoordinates ToCoordinates(MapCoordinates coordinates)
{
if (_map.TryGetMap(coordinates.MapId, out var uid))
return ToCoordinates(uid.Value, coordinates);
Log.Error($"Attempted to convert map coordinates with unknown map id: {coordinates}. Trace: {Environment.StackTrace}");
return default;
}
///
/// Returns the grid that the entity whose position the coordinates are relative to is on.
///
[Pure]
public EntityUid? GetGrid(EntityCoordinates coordinates)
{
return GetGrid(coordinates.EntityId);
}
[Pure]
public EntityUid? GetGrid(Entity entity)
{
return !Resolve(entity, ref entity.Comp, logMissing:false) ? null : entity.Comp.GridUid;
}
///
/// Returns the Map Id these coordinates are on.
///
[Pure]
public MapId GetMapId(EntityCoordinates coordinates)
{
return GetMapId(coordinates.EntityId);
}
public MapId GetMapId(Entity entity)
{
return !Resolve(entity, ref entity.Comp, logMissing: false) ? MapId.Nullspace : entity.Comp.MapID;
}
///
/// Returns the Map that these coordinates are on.
///
public EntityUid? GetMap(EntityCoordinates coordinates)
{
return GetMap(coordinates.EntityId);
}
public EntityUid? GetMap(Entity entity)
{
return !Resolve(entity, ref entity.Comp, logMissing: false) ? null : entity.Comp.MapUid;
}
///
/// Compares two sets of coordinates to see if they are in range of each other.
///
/// maximum distance between the two sets of coordinates.
/// True if the two points are within a given range.
public bool InRange(EntityCoordinates coordA, EntityCoordinates coordB, float range)
{
if (!coordA.EntityId.IsValid() || !coordB.EntityId.IsValid())
return false;
if (coordA.EntityId == coordB.EntityId)
return (coordA.Position - coordB.Position).LengthSquared() < range * range;
var mapA = ToMapCoordinates(coordA, logError:false);
var mapB = ToMapCoordinates(coordB, logError:false);
if (mapA.MapId != mapB.MapId || mapA.MapId == MapId.Nullspace)
return false;
return mapA.InRange(mapB, range);
}
///
/// Compares the positions of two entities to see if they are within some specified distance of each other.
///
public bool InRange(Entity entA, Entity entB, float range)
{
if (!Resolve(entA, ref entA.Comp, logMissing: false))
return false;
if (!Resolve(entB, ref entB.Comp, logMissing: false))
return false;
if (!entA.Comp.ParentUid.IsValid() || !entB.Comp.ParentUid.IsValid())
return false;
if (entA.Comp.ParentUid == entB.Comp.ParentUid)
return (entA.Comp.LocalPosition - entB.Comp.LocalPosition).LengthSquared() < range * range;
if (entA.Comp.ParentUid == entB.Owner)
return entA.Comp.LocalPosition.LengthSquared() < range * range;
if (entB.Comp.ParentUid == entA.Owner)
return entB.Comp.LocalPosition.LengthSquared() < range * range;
var mapA = GetMapCoordinates(entA!);
var mapB = GetMapCoordinates(entB!);
if (mapA.MapId != mapB.MapId || mapA.MapId == MapId.Nullspace)
return false;
return mapA.InRange(mapB, range);
}
///
/// Returns a readable string with the entity's local and world position and rotation. Useful for debugging.
///
public string GetDebugString(Entity ent)
{
if (!Resolve(ent, ref ent.Comp, logMissing: false))
return "invalid";
return $"pos/rot/wpos/wrot: {ent.Comp.Coordinates}/{ent.Comp.LocalRotation}/{GetWorldPosition(ent.Comp)}/{GetWorldRotation(ent.Comp)}";
}
}