mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
784 lines
26 KiB
C#
784 lines
26 KiB
C#
using System;
|
|
using System.Runtime.CompilerServices;
|
|
using JetBrains.Annotations;
|
|
using Robust.Shared.GameStates;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Log;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Maths;
|
|
using Robust.Shared.Physics;
|
|
using Robust.Shared.Timing;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Robust.Shared.GameObjects;
|
|
|
|
public abstract partial class SharedTransformSystem
|
|
{
|
|
[IoC.Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
|
|
#region Anchoring
|
|
|
|
internal void ReAnchor(TransformComponent xform,
|
|
MapGridComponent oldGrid,
|
|
MapGridComponent newGrid,
|
|
Vector2i tilePos,
|
|
TransformComponent oldGridXform,
|
|
TransformComponent newGridXform,
|
|
EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
// Bypass some of the expensive stuff in unanchoring / anchoring.
|
|
oldGrid.Grid.RemoveFromSnapGridCell(tilePos, xform.Owner);
|
|
newGrid.Grid.AddToSnapGridCell(tilePos, xform.Owner);
|
|
// TODO: Could do this re-parent way better.
|
|
// Unfortunately we don't want any anchoring events to go out hence... this.
|
|
xform._anchored = false;
|
|
oldGridXform._children.Remove(xform.Owner);
|
|
newGridXform._children.Add(xform.Owner);
|
|
xform._parent = newGrid.Owner;
|
|
xform._anchored = true;
|
|
|
|
SetGridId(xform, newGrid.Owner, xformQuery);
|
|
var reParent = new EntParentChangedMessage(xform.Owner, oldGrid.Owner, xform.MapID, xform);
|
|
RaiseLocalEvent(xform.Owner, ref reParent, true);
|
|
// TODO: Ideally shouldn't need to call the moveevent
|
|
var movEevee = new MoveEvent(xform.Owner,
|
|
new EntityCoordinates(oldGrid.Owner, xform._localPosition),
|
|
new EntityCoordinates(newGrid.Owner, xform._localPosition),
|
|
xform,
|
|
_gameTiming.ApplyingState);
|
|
RaiseLocalEvent(xform.Owner, ref movEevee, true);
|
|
|
|
DebugTools.Assert(xformQuery.GetComponent(oldGrid.Owner).MapID == xformQuery.GetComponent(newGrid.Owner).MapID);
|
|
DebugTools.Assert(xform._anchored);
|
|
|
|
Dirty(xform);
|
|
var ev = new ReAnchorEvent(xform.Owner, oldGrid.Owner, newGrid.Owner, tilePos);
|
|
RaiseLocalEvent(xform.Owner, ref ev);
|
|
}
|
|
|
|
public bool AnchorEntity(TransformComponent xform, IMapGrid grid, Vector2i tileIndices)
|
|
{
|
|
var result = grid.AddToSnapGridCell(tileIndices, xform.Owner);
|
|
|
|
if (result)
|
|
{
|
|
// Mark as static first to avoid the velocity change on parent change.
|
|
if (TryComp<PhysicsComponent>(xform.Owner, out var physicsComponent))
|
|
physicsComponent.BodyType = BodyType.Static;
|
|
|
|
// anchor snapping
|
|
// Internally it will do the parent update; doing it separately just triggers a redundant move.
|
|
xform.Coordinates = new EntityCoordinates(grid.GridEntityId, grid.GridTileToLocal(tileIndices).Position);
|
|
xform.SetAnchored(result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public bool AnchorEntity(TransformComponent xform, IMapGridComponent component)
|
|
{
|
|
return AnchorEntity(xform, component.Grid);
|
|
}
|
|
|
|
public bool AnchorEntity(TransformComponent xform, IMapGrid grid)
|
|
{
|
|
var tileIndices = grid.TileIndicesFor(xform.Coordinates);
|
|
return AnchorEntity(xform, grid, tileIndices);
|
|
}
|
|
|
|
public bool AnchorEntity(TransformComponent xform)
|
|
{
|
|
if (!_mapManager.TryGetGrid(xform.GridUid, out var grid))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var tileIndices = grid.TileIndicesFor(xform.Coordinates);
|
|
return AnchorEntity(xform, grid, tileIndices);
|
|
}
|
|
|
|
public void Unanchor(TransformComponent xform)
|
|
{
|
|
//HACK: Client grid pivot causes this.
|
|
//TODO: make grid components the actual grid
|
|
if(xform.GridUid == null)
|
|
return;
|
|
|
|
UnanchorEntity(xform, Comp<IMapGridComponent>(xform.GridUid.Value));
|
|
}
|
|
|
|
public void UnanchorEntity(TransformComponent xform, IMapGridComponent grid)
|
|
{
|
|
var tileIndices = grid.Grid.TileIndicesFor(xform.Coordinates);
|
|
grid.Grid.RemoveFromSnapGridCell(tileIndices, xform.Owner);
|
|
if (TryComp<PhysicsComponent>(xform.Owner, out var physicsComponent))
|
|
{
|
|
physicsComponent.BodyType = BodyType.Dynamic;
|
|
}
|
|
|
|
xform.SetAnchored(false);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Contains
|
|
|
|
/// <summary>
|
|
/// Returns whether the given entity is a child of this transform or one of its descendants.
|
|
/// </summary>
|
|
public bool ContainsEntity(TransformComponent xform, EntityUid entity)
|
|
{
|
|
return ContainsEntity(xform, entity, GetEntityQuery<TransformComponent>());
|
|
}
|
|
|
|
/// <inheritdoc cref="ContainsEntity(Robust.Shared.GameObjects.TransformComponent,Robust.Shared.GameObjects.EntityUid)"/>
|
|
public bool ContainsEntity(TransformComponent xform, EntityUid entity, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
return ContainsEntity(xform, xformQuery.GetComponent(entity), xformQuery);
|
|
}
|
|
|
|
/// <inheritdoc cref="ContainsEntity(Robust.Shared.GameObjects.TransformComponent,Robust.Shared.GameObjects.EntityUid)"/>
|
|
public bool ContainsEntity(TransformComponent xform, TransformComponent entityTransform)
|
|
{
|
|
return ContainsEntity(xform, entityTransform, GetEntityQuery<TransformComponent>());
|
|
}
|
|
|
|
/// <inheritdoc cref="ContainsEntity(Robust.Shared.GameObjects.TransformComponent,Robust.Shared.GameObjects.EntityUid)"/>
|
|
public bool ContainsEntity(TransformComponent xform, TransformComponent entityTransform, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
// Is the entity the scene root
|
|
if (!entityTransform.ParentUid.IsValid())
|
|
return false;
|
|
|
|
// Is this the direct parent of the entity
|
|
if (xform.Owner == entityTransform.ParentUid)
|
|
return true;
|
|
|
|
// Recursively search up the parents for this object
|
|
var parentXform = xformQuery.GetComponent(entityTransform.ParentUid);
|
|
return ContainsEntity(xform, parentXform, xformQuery);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Component Lifetime
|
|
|
|
private void OnCompInit(EntityUid uid, TransformComponent component, ComponentInit args)
|
|
{
|
|
// Children MAY be initialized here before their parents are.
|
|
// We do this whole dance to handle this recursively,
|
|
// setting _mapIdInitialized along the way to avoid going to the IMapComponent every iteration.
|
|
static MapId FindMapIdAndSet(TransformComponent xform, IEntityManager entMan, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
if (xform._mapIdInitialized)
|
|
return xform.MapID;
|
|
|
|
MapId value;
|
|
|
|
if (xform.ParentUid.IsValid())
|
|
{
|
|
value = FindMapIdAndSet(xformQuery.GetComponent(xform.ParentUid), entMan, xformQuery);
|
|
}
|
|
else
|
|
{
|
|
// second level node, terminates recursion up the branch of the tree
|
|
if (entMan.TryGetComponent(xform.Owner, out IMapComponent? mapComp))
|
|
{
|
|
value = mapComp.WorldMap;
|
|
}
|
|
else
|
|
{
|
|
// We allow entities to be spawned directly into null-space.
|
|
value = MapId.Nullspace;
|
|
}
|
|
}
|
|
|
|
xform.MapID = value;
|
|
xform._mapIdInitialized = true;
|
|
return value;
|
|
}
|
|
|
|
var xformQuery = GetEntityQuery<TransformComponent>();
|
|
|
|
if (!component._mapIdInitialized)
|
|
{
|
|
FindMapIdAndSet(component, EntityManager, xformQuery);
|
|
component._mapIdInitialized = true;
|
|
}
|
|
|
|
// Has to be done if _parent is set from ExposeData.
|
|
if (component.ParentUid.IsValid())
|
|
{
|
|
// Note that _children is a SortedSet<EntityUid>,
|
|
// so duplicate additions (which will happen) don't matter.
|
|
|
|
var parentXform = xformQuery.GetComponent(component.ParentUid);
|
|
if (parentXform.LifeStage > ComponentLifeStage.Running || LifeStage(parentXform.Owner) > EntityLifeStage.MapInitialized)
|
|
{
|
|
var msg = $"Attempted to re-parent to a terminating object. Entity: {ToPrettyString(parentXform.Owner)}, new parent: {ToPrettyString(uid)}";
|
|
#if EXCEPTION_TOLERANCE
|
|
Logger.Error(msg);
|
|
Del(uid);
|
|
#else
|
|
throw new InvalidOperationException(msg);
|
|
#endif
|
|
}
|
|
|
|
parentXform._children.Add(uid);
|
|
}
|
|
|
|
|
|
SetGridId(component, component.FindGridEntityId(xformQuery));
|
|
component.RebuildMatrices();
|
|
}
|
|
|
|
private void OnCompStartup(EntityUid uid, TransformComponent component, ComponentStartup args)
|
|
{
|
|
// Re-Anchor the entity if needed.
|
|
if (component._anchored && _mapManager.TryFindGridAt(component.MapPosition, out var grid))
|
|
{
|
|
if (!grid.IsAnchored(component.Coordinates, uid))
|
|
{
|
|
AnchorEntity(component, grid);
|
|
}
|
|
}
|
|
else
|
|
component._anchored = false;
|
|
|
|
// Keep the cached matrices in sync with the fields.
|
|
Dirty(component);
|
|
|
|
var ev = new TransformStartupEvent(component);
|
|
RaiseLocalEvent(uid, ref ev, true);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GridId
|
|
|
|
/// <summary>
|
|
/// Sets the <see cref="GridId"/> for the transformcomponent. Does not Dirty it.
|
|
/// </summary>
|
|
public void SetGridId(TransformComponent xform, EntityUid? gridId, EntityQuery<TransformComponent>? xformQuery = null)
|
|
{
|
|
if (xform._gridUid == gridId) return;
|
|
|
|
|
|
DebugTools.Assert(gridId == null || HasComp<MapGridComponent>(gridId));
|
|
|
|
xformQuery ??= GetEntityQuery<TransformComponent>();
|
|
SetGridIdRecursive(xform, gridId, xformQuery.Value);
|
|
}
|
|
|
|
private static void SetGridIdRecursive(TransformComponent xform, EntityUid? gridId, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
xform._gridUid = gridId;
|
|
var childEnumerator = xform.ChildEnumerator;
|
|
|
|
while (childEnumerator.MoveNext(out var child))
|
|
{
|
|
SetGridIdRecursive(xformQuery.GetComponent(child.Value), gridId, xformQuery);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Local Position
|
|
|
|
public void SetLocalPosition(EntityUid uid, Vector2 value, TransformComponent? xform = null)
|
|
{
|
|
if (!Resolve(uid, ref xform)) return;
|
|
SetLocalPosition(xform, value);
|
|
}
|
|
|
|
public virtual void SetLocalPosition(TransformComponent xform, Vector2 value)
|
|
{
|
|
xform.LocalPosition = value;
|
|
}
|
|
|
|
public void SetLocalPositionNoLerp(EntityUid uid, Vector2 value, TransformComponent? xform = null)
|
|
{
|
|
if (!Resolve(uid, ref xform)) return;
|
|
SetLocalPositionNoLerp(xform, value);
|
|
}
|
|
|
|
public virtual void SetLocalPositionNoLerp(TransformComponent xform, Vector2 value)
|
|
{
|
|
xform.LocalPosition = value;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Local Rotation
|
|
|
|
public void SetLocalRotation(EntityUid uid, Angle value, TransformComponent? xform = null)
|
|
{
|
|
if (!Resolve(uid, ref xform)) return;
|
|
SetLocalRotation(xform, value);
|
|
}
|
|
|
|
public virtual void SetLocalRotation(TransformComponent xform, Angle value)
|
|
{
|
|
xform.LocalRotation = value;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Parent
|
|
|
|
public TransformComponent? GetParent(EntityUid uid)
|
|
{
|
|
return GetParent(uid, GetEntityQuery<TransformComponent>());
|
|
}
|
|
|
|
public TransformComponent? GetParent(EntityUid uid, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
return GetParent(xformQuery.GetComponent(uid), xformQuery);
|
|
}
|
|
|
|
public TransformComponent? GetParent(TransformComponent xform)
|
|
{
|
|
return GetParent(xform, GetEntityQuery<TransformComponent>());
|
|
}
|
|
|
|
public TransformComponent? GetParent(TransformComponent xform, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
if (!xform.ParentUid.IsValid()) return null;
|
|
return xformQuery.GetComponent(xform.ParentUid);
|
|
}
|
|
|
|
/* TODO: Need to peel out relevant bits of AttachParent e.g. children updates.
|
|
public void SetParent(TransformComponent xform, EntityUid parent, bool move = true)
|
|
{
|
|
if (xform.ParentUid == parent) return;
|
|
|
|
if (!parent.IsValid())
|
|
{
|
|
xform.AttachToGridOrMap();
|
|
return;
|
|
}
|
|
|
|
if (xform.Anchored)
|
|
{
|
|
xform.Anchored = false;
|
|
}
|
|
|
|
if (move)
|
|
xform.AttachParent(parent);
|
|
else
|
|
xform._parent = parent;
|
|
|
|
Dirty(xform);
|
|
}
|
|
*/
|
|
|
|
#endregion
|
|
|
|
#region States
|
|
|
|
protected void ActivateLerp(TransformComponent xform)
|
|
{
|
|
if (xform.ActivelyLerping)
|
|
return;
|
|
|
|
xform.ActivelyLerping = true;
|
|
RaiseLocalEvent(xform.Owner, new TransformStartLerpMessage(xform), true);
|
|
}
|
|
|
|
internal void OnGetState(EntityUid uid, TransformComponent component, ref ComponentGetState args)
|
|
{
|
|
args.State = new TransformComponentState(
|
|
component.LocalPosition,
|
|
component.LocalRotation,
|
|
component.ParentUid,
|
|
component.NoLocalRotation,
|
|
component.Anchored);
|
|
}
|
|
|
|
internal void OnHandleState(EntityUid uid, TransformComponent component, ref ComponentHandleState args)
|
|
{
|
|
if (args.Current is TransformComponentState newState)
|
|
{
|
|
var newParentId = newState.ParentID;
|
|
var rebuildMatrices = false;
|
|
|
|
// Update to new parent.
|
|
// if the new one isn't valid (e.g. PVS) then we'll just return early after yeeting them to nullspace.
|
|
if (component.ParentUid != newParentId)
|
|
{
|
|
if (!newParentId.IsValid())
|
|
{
|
|
DetachParentToNull(component);
|
|
}
|
|
else
|
|
{
|
|
if (!Exists(newParentId))
|
|
{
|
|
#if !EXCEPTION_TOLERANCE
|
|
throw new InvalidOperationException($"Unable to find new parent {newParentId}! This probably means the server never sent it.");
|
|
#else
|
|
_logger.Error($"Unable to find new parent {newParentId}! Deleting {ToPrettyString(uid)}");
|
|
QueueDel(uid);
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
component.AttachParent(Transform(newParentId));
|
|
}
|
|
|
|
rebuildMatrices = true;
|
|
}
|
|
|
|
if (component.LocalRotation != newState.Rotation)
|
|
{
|
|
component._localRotation = newState.Rotation;
|
|
rebuildMatrices = true;
|
|
}
|
|
|
|
if (!component.LocalPosition.EqualsApprox(newState.LocalPosition))
|
|
{
|
|
var oldPos = component.Coordinates;
|
|
component._localPosition = newState.LocalPosition;
|
|
|
|
var ev = new MoveEvent(uid, oldPos, component.Coordinates, component, _gameTiming.ApplyingState);
|
|
DeferMoveEvent(ref ev);
|
|
|
|
rebuildMatrices = true;
|
|
}
|
|
|
|
component._prevPosition = newState.LocalPosition;
|
|
component._prevRotation = newState.Rotation;
|
|
|
|
// Anchored currently does a TryFindGridAt internally which may fail in particularly... violent situations.
|
|
if (newState.Anchored && !component.Anchored)
|
|
{
|
|
DebugTools.Assert(component.GridUid != null);
|
|
var iGrid = Comp<MapGridComponent>(component.GridUid!.Value);
|
|
AnchorEntity(component, iGrid);
|
|
DebugTools.Assert(component.Anchored);
|
|
}
|
|
else
|
|
{
|
|
component.Anchored = newState.Anchored;
|
|
}
|
|
|
|
component._noLocalRotation = newState.NoLocalRotation;
|
|
|
|
// This is not possible, because client entities don't exist on the server, so the parent HAS to be a shared entity.
|
|
// If this assert fails, the code above that sets the parent is broken.
|
|
DebugTools.Assert(!component.ParentUid.IsClientSide(), "Transform received a state, but is still parented to a client entity.");
|
|
|
|
// Whatever happened on the client, these should still be correct
|
|
DebugTools.Assert(component.ParentUid == newState.ParentID);
|
|
DebugTools.Assert(component.Anchored == newState.Anchored);
|
|
|
|
if (rebuildMatrices)
|
|
{
|
|
component.RebuildMatrices();
|
|
}
|
|
|
|
Dirty(component);
|
|
}
|
|
|
|
if (args.Next is TransformComponentState nextTransform)
|
|
{
|
|
component._nextPosition = nextTransform.LocalPosition;
|
|
component._nextRotation = nextTransform.Rotation;
|
|
component.LerpParent = nextTransform.ParentID;
|
|
ActivateLerp(component);
|
|
}
|
|
else
|
|
{
|
|
DeactivateLerp(component);
|
|
}
|
|
}
|
|
|
|
private void DeactivateLerp(TransformComponent component)
|
|
{
|
|
// this should cause the lerp to do nothing
|
|
component._nextPosition = null;
|
|
component._nextRotation = null;
|
|
component.LerpParent = EntityUid.Invalid;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region World Matrix
|
|
|
|
[Pure]
|
|
public Matrix3 GetWorldMatrix(EntityUid uid)
|
|
{
|
|
return Transform(uid).WorldMatrix;
|
|
}
|
|
|
|
// Temporary until it's moved here
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Matrix3 GetWorldMatrix(TransformComponent component)
|
|
{
|
|
return component.WorldMatrix;
|
|
}
|
|
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Matrix3 GetWorldMatrix(EntityUid uid, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
return GetWorldMatrix(xformQuery.GetComponent(uid));
|
|
}
|
|
|
|
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Matrix3 GetWorldMatrix(TransformComponent component, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
return component.WorldMatrix;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region World Position
|
|
|
|
[Pure]
|
|
public Vector2 GetWorldPosition(EntityUid uid)
|
|
{
|
|
return Transform(uid).WorldPosition;
|
|
}
|
|
|
|
// Temporary until it's moved here
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Vector2 GetWorldPosition(TransformComponent component)
|
|
{
|
|
return component.WorldPosition;
|
|
}
|
|
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Vector2 GetWorldPosition(EntityUid uid, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
return GetWorldPosition(xformQuery.GetComponent(uid));
|
|
}
|
|
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Vector2 GetWorldPosition(TransformComponent component, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
return component.WorldPosition;
|
|
}
|
|
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public (Vector2 WorldPosition, Angle WorldRotation) GetWorldPositionRotation(TransformComponent component, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
return component.GetWorldPositionRotation(xformQuery);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void SetWorldPosition(EntityUid uid, Vector2 worldPos)
|
|
{
|
|
var xform = Transform(uid);
|
|
SetWorldPosition(xform, worldPos);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void SetWorldPosition(EntityUid uid, Vector2 worldPos, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
var component = xformQuery.GetComponent(uid);
|
|
SetWorldPosition(component, worldPos, xformQuery);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void SetWorldPosition(TransformComponent component, Vector2 worldPos)
|
|
{
|
|
if (!component._parent.IsValid())
|
|
{
|
|
DebugTools.Assert("Parent is invalid while attempting to set WorldPosition - did you try to move root node?");
|
|
return;
|
|
}
|
|
|
|
// world coords to parent coords
|
|
var newPos = component.Parent!.InvWorldMatrix.Transform(worldPos);
|
|
SetLocalPosition(component, newPos);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void SetWorldPosition(TransformComponent component, Vector2 worldPos, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
if (!component._parent.IsValid())
|
|
{
|
|
DebugTools.Assert("Parent is invalid while attempting to set WorldPosition - did you try to move root node?");
|
|
return;
|
|
}
|
|
|
|
// world coords to parent coords
|
|
var newPos = GetInvWorldMatrix(component._parent, xformQuery).Transform(worldPos);
|
|
SetLocalPosition(component, newPos);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region World Rotation
|
|
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Angle GetWorldRotation(EntityUid uid)
|
|
{
|
|
return Transform(uid).WorldRotation;
|
|
}
|
|
|
|
// Temporary until it's moved here
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Angle GetWorldRotation(TransformComponent component)
|
|
{
|
|
return component.WorldRotation;
|
|
}
|
|
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Angle GetWorldRotation(EntityUid uid, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
return GetWorldRotation(xformQuery.GetComponent(uid));
|
|
}
|
|
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Angle GetWorldRotation(TransformComponent component, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
return component.WorldRotation;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void SetWorldRotation(EntityUid uid, Angle angle)
|
|
{
|
|
var component = Transform(uid);
|
|
SetWorldRotation(component, angle);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void SetWorldRotation(TransformComponent component, Angle angle)
|
|
{
|
|
var current = GetWorldRotation(component);
|
|
var diff = angle - current;
|
|
SetLocalRotation(component, component.LocalRotation + diff);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void SetWorldRotation(EntityUid uid, Angle angle, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
SetWorldRotation(xformQuery.GetComponent(uid), angle, xformQuery);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void SetWorldRotation(TransformComponent component, Angle angle, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
var current = GetWorldRotation(component, xformQuery);
|
|
var diff = angle - current;
|
|
SetLocalRotation(component, component.LocalRotation + diff);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Inverse World Matrix
|
|
|
|
[Pure]
|
|
public Matrix3 GetInvWorldMatrix(EntityUid uid)
|
|
{
|
|
return Comp<TransformComponent>(uid).InvWorldMatrix;
|
|
}
|
|
|
|
// Temporary until it's moved here
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Matrix3 GetInvWorldMatrix(TransformComponent component)
|
|
{
|
|
return component.InvWorldMatrix;
|
|
}
|
|
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Matrix3 GetInvWorldMatrix(EntityUid uid, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
return GetInvWorldMatrix(xformQuery.GetComponent(uid));
|
|
}
|
|
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Matrix3 GetInvWorldMatrix(TransformComponent component, EntityQuery<TransformComponent> xformQuery)
|
|
{
|
|
return component.InvWorldMatrix;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region State Handling
|
|
private void ChangeMapId(TransformComponent xform, MapId newMapId, EntityQuery<TransformComponent> xformQuery, EntityQuery<MetaDataComponent> metaQuery)
|
|
{
|
|
if (newMapId == xform.MapID)
|
|
return;
|
|
|
|
//Set Paused state
|
|
var mapPaused = _mapManager.IsMapPaused(newMapId);
|
|
var meta = metaQuery.GetComponent(xform.Owner);
|
|
_metaSys.SetEntityPaused(xform.Owner, mapPaused, meta);
|
|
|
|
xform.MapID = newMapId;
|
|
xform.UpdateChildMapIdsRecursive(xform.MapID, mapPaused, xformQuery, metaQuery, _metaSys);
|
|
}
|
|
|
|
public void DetachParentToNull(TransformComponent xform)
|
|
{
|
|
if (xform._parent.IsValid())
|
|
DetachParentToNull(xform, GetEntityQuery<TransformComponent>(), GetEntityQuery<MetaDataComponent>());
|
|
else
|
|
DebugTools.Assert(!xform.Anchored);
|
|
}
|
|
|
|
public void DetachParentToNull(TransformComponent xform, EntityQuery<TransformComponent> xformQuery, EntityQuery<MetaDataComponent> metaQuery, TransformComponent? oldConcrete = null)
|
|
{
|
|
var oldParent = xform._parent;
|
|
|
|
// Even though they may already be in nullspace we may want to deparent them anyway
|
|
if (!oldParent.IsValid())
|
|
{
|
|
DebugTools.Assert(!xform.Anchored);
|
|
return;
|
|
}
|
|
|
|
// Stop any active lerps
|
|
xform._nextPosition = null;
|
|
xform._nextRotation = null;
|
|
xform.LerpParent = EntityUid.Invalid;
|
|
|
|
if (xform.Anchored && metaQuery.TryGetComponent(xform.GridUid, out var meta) && meta.EntityLifeStage <= EntityLifeStage.MapInitialized)
|
|
{
|
|
var grid = Comp<IMapGridComponent>(xform.GridUid.Value);
|
|
var tileIndices = grid.Grid.TileIndicesFor(xform.Coordinates);
|
|
grid.Grid.RemoveFromSnapGridCell(tileIndices, xform.Owner);
|
|
|
|
// intentionally not updating physics body type to non-static, there is no need to add it to the current map.
|
|
|
|
xform._anchored = false;
|
|
var anchorStateChangedEvent = new AnchorStateChangedEvent(xform, true);
|
|
RaiseLocalEvent(xform.Owner, ref anchorStateChangedEvent, true);
|
|
}
|
|
|
|
oldConcrete ??= xformQuery.GetComponent(oldParent);
|
|
oldConcrete._children.Remove(xform.Owner);
|
|
|
|
xform._parent = EntityUid.Invalid;
|
|
var oldMap = xform.MapID;
|
|
|
|
// aaaaaaaaaaaaaaaa
|
|
ChangeMapId(xform, MapId.Nullspace, xformQuery, metaQuery);
|
|
|
|
if (xform.GridUid != null)
|
|
SetGridId(xform, null, xformQuery);
|
|
|
|
var entParentChangedMessage = new EntParentChangedMessage(xform.Owner, oldParent, oldMap, xform);
|
|
RaiseLocalEvent(xform.Owner, ref entParentChangedMessage, true);
|
|
Dirty(xform);
|
|
}
|
|
#endregion
|
|
}
|