From d2ad6cdcaa21b1d5000d8a43f42f612b03ebc469 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 31 Mar 2025 18:00:04 -0400 Subject: [PATCH] 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 --- Content.Server/Hands/Systems/HandsSystem.cs | 39 +++++++++++++++++---- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs index 41f582cde8..1e8e012c52 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -39,6 +39,15 @@ namespace Content.Server.Hands.Systems [Dependency] private readonly PullingSystem _pullingSystem = default!; [Dependency] private readonly ThrowingSystem _throwingSystem = default!; + private EntityQuery _physicsQuery; + + /// + /// 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. + /// + 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(); + + _physicsQuery = GetEntityQuery(); } public override void Shutdown() @@ -234,13 +245,13 @@ namespace Content.Server.Hands.Systems private void OnDropHandItems(Entity 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 + ); } } -- 2.51.2