using Robust.Shared.Log;
using Robust.Shared.Maths;
namespace Robust.Shared.GameObjects;
public abstract partial class SharedPhysicsSystem
{
///
/// This is the total rate of change of the entity's map-position, resulting from the linear and angular
/// velocities of this entity and any parents.
///
///
/// Use if you need linear and angular at the same time.
///
public Vector2 GetMapLinearVelocity(
EntityUid uid,
PhysicsComponent? component = null,
TransformComponent? xform = null,
EntityQuery? xformQuery = null,
EntityQuery? physicsQuery = null)
{
if (!Resolve(uid, ref component))
return Vector2.Zero;
xformQuery ??= EntityManager.GetEntityQuery();
physicsQuery ??= EntityManager.GetEntityQuery();
xform ??= xformQuery.Value.GetComponent(uid);
var parent = xform.ParentUid;
var localPos = xform.LocalPosition;
var velocity = component.LinearVelocity;
Vector2 angularComponent = Vector2.Zero;
while (parent.IsValid())
{
var parentXform = xformQuery.Value.GetComponent(parent);
if (physicsQuery.Value.TryGetComponent(parent, out var body))
{
// add linear velocity of parent relative to it's own parent (again, in map coordinates)
velocity += body.LinearVelocity;
// add angular velocity that results from the parent's rotation (NOT in map coordinates, but in the parentXform.Parent's frame)
angularComponent += Vector2.Cross(body.AngularVelocity, localPos - body.LocalCenter);
angularComponent = parentXform.LocalRotation.RotateVec(angularComponent);
}
localPos = parentXform.LocalPosition + parentXform.LocalRotation.RotateVec(localPos);
parent = parentXform.ParentUid;
}
// angular component of the velocity should now be in terms of map coordinates and can be added onto the sum of
// linear velocities.
return velocity + angularComponent;
}
///
/// Get the body's total angular velocity. This is the rate of change of the entity's world rotation.
///
///
/// Consider using if you need linear and angular at the same time.
///
public float GetMapAngularVelocity(
EntityUid uid,
PhysicsComponent? component = null,
TransformComponent? xform = null,
EntityQuery? xformQuery = null,
EntityQuery? physicsQuery = null)
{
if (!Resolve(uid, ref component))
return 0;
xformQuery ??= EntityManager.GetEntityQuery();
physicsQuery ??= EntityManager.GetEntityQuery();
xform ??= xformQuery.Value.GetComponent(uid);
var parent = xform.ParentUid;
var angularVelocity = component.AngularVelocity;
while (parent.IsValid())
{
var parentXform = xformQuery.Value.GetComponent(parent);
if (physicsQuery.Value.TryGetComponent(parent, out var body))
angularVelocity += body.AngularVelocity;
parent = parentXform.ParentUid;
}
return angularVelocity;
}
public (Vector2, float) GetMapVelocities(
EntityUid uid,
PhysicsComponent? component = null,
TransformComponent? xform = null,
EntityQuery? xformQuery = null,
EntityQuery? physicsQuery = null)
{
if (!Resolve(uid, ref component))
return (Vector2.Zero, 0);
xformQuery ??= EntityManager.GetEntityQuery();
physicsQuery ??= EntityManager.GetEntityQuery();
xform ??= xformQuery.Value.GetComponent(uid);
var parent = xform.ParentUid;
var localPos = xform.LocalPosition;
var linearVelocity = component.LinearVelocity;
var angularVelocity = component.AngularVelocity;
Vector2 linearVelocityAngularContribution = Vector2.Zero;
while (parent.IsValid())
{
var parentXform = xformQuery.Value.GetComponent(parent);
if (physicsQuery.Value.TryGetComponent(parent, out var body))
{
angularVelocity += body.AngularVelocity;
// add linear velocity of parent relative to it's own parent (again, in map coordinates)
linearVelocity += body.LinearVelocity;
// add the component of the linear velocity that results from the parent's rotation. This is NOT in map
// coordinates, this is the velocity in the parentXform.Parent's frame.
linearVelocityAngularContribution += Vector2.Cross(body.AngularVelocity, localPos - body.LocalCenter);
linearVelocityAngularContribution = parentXform.LocalRotation.RotateVec(linearVelocityAngularContribution);
}
localPos = parentXform.LocalPosition + parentXform.LocalRotation.RotateVec(localPos);
parent = parentXform.ParentUid;
}
return (linearVelocity + linearVelocityAngularContribution, angularVelocity);
}
private void HandleParentChangeVelocity(EntityUid uid, PhysicsComponent physics, ref EntParentChangedMessage args, TransformComponent xform)
{
if (physics.LifeStage != ComponentLifeStage.Running)
return;
// When transferring bodies, we will preserve map angular and linear velocities. For this purpose, we simply
// modify the velocities so that the map value remains unchanged.
// TODO currently this causes issues when the parent changes due to teleportation or something like that. Though
// I guess the question becomes, what do you do with conservation of momentum in that case. I guess its the job
// of the teleporter to select a velocity at the after the parent has changed.
var xformQuery = EntityManager.GetEntityQuery();
var physicsQuery = EntityManager.GetEntityQuery();
// for the new velocities (that need to be updated), we can just use the existing function:
var (newLinear, newAngular) = GetMapVelocities(uid, physics, xform, xformQuery, physicsQuery);
// for the old velocities, we need to re-implement this function while using the old parent and old local position:
if (args.OldParent is not EntityUid { Valid: true} parent)
{
// no previous parent --> simple
physics.LinearVelocity += physics.LinearVelocity - newLinear;
physics.AngularVelocity += physics.AngularVelocity - newAngular;
return;
}
TransformComponent? parentXform = xformQuery.GetComponent(parent);
var localPos = parentXform.InvWorldMatrix.Transform(xform.WorldPosition);
var oldLinear = physics.LinearVelocity;
var oldAngular = physics.AngularVelocity;
Vector2 linearAngularContribution = Vector2.Zero;
do
{
if (physicsQuery.TryGetComponent(parent, out var body))
{
oldAngular += body.AngularVelocity;
// add linear velocity of parent relative to it's own parent (again, in map coordinates)
oldLinear += body.LinearVelocity;
// add the component of the linear velocity that results from the parent's rotation. This is NOT in map
// coordinates, this is the velocity in the parentXform.Parent's frame.
linearAngularContribution += Vector2.Cross(body.AngularVelocity, localPos - body.LocalCenter);
linearAngularContribution = parentXform.LocalRotation.RotateVec(linearAngularContribution);
}
localPos = parentXform.LocalPosition + parentXform.LocalRotation.RotateVec(localPos);
parent = parentXform.ParentUid;
} while (parent.IsValid() && xformQuery.TryGetComponent(parent, out parentXform));
oldLinear += linearAngularContribution;
// Finally we can update the Velocities. linear velocity is already in terms of map-coordinates, so no
// world-rotation is required
physics.LinearVelocity += oldLinear - newLinear;
physics.AngularVelocity += oldAngular - newAngular;
}
}