]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Throwing item scaling animation + recoil (#24724)
authorKara <lunarautomaton6@gmail.com>
Tue, 30 Jan 2024 10:50:41 +0000 (03:50 -0700)
committerGitHub <noreply@github.com>
Tue, 30 Jan 2024 10:50:41 +0000 (21:50 +1100)
Content.Client/Throwing/ThrownItemVisualizerSystem.cs [new file with mode: 0644]
Content.Shared/Throwing/ThrowingSystem.cs
Content.Shared/Throwing/ThrownItemComponent.cs
Content.Shared/Throwing/ThrownItemSystem.cs

diff --git a/Content.Client/Throwing/ThrownItemVisualizerSystem.cs b/Content.Client/Throwing/ThrownItemVisualizerSystem.cs
new file mode 100644 (file)
index 0000000..bbd3673
--- /dev/null
@@ -0,0 +1,87 @@
+using Content.Shared.Throwing;
+using Robust.Client.Animations;
+using Robust.Client.GameObjects;
+using Robust.Shared.Animations;
+
+namespace Content.Client.Throwing;
+
+/// <summary>
+///     Handles animating thrown items.
+/// </summary>
+public sealed class ThrownItemVisualizerSystem : EntitySystem
+{
+    [Dependency] private readonly AnimationPlayerSystem _anim = default!;
+
+    private const string AnimationKey = "thrown-item";
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<ThrownItemComponent, AfterAutoHandleStateEvent>(OnAutoHandleState);
+        SubscribeLocalEvent<ThrownItemComponent, ComponentShutdown>(OnShutdown);
+    }
+
+    private void OnAutoHandleState(EntityUid uid, ThrownItemComponent component, ref AfterAutoHandleStateEvent args)
+    {
+        if (!TryComp<SpriteComponent>(uid, out var sprite))
+            return;
+
+        var animationPlayer = EnsureComp<AnimationPlayerComponent>(uid);
+
+        if (_anim.HasRunningAnimation(uid, animationPlayer, AnimationKey))
+            return;
+
+        var anim = GetAnimation((uid, component, sprite));
+        if (anim == null)
+            return;
+
+        component.OriginalScale = sprite.Scale;
+        _anim.Play((uid, animationPlayer), anim, AnimationKey);
+    }
+
+    private void OnShutdown(EntityUid uid, ThrownItemComponent component, ComponentShutdown args)
+    {
+        if (!_anim.HasRunningAnimation(uid, AnimationKey))
+            return;
+
+        if (TryComp<SpriteComponent>(uid, out var sprite) && component.OriginalScale != null)
+            sprite.Scale = component.OriginalScale.Value;
+
+        _anim.Stop(uid, AnimationKey);
+    }
+
+    private static Animation? GetAnimation(Entity<ThrownItemComponent, SpriteComponent> ent)
+    {
+        if (ent.Comp1.LandTime - ent.Comp1.ThrownTime is not { } length)
+            return null;
+
+        if (length <= TimeSpan.Zero)
+            return null;
+
+        length += TimeSpan.FromSeconds(ThrowingSystem.FlyTime);
+        var scale = ent.Comp2.Scale;
+        var lenFloat = (float) length.TotalSeconds;
+
+        // TODO use like actual easings here
+        return new Animation
+        {
+            Length = length,
+            AnimationTracks =
+            {
+                new AnimationTrackComponentProperty
+                {
+                    ComponentType = typeof(SpriteComponent),
+                    Property = nameof(SpriteComponent.Scale),
+                    KeyFrames =
+                    {
+                        new AnimationTrackProperty.KeyFrame(scale, 0.0f),
+                        new AnimationTrackProperty.KeyFrame(scale * 1.4f, lenFloat * 0.25f),
+                        new AnimationTrackProperty.KeyFrame(scale, lenFloat * 0.75f)
+                    },
+                    InterpolationMode = AnimationInterpolationMode.Linear
+                }
+            }
+        };
+    }
+}
index e6315464119c59aa1d40a4f2004de9d018d50f48..5fe02a057172fded6cdd648037b62f4a824725fd 100644 (file)
@@ -1,5 +1,6 @@
 using System.Numerics;
 using Content.Shared.Administration.Logs;
+using Content.Shared.Camera;
 using Content.Shared.Database;
 using Content.Shared.Gravity;
 using Content.Shared.Hands.Components;
@@ -32,6 +33,7 @@ public sealed class ThrowingSystem : EntitySystem
     [Dependency] private readonly SharedPhysicsSystem _physics = default!;
     [Dependency] private readonly SharedTransformSystem _transform = default!;
     [Dependency] private readonly ThrownItemSystem _thrownSystem = default!;
+    [Dependency] private readonly SharedCameraRecoilSystem _recoil = default!;
     [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
 
     public void TryThrow(
@@ -114,7 +116,7 @@ public sealed class ThrowingSystem : EntitySystem
         if (projectileQuery.TryGetComponent(uid, out var proj) && !proj.OnlyCollideWhenShot)
             return;
 
-        var comp = EnsureComp<ThrownItemComponent>(uid);
+        var comp = new ThrownItemComponent();
         comp.Thrower = user;
 
         // Estimate time to arrival so we can apply OnGround status and slow it much faster.
@@ -126,6 +128,7 @@ public sealed class ThrowingSystem : EntitySystem
         else
             comp.LandTime = time < FlyTime ? default : comp.ThrownTime + TimeSpan.FromSeconds(time - FlyTime);
         comp.PlayLandSound = playSound;
+        AddComp(uid, comp, true);
 
         ThrowingAngleComponent? throwingAngle = null;
 
@@ -160,9 +163,13 @@ public sealed class ThrowingSystem : EntitySystem
             _physics.SetBodyStatus(physics, BodyStatus.InAir);
         }
 
+        if (user == null)
+            return;
+
+        _recoil.KickCamera(user.Value, -direction * 0.3f);
+
         // Give thrower an impulse in the other direction
-        if (user != null &&
-            pushbackRatio != 0.0f &&
+        if (pushbackRatio != 0.0f &&
             physics.Mass > 0f &&
             TryComp(user.Value, out PhysicsComponent? userPhysics) &&
             _gravity.IsWeightless(user.Value, userPhysics))
index c6c9c4a44671fbda55dc78e341f71f2a2b4b1794..ab80e079383620ae9f6ccb044f288e02f7c0ad21 100644 (file)
@@ -1,54 +1,47 @@
+using System.Numerics;
 using Robust.Shared.GameStates;
 using Robust.Shared.Serialization;
 using Robust.Shared.Timing;
 
 namespace Content.Shared.Throwing
 {
-    [RegisterComponent, NetworkedComponent]
+    [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
     public sealed partial class ThrownItemComponent : Component
     {
         /// <summary>
         ///     The entity that threw this entity.
         /// </summary>
-        [DataField, ViewVariables(VVAccess.ReadWrite)]
+        [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
         public EntityUid? Thrower;
 
         /// <summary>
         ///     The <see cref="IGameTiming.CurTime"/> timestamp at which this entity was thrown.
         /// </summary>
-        [DataField, ViewVariables(VVAccess.ReadWrite)]
+        [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
         public TimeSpan? ThrownTime;
 
         /// <summary>
         ///     Compared to <see cref="IGameTiming.CurTime"/> to land this entity, if any.
         /// </summary>
-        [DataField, ViewVariables(VVAccess.ReadWrite)]
+        [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
         public TimeSpan? LandTime;
 
         /// <summary>
         ///     Whether or not this entity was already landed.
         /// </summary>
-        [DataField, ViewVariables(VVAccess.ReadWrite)]
+        [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
         public bool Landed;
 
         /// <summary>
         ///     Whether or not to play a sound when the entity lands.
         /// </summary>
-        [DataField, ViewVariables(VVAccess.ReadWrite)]
+        [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
         public bool PlayLandSound;
-    }
-
-    [Serializable, NetSerializable]
-    public sealed class ThrownItemComponentState : ComponentState
-    {
-        public NetEntity? Thrower;
-
-        public TimeSpan? ThrownTime;
-
-        public TimeSpan? LandTime;
-
-        public bool Landed;
 
-        public bool PlayLandSound;
+        /// <summary>
+        ///     Used to restore state after the throwing scale animation is finished.
+        /// </summary>
+        [DataField]
+        public Vector2? OriginalScale = null;
     }
 }
index 9b7ba3ebb05abad7f264c51b80768c205938cb8b..8d84cf36fa2481d0bdcc2846f17011353230ae49 100644 (file)
@@ -36,41 +36,10 @@ namespace Content.Shared.Throwing
             SubscribeLocalEvent<ThrownItemComponent, PreventCollideEvent>(PreventCollision);
             SubscribeLocalEvent<ThrownItemComponent, ThrownEvent>(ThrowItem);
             SubscribeLocalEvent<ThrownItemComponent, EntityUnpausedEvent>(OnThrownUnpaused);
-            SubscribeLocalEvent<ThrownItemComponent, ComponentGetState>(OnThrownGetState);
-            SubscribeLocalEvent<ThrownItemComponent, ComponentHandleState>(OnThrownHandleState);
 
             SubscribeLocalEvent<PullStartedMessage>(HandlePullStarted);
         }
 
-        private void OnThrownGetState(EntityUid uid, ThrownItemComponent component, ref ComponentGetState args)
-        {
-            // TODO: Throwing needs to handle this properly I just want the bad asserts to stop getting in my way.
-            TryGetNetEntity(component.Thrower, out var nent);
-
-            args.State = new ThrownItemComponentState()
-            {
-                ThrownTime = component.ThrownTime,
-                LandTime = component.LandTime,
-                Thrower = nent,
-                Landed = component.Landed,
-                PlayLandSound = component.PlayLandSound,
-            };
-        }
-
-        private void OnThrownHandleState(EntityUid uid, ThrownItemComponent component, ref ComponentHandleState args)
-        {
-            if (args.Current is not ThrownItemComponentState state)
-                return;
-
-            TryGetEntity(state.Thrower, out var thrower);
-
-            component.ThrownTime = state.ThrownTime;
-            component.LandTime = state.LandTime;
-            component.Thrower = thrower;
-            component.Landed = state.Landed;
-            component.PlayLandSound = state.PlayLandSound;
-        }
-
         private void OnMapInit(EntityUid uid, ThrownItemComponent component, MapInitEvent args)
         {
             component.ThrownTime ??= _gameTiming.CurTime;