]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Improve throwing precision (#29726)
authorslarticodefast <161409025+slarticodefast@users.noreply.github.com>
Mon, 8 Jul 2024 09:03:53 +0000 (11:03 +0200)
committerGitHub <noreply@github.com>
Mon, 8 Jul 2024 09:03:53 +0000 (19:03 +1000)
* improve throwing precision

* remove debugging logs

* minor fixes

* f

* Update Content.Shared/Throwing/LandAtCursorComponent.cs

---------

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
18 files changed:
Content.Client/Throwing/ThrownItemVisualizerSystem.cs
Content.Server/Hands/Systems/HandsSystem.cs
Content.Server/Lube/LubedSystem.cs
Content.Server/Singularity/EntitySystems/ContainmentFieldSystem.cs
Content.Shared/Hands/Components/HandsComponent.cs
Content.Shared/Throwing/BeforeThrowEvent.cs
Content.Shared/Throwing/LandAtCursorComponent.cs [new file with mode: 0644]
Content.Shared/Throwing/ThrowingSystem.cs
Content.Shared/Throwing/ThrownItemSystem.cs
Resources/Prototypes/Entities/Objects/Fun/darts.yml
Resources/Prototypes/Entities/Objects/Misc/pen.yml
Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml
Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/toy.yml
Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml
Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml
Resources/Prototypes/Entities/Objects/Weapons/Throwable/bola.yml
Resources/Prototypes/Entities/Objects/Weapons/Throwable/clusterbang.yml
Resources/Prototypes/Entities/Objects/Weapons/Throwable/throwing_stars.yml

index b25b4fbb7deffc3f87705056755f0ac43e384701..28a07ae94a701094c9f8cea86aca46d0b6fe54c0 100644 (file)
@@ -59,7 +59,6 @@ public sealed class ThrownItemVisualizerSystem : EntitySystem
         if (length <= TimeSpan.Zero)
             return null;
 
-        length += TimeSpan.FromSeconds(ThrowingSystem.FlyTime);
         var scale = ent.Comp2.Scale;
         var lenFloat = (float) length.TotalSeconds;
 
index feae130ce8e363ea73f2e510fd008387da825b2d..e2bb991318ff2414ee4e7bf19b80699ffb895ee4 100644 (file)
@@ -207,13 +207,13 @@ namespace Content.Server.Hands.Systems
 
             var length = direction.Length();
             var distance = Math.Clamp(length, minDistance, hands.ThrowRange);
-            direction *= distance/length;
+            direction *= distance / length;
 
-            var throwStrength = hands.ThrowForceMultiplier;
+            var throwSpeed = hands.BaseThrowspeed;
 
             // Let other systems change the thrown entity (useful for virtual items)
             // or the throw strength.
-            var ev = new BeforeThrowEvent(throwEnt, direction, throwStrength, player);
+            var ev = new BeforeThrowEvent(throwEnt, direction, throwSpeed, player);
             RaiseLocalEvent(player, ref ev);
 
             if (ev.Cancelled)
@@ -223,7 +223,7 @@ namespace Content.Server.Hands.Systems
             if (IsHolding(player, throwEnt, out _, hands) && !TryDrop(player, throwEnt, handsComp: hands))
                 return false;
 
-            _throwingSystem.TryThrow(ev.ItemUid, ev.Direction, ev.ThrowStrength, ev.PlayerUid);
+            _throwingSystem.TryThrow(ev.ItemUid, ev.Direction, ev.ThrowSpeed, ev.PlayerUid, compensateFriction: !HasComp<LandAtCursorComponent>(ev.ItemUid));
 
             return true;
         }
index c2d15c8a284063e955a32f535e56cc5fd4bbe688..3c536dcceb273c6a7a720e1cfb3dc934ce8111f4 100644 (file)
@@ -43,7 +43,7 @@ public sealed class LubedSystem : EntitySystem
         var user = args.Container.Owner;
         _transform.SetCoordinates(uid, Transform(user).Coordinates);
         _transform.AttachToGridOrMap(uid);
-        _throwing.TryThrow(uid, _random.NextVector2(), strength: component.SlipStrength);
+        _throwing.TryThrow(uid, _random.NextVector2(), baseThrowSpeed: component.SlipStrength);
         _popup.PopupEntity(Loc.GetString("lube-slip", ("target", Identity.Entity(uid, EntityManager))), user, user, PopupType.MediumCaution);
     }
 
index 3944e94c20d8f957b675ea0fa59aa3afd2735cf5..0af49545190101ee3e94f8800a6d9ac6c25427bf 100644 (file)
@@ -37,7 +37,7 @@ public sealed class ContainmentFieldSystem : EntitySystem
             var fieldDir = Transform(uid).WorldPosition;
             var playerDir = Transform(otherBody).WorldPosition;
 
-            _throwing.TryThrow(otherBody, playerDir-fieldDir, strength: component.ThrowForce);
+            _throwing.TryThrow(otherBody, playerDir-fieldDir, baseThrowSpeed: component.ThrowForce);
         }
     }
 
index 919d55f294aaee88b6149c33018727f44d5d51b6..84a389a39c5f53583d3284b2f9d745dd74df7419 100644 (file)
@@ -38,11 +38,11 @@ public sealed partial class HandsComponent : Component
     public bool DisableExplosionRecursion = false;
 
     /// <summary>
-    ///     The amount of throw impulse per distance the player is from the throw target.
+    ///     Modifies the speed at which items are thrown.
     /// </summary>
-    [DataField("throwForceMultiplier")]
+    [DataField]
     [ViewVariables(VVAccess.ReadWrite)]
-    public float ThrowForceMultiplier { get; set; } = 10f; //should be tuned so that a thrown item lands about under the player's cursor
+    public float BaseThrowspeed { get; set; } = 10f;
 
     /// <summary>
     ///     Distance after which longer throw targets stop increasing throw impulse.
index 36e7dd758b8fb88349e9d69bb03eac26252a215e..d2bf2f38b8e075e521d785bd9002ef863daf1939 100644 (file)
@@ -5,17 +5,17 @@ namespace Content.Shared.Throwing;
 [ByRefEvent]
 public struct BeforeThrowEvent
 {
-    public BeforeThrowEvent(EntityUid itemUid, Vector2 direction, float throwStrength,  EntityUid playerUid)
+    public BeforeThrowEvent(EntityUid itemUid, Vector2 direction, float throwSpeed,  EntityUid playerUid)
     {
         ItemUid = itemUid;
         Direction = direction;
-        ThrowStrength = throwStrength;
+        ThrowSpeed = throwSpeed;
         PlayerUid = playerUid;
     }
 
     public EntityUid ItemUid { get; set; }
     public Vector2 Direction { get; }
-    public float ThrowStrength { get; set;}
+    public float ThrowSpeed { get; set;}
     public EntityUid PlayerUid { get; }
 
     public bool Cancelled = false;
diff --git a/Content.Shared/Throwing/LandAtCursorComponent.cs b/Content.Shared/Throwing/LandAtCursorComponent.cs
new file mode 100644 (file)
index 0000000..f8e99e2
--- /dev/null
@@ -0,0 +1,12 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Throwing
+{
+    /// <summary>
+    ///     Makes an item land at the cursor when thrown and slide a little further.
+    ///     Without it the item lands slightly in front and stops moving at the cursor.
+    ///     Use this for throwing weapons that should pierce the opponent, for example spears.
+    /// </summary>
+    [RegisterComponent, NetworkedComponent]
+    public sealed partial class LandAtCursorComponent : Component { }
+}
index 7d94ada924d0838ed1cd7fa79c5d617cf4092247..56bbf4c2bf3dd27f9d7eaf32d7ce27fd253b1f4a 100644 (file)
@@ -1,13 +1,12 @@
 using System.Numerics;
 using Content.Shared.Administration.Logs;
 using Content.Shared.Camera;
+using Content.Shared.CCVar;
 using Content.Shared.Database;
+using Content.Shared.Friction;
 using Content.Shared.Gravity;
-using Content.Shared.Hands.Components;
-using Content.Shared.Hands.EntitySystems;
-using Content.Shared.Interaction;
 using Content.Shared.Projectiles;
-using Content.Shared.Tag;
+using Robust.Shared.Configuration;
 using Robust.Shared.Map;
 using Robust.Shared.Physics;
 using Robust.Shared.Physics.Components;
@@ -31,7 +30,10 @@ public sealed class ThrowingSystem : EntitySystem
     /// The minimum amount of time an entity needs to be thrown before the timer can be run.
     /// Anything below this threshold never enters the air.
     /// </summary>
-    public const float FlyTime = 0.15f;
+    public const float MinFlyTime = 0.15f;
+    public const float FlyTimePercentage = 0.8f;
+
+    private float _frictionModifier;
 
     [Dependency] private readonly IGameTiming _gameTiming = default!;
     [Dependency] private readonly SharedGravitySystem _gravity = default!;
@@ -40,13 +42,23 @@ public sealed class ThrowingSystem : EntitySystem
     [Dependency] private readonly ThrownItemSystem _thrownSystem = default!;
     [Dependency] private readonly SharedCameraRecoilSystem _recoil = default!;
     [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
+    [Dependency] private readonly IConfigurationManager _configManager = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        Subs.CVar(_configManager, CCVars.TileFrictionModifier, value => _frictionModifier = value, true);
+    }
 
     public void TryThrow(
         EntityUid uid,
         EntityCoordinates coordinates,
-        float strength = 1.0f,
+        float baseThrowSpeed = 10.0f,
         EntityUid? user = null,
         float pushbackRatio = PushbackDefault,
+        float? friction = null,
+        bool compensateFriction = false,
         bool recoil = true,
         bool animated = true,
         bool playSound = true,
@@ -58,7 +70,7 @@ public sealed class ThrowingSystem : EntitySystem
         if (mapPos.MapId != thrownPos.MapId)
             return;
 
-        TryThrow(uid, mapPos.Position - thrownPos.Position, strength, user, pushbackRatio, recoil: recoil, animated: animated, playSound: playSound, doSpin: doSpin);
+        TryThrow(uid, mapPos.Position - thrownPos.Position, baseThrowSpeed, user, pushbackRatio, friction, compensateFriction: compensateFriction, recoil: recoil, animated: animated, playSound: playSound, doSpin: doSpin);
     }
 
     /// <summary>
@@ -66,14 +78,18 @@ public sealed class ThrowingSystem : EntitySystem
     /// </summary>
     /// <param name="uid">The entity being thrown.</param>
     /// <param name="direction">A vector pointing from the entity to its destination.</param>
-    /// <param name="strength">How much the direction vector should be multiplied for velocity.</param>
+    /// <param name="baseThrowSpeed">Throw velocity. Gets modified if compensateFriction is true.</param>
     /// <param name="pushbackRatio">The ratio of impulse applied to the thrower - defaults to 10 because otherwise it's not enough to properly recover from getting spaced</param>
+    /// <param name="friction">friction value used for the distance calculation. If set to null this defaults to the standard tile values</param>
+    /// <param name="compensateFriction">True will adjust the throw so the item stops at the target coordinates. False means it will land at the target and keep sliding.</param>
     /// <param name="doSpin">Whether spin will be applied to the thrown entity.</param>
     public void TryThrow(EntityUid uid,
         Vector2 direction,
-        float strength = 1.0f,
+        float baseThrowSpeed = 10.0f,
         EntityUid? user = null,
         float pushbackRatio = PushbackDefault,
+        float? friction = null,
+        bool compensateFriction = false,
         bool recoil = true,
         bool animated = true,
         bool playSound = true,
@@ -91,9 +107,10 @@ public sealed class ThrowingSystem : EntitySystem
             physics,
             Transform(uid),
             projectileQuery,
-            strength,
+            baseThrowSpeed,
             user,
-            pushbackRatio, recoil: recoil, animated: animated, playSound: playSound, doSpin: doSpin);
+            pushbackRatio,
+            friction, compensateFriction: compensateFriction, recoil: recoil, animated: animated, playSound: playSound, doSpin: doSpin);
     }
 
     /// <summary>
@@ -101,23 +118,27 @@ public sealed class ThrowingSystem : EntitySystem
     /// </summary>
     /// <param name="uid">The entity being thrown.</param>
     /// <param name="direction">A vector pointing from the entity to its destination.</param>
-    /// <param name="strength">How much the direction vector should be multiplied for velocity.</param>
+    /// <param name="baseThrowSpeed">Throw velocity. Gets modified if compensateFriction is true.</param>
     /// <param name="pushbackRatio">The ratio of impulse applied to the thrower - defaults to 10 because otherwise it's not enough to properly recover from getting spaced</param>
+    /// <param name="friction">friction value used for the distance calculation. If set to null this defaults to the standard tile values</param>
+    /// <param name="compensateFriction">True will adjust the throw so the item stops at the target coordinates. False means it will land at the target and keep sliding.</param>
     /// <param name="doSpin">Whether spin will be applied to the thrown entity.</param>
     public void TryThrow(EntityUid uid,
         Vector2 direction,
         PhysicsComponent physics,
         TransformComponent transform,
         EntityQuery<ProjectileComponent> projectileQuery,
-        float strength = 1.0f,
+        float baseThrowSpeed = 10.0f,
         EntityUid? user = null,
         float pushbackRatio = PushbackDefault,
+        float? friction = null,
+        bool compensateFriction = false,
         bool recoil = true,
         bool animated = true,
         bool playSound = true,
         bool doSpin = true)
     {
-        if (strength <= 0 || direction == Vector2Helpers.Infinity || direction == Vector2Helpers.NaN || direction == Vector2.Zero)
+        if (baseThrowSpeed <= 0 || direction == Vector2Helpers.Infinity || direction == Vector2Helpers.NaN || direction == Vector2.Zero || friction < 0)
             return;
 
         if ((physics.BodyType & (BodyType.Dynamic | BodyType.KinematicController)) == 0x0)
@@ -136,16 +157,22 @@ public sealed class ThrowingSystem : EntitySystem
             Animate = animated,
         };
 
-        // Estimate time to arrival so we can apply OnGround status and slow it much faster.
-        var time = direction.Length() / strength;
+        // if not given, get the default friction value for distance calculation
+        var tileFriction = friction ?? _frictionModifier * TileFrictionController.DefaultFriction;
+
+        if (tileFriction == 0f)
+            compensateFriction = false; // cannot calculate this if there is no friction
+
+        // Set the time the item is supposed to be in the air so we can apply OnGround status.
+        // This is a free parameter, but we should set it to something reasonable.
+        var flyTime = direction.Length() / baseThrowSpeed;
+        if (compensateFriction)
+            flyTime *= FlyTimePercentage;
+
+        if (flyTime < MinFlyTime)
+            flyTime = 0f;
         comp.ThrownTime = _gameTiming.CurTime;
-        // TODO: This is a bandaid, don't do this.
-        // if you want to force landtime have the caller handle it or add a new method.
-        // did we launch this with something stronger than our hands?
-        if (TryComp<HandsComponent>(comp.Thrower, out var hands) && strength > hands.ThrowForceMultiplier)
-            comp.LandTime = comp.ThrownTime + TimeSpan.FromSeconds(time);
-        else
-            comp.LandTime = time < FlyTime ? default : comp.ThrownTime + TimeSpan.FromSeconds(time - FlyTime);
+        comp.LandTime = comp.ThrownTime + TimeSpan.FromSeconds(flyTime);
         comp.PlayLandSound = playSound;
         AddComp(uid, comp, true);
 
@@ -173,7 +200,12 @@ public sealed class ThrowingSystem : EntitySystem
         if (user != null)
             _adminLogger.Add(LogType.Throw, LogImpact.Low, $"{ToPrettyString(user.Value):user} threw {ToPrettyString(uid):entity}");
 
-        var impulseVector = direction.Normalized() * strength * physics.Mass;
+        // if compensateFriction==true compensate for the distance the item will slide over the floor after landing by reducing the throw speed accordingly.
+        // else let the item land on the cursor and from where it slides a little further.
+        // This is an exact formula we get from exponentially decaying velocity after landing.
+        // If someone changes how tile friction works at some point, this will have to be adjusted.
+        var throwSpeed = compensateFriction ? direction.Length() / (flyTime + 1 / tileFriction) : baseThrowSpeed;
+        var impulseVector = direction.Normalized() * throwSpeed * physics.Mass;
         _physics.ApplyLinearImpulse(uid, impulseVector, body: physics);
 
         if (comp.LandTime == null || comp.LandTime <= TimeSpan.Zero)
index 770273fa0327ac6e8969bfb8b9ef0af381131876..54dc5c3490e82d97eee6ac428d9a456bc2167278 100644 (file)
@@ -153,7 +153,7 @@ namespace Content.Shared.Throwing
                     LandComponent(uid, thrown, physics, thrown.PlayLandSound);
                 }
 
-                var stopThrowTime = (thrown.LandTime ?? thrown.ThrownTime) + TimeSpan.FromSeconds(ThrowingSystem.FlyTime);
+                var stopThrowTime = thrown.LandTime ?? thrown.ThrownTime;
                 if (stopThrowTime <= _gameTiming.CurTime)
                 {
                     StopThrow(uid, thrown);
index c0b5eb33999d9b9b4b40ed151af578242d2ef056..dd25d503ce23b54d1ec0e9c413ecadcac60a0747 100644 (file)
@@ -10,6 +10,7 @@
     offset: 0.0,0.0
   - type: ThrowingAngle
     angle: 315
+  - type: LandAtCursor
   - type: Fixtures
     fixtures:
       fix1:
index 635df230a4a3856fdd5836a3b5ca512103131fbb..187672ec4dd72533545273fb7f00cf1b92f2ac12 100644 (file)
@@ -29,6 +29,7 @@
     removalTime: 0.0
   - type: ThrowingAngle
     angle: 315
+  - type: LandAtCursor
   - type: DamageOtherOnHit
     damage:
       types:
index f4cc09430a20a3df759a079c5eeef72f802804da..2a6f314904a0e976ab98ff4d2df3adf4a36ac7b9 100644 (file)
     embedOnThrow: True
   - type: ThrowingAngle
     angle: 0
+  - type: LandAtCursor
   - type: Ammo
     muzzleFlash: null
   - type: Projectile
index 9b6c288e3786644a1b6436b39a99e7c7250d59af..14595bd34a2e1719e84327dad786dcf756007fe8 100644 (file)
@@ -31,3 +31,4 @@
     removalTime: .2
   - type: ThrowingAngle
     angle: 180
+  - type: LandAtCursor
index afe4644517c8ee07212e42e4f018423589558c82..f862a0f10aab31d25cb350e4b4eb100c3d767413 100644 (file)
@@ -92,6 +92,7 @@
         Slash: 12
   - type: EmbeddableProjectile
     sound: /Audio/Weapons/star_hit.ogg
+  - type: LandAtCursor
   - type: DamageOtherOnHit
     damage:
       types:
     damage:
       types:
         Slash: 10
+  - type: LandAtCursor
   - type: Sprite
     sprite: Clothing/Head/Hats/greyflatcap.rsi
   - type: Clothing
         Slash: 5
   - type: EmbeddableProjectile
     sound: /Audio/Weapons/star_hit.ogg
+  - type: LandAtCursor
   - type: DamageOtherOnHit
     ignoreResistances: true
     damage:
index 0def916ddc73cbd0aeec10a84af66fbf0ca29298..ea2e73ac151750f3a0cf6bd255dedc8ccc4fe9bd 100644 (file)
@@ -8,6 +8,7 @@
     offset: 0.15,0.15
   - type: ThrowingAngle
     angle: 225
+  - type: LandAtCursor
   - type: Tag
     tags:
     - Spear
index ca3edf68c0df4f8ac7c4b3e7c1fb20a19309a787..fea9a8d5ea3b3dc619d545ee112d3a0280dd61da 100644 (file)
@@ -47,4 +47,4 @@
     staminaDamage: 55 # Sudden weight increase sapping stamina
     canThrowTrigger: true
     canMoveBreakout: true
-
+  - type: LandAtCursor
index 35174ba34d26dffe6764f175f0d9ccf845a31181..cc55aab237c0547c00aeef72b9062451dd60a66a 100644 (file)
@@ -96,6 +96,7 @@
     damage:
       types:
         Blunt: 10
+  - type: LandAtCursor
   - type: Damageable
     damageContainer: Inorganic
   - type: EmitSoundOnTrigger
     damage:
       types:
         Blunt: 10
+  - type: LandAtCursor
   - type: EmitSoundOnTrigger
     sound:
       path: "/Audio/Effects/flash_bang.ogg"
index c68feff0b5c86f744881ddf96fd0eb37f979ebb3..0dfc9a0312fdf05d917fe64f604adb1ddb82510c 100644 (file)
@@ -31,6 +31,7 @@
         friction: 0.2
   - type: EmbeddableProjectile
     sound: /Audio/Weapons/star_hit.ogg
+  - type: LandAtCursor
   - type: DamageOtherOnHit
     damage:
       types: