Rework the way held items scatter when holder is knocked down (#36232)

* Redo drop held items math

* Don't assume the holder has a PhysicsComponent

* Assume infinite mass for held items with no PhysicsComponent

* Switch to EntityQuery for PhysicsComponent

* The micro-est of optimizations

* use NextAngle

* Might as well do that outside the loop
This commit is contained in:
Tayrtahn
2025-03-31 18:00:04 -04:00
committed by GitHub
parent d6dad24db8
commit d2ad6cdcaa

View File

@@ -39,6 +39,15 @@ namespace Content.Server.Hands.Systems
[Dependency] private readonly PullingSystem _pullingSystem = default!;
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
private EntityQuery<PhysicsComponent> _physicsQuery;
/// <summary>
/// Items dropped when the holder falls down will be launched in
/// a direction offset by up to this many degrees from the holder's
/// movement direction.
/// </summary>
private const float DropHeldItemsSpread = 45;
public override void Initialize()
{
base.Initialize();
@@ -60,6 +69,8 @@ namespace Content.Server.Hands.Systems
CommandBinds.Builder
.Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem))
.Register<HandsSystem>();
_physicsQuery = GetEntityQuery<PhysicsComponent>();
}
public override void Shutdown()
@@ -234,13 +245,13 @@ namespace Content.Server.Hands.Systems
private void OnDropHandItems(Entity<HandsComponent> entity, ref DropHandItemsEvent args)
{
var direction = EntityManager.TryGetComponent(entity, out PhysicsComponent? comp) ? comp.LinearVelocity / 50 : Vector2.Zero;
var dropAngle = _random.NextFloat(0.8f, 1.2f);
// If the holder doesn't have a physics component, they ain't moving
var holderVelocity = _physicsQuery.TryComp(entity, out var physics) ? physics.LinearVelocity : Vector2.Zero;
var spreadMaxAngle = Angle.FromDegrees(DropHeldItemsSpread);
var fellEvent = new FellDownEvent(entity);
RaiseLocalEvent(entity, fellEvent, false);
var worldRotation = TransformSystem.GetWorldRotation(entity).ToVec();
foreach (var hand in entity.Comp.Hands.Values)
{
if (hand.HeldEntity is not EntityUid held)
@@ -255,10 +266,26 @@ namespace Content.Server.Hands.Systems
if (!TryDrop(entity, hand, null, checkActionBlocker: false, handsComp: entity.Comp))
continue;
// Rotate the item's throw vector a bit for each item
var angleOffset = _random.NextAngle(-spreadMaxAngle, spreadMaxAngle);
// Rotate the holder's velocity vector by the angle offset to get the item's velocity vector
var itemVelocity = angleOffset.RotateVec(holderVelocity);
// Decrease the distance of the throw by a random amount
itemVelocity *= _random.NextFloat(1f);
// Heavier objects don't get thrown as far
// If the item doesn't have a physics component, it isn't going to get thrown anyway, but we'll assume infinite mass
itemVelocity *= _physicsQuery.TryComp(held, out var heldPhysics) ? heldPhysics.InvMass : 0;
// Throw at half the holder's intentional throw speed and
// vary the speed a little to make it look more interesting
var throwSpeed = entity.Comp.BaseThrowspeed * _random.NextFloat(0.45f, 0.55f);
_throwingSystem.TryThrow(held,
_random.NextAngle().RotateVec(direction / dropAngle + worldRotation / 50),
0.5f * dropAngle * _random.NextFloat(-0.9f, 1.1f),
entity, 0);
itemVelocity,
throwSpeed,
entity,
pushbackRatio: 0,
compensateFriction: false
);
}
}