mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-17 05:27:38 +03:00
Throwing item scaling animation + recoil (#24724)
(cherry picked from commit ef55039560814a98517552bd58493fc89a2a4812)
This commit is contained in:
87
Content.Client/Throwing/ThrownItemVisualizerSystem.cs
Normal file
87
Content.Client/Throwing/ThrownItemVisualizerSystem.cs
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user