Makes mobs visually float when weightless (#13391)

This commit is contained in:
AJCM-git
2023-01-17 18:01:53 -04:00
committed by GitHub
parent 21db0ab35b
commit 67ed59a50d
8 changed files with 217 additions and 46 deletions

View File

@@ -0,0 +1,61 @@
using Content.Shared.Gravity;
using Robust.Client.GameObjects;
using Robust.Client.Animations;
using Robust.Shared.Animations;
namespace Content.Client.Gravity;
/// <inheritdoc/>
public sealed class FloatingVisualizerSystem : SharedFloatingVisualizerSystem
{
[Dependency] private readonly AnimationPlayerSystem AnimationSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FloatingVisualsComponent, AnimationCompletedEvent>(OnAnimationCompleted);
}
/// <inheritdoc/>
public override void FloatAnimation(EntityUid uid, Vector2 offset, string animationKey, float animationTime, bool stop = false)
{
if (stop)
{
AnimationSystem.Stop(uid, animationKey);
return;
}
var animation = new Animation
{
// We multiply by the number of extra keyframes to make time for them
Length = TimeSpan.FromSeconds(animationTime*2),
AnimationTracks =
{
new AnimationTrackComponentProperty
{
ComponentType = typeof(SpriteComponent),
Property = nameof(SpriteComponent.Offset),
InterpolationMode = AnimationInterpolationMode.Linear,
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(Vector2.Zero, 0f),
new AnimationTrackProperty.KeyFrame(offset, animationTime),
new AnimationTrackProperty.KeyFrame(Vector2.Zero, animationTime),
}
}
}
};
if (!AnimationSystem.HasRunningAnimation(uid, animationKey))
AnimationSystem.Play(uid, animation, animationKey);
}
private void OnAnimationCompleted(EntityUid uid, FloatingVisualsComponent component, AnimationCompletedEvent args)
{
if (args.Key != component.AnimationKey)
return;
FloatAnimation(uid, component.Offset, component.AnimationKey, component.AnimationTime, !component.CanFloat);
}
}

View File

@@ -1,7 +1,22 @@
using Content.Shared.Pointing.Components; using Content.Shared.Pointing.Components;
namespace Content.Client.Pointing.Components namespace Content.Client.Pointing.Components;
{
[RegisterComponent] [RegisterComponent]
public sealed class PointingArrowComponent : SharedPointingArrowComponent {} public sealed class PointingArrowComponent : SharedPointingArrowComponent
{
/// <summary>
/// How long it takes to go from the bottom of the animation to the top.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("animationTime")]
public readonly float AnimationTime = 0.5f;
/// <summary>
/// How far it goes in any direction.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("offset")]
public readonly Vector2 Offset = (0, 0.25f);
public readonly string AnimationKey = "pointingarrow";
} }

View File

@@ -1,53 +1,17 @@
using Content.Client.Pointing.Components; using Content.Client.Pointing.Components;
using Content.Client.Gravity;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.Pointing; using Content.Shared.Pointing;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Client.Animations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.Animations;
using DrawDepth = Content.Shared.DrawDepth.DrawDepth; using DrawDepth = Content.Shared.DrawDepth.DrawDepth;
namespace Content.Client.Pointing; namespace Content.Client.Pointing;
public sealed class PointingSystem : SharedPointingSystem public sealed class PointingSystem : SharedPointingSystem
{ {
[Dependency] private readonly AnimationPlayerSystem _player = default!;
[Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly FloatingVisualizerSystem _floatingSystem = default!;
private const string AnimationKey = "pointingarrow";
/// <summary>
/// How far it goes in any direction.
/// </summary>
private const float Offset = 0.25f;
/// <summary>
/// How long it takes to go from the bottom of the animation to the top.
/// </summary>
private const float UpTime = 0.5f;
/// <summary>
/// Starts at the bottom then goes up and comes back down. Seems to look nicer than starting in the middle.
/// </summary>
private static readonly Animation PointingAnimation = new Animation()
{
Length = TimeSpan.FromSeconds(2 * UpTime),
AnimationTracks =
{
new AnimationTrackComponentProperty()
{
ComponentType = typeof(SpriteComponent),
Property = nameof(SpriteComponent.Offset),
InterpolationMode = AnimationInterpolationMode.Linear,
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(Vector2.Zero, 0f),
new AnimationTrackProperty.KeyFrame(new Vector2(0f, Offset), UpTime),
new AnimationTrackProperty.KeyFrame(Vector2.Zero, UpTime),
}
}
}
};
public override void Initialize() public override void Initialize()
{ {
@@ -61,7 +25,7 @@ public sealed class PointingSystem : SharedPointingSystem
private void OnArrowAnimation(EntityUid uid, PointingArrowComponent component, AnimationCompletedEvent args) private void OnArrowAnimation(EntityUid uid, PointingArrowComponent component, AnimationCompletedEvent args)
{ {
_player.Play(uid, PointingAnimation, AnimationKey); _floatingSystem.FloatAnimation(uid, component.Offset, component.AnimationKey, component.AnimationTime);
} }
private void AddPointingVerb(GetVerbsEvent<Verb> args) private void AddPointingVerb(GetVerbsEvent<Verb> args)
@@ -98,19 +62,19 @@ public sealed class PointingSystem : SharedPointingSystem
args.Verbs.Add(verb); args.Verbs.Add(verb);
} }
private void OnArrowStartup(EntityUid uid, PointingArrowComponent arrow, ComponentStartup args) private void OnArrowStartup(EntityUid uid, PointingArrowComponent component, ComponentStartup args)
{ {
if (EntityManager.TryGetComponent(uid, out SpriteComponent? sprite)) if (TryComp<SpriteComponent>(uid, out var sprite))
{ {
sprite.DrawDepth = (int) DrawDepth.Overlays; sprite.DrawDepth = (int) DrawDepth.Overlays;
} }
_player.Play(uid, PointingAnimation, AnimationKey); _floatingSystem.FloatAnimation(uid, component.Offset, component.AnimationKey, component.AnimationTime);
} }
private void OnRogueArrowStartup(EntityUid uid, RoguePointingArrowComponent arrow, ComponentStartup args) private void OnRogueArrowStartup(EntityUid uid, RoguePointingArrowComponent arrow, ComponentStartup args)
{ {
if (EntityManager.TryGetComponent(uid, out SpriteComponent? sprite)) if (TryComp<SpriteComponent>(uid, out var sprite))
{ {
sprite.DrawDepth = (int) DrawDepth.Overlays; sprite.DrawDepth = (int) DrawDepth.Overlays;
sprite.NoRotation = false; sprite.NoRotation = false;

View File

@@ -0,0 +1,7 @@
using Content.Shared.Gravity;
using Robust.Shared.GameStates;
namespace Content.Server.Gravity;
/// <inheritdoc/>
public sealed class FloatingVisualizerSystem : SharedFloatingVisualizerSystem { }

View File

@@ -0,0 +1,43 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Gravity;
[RegisterComponent, NetworkedComponent]
[Access(typeof(SharedFloatingVisualizerSystem))]
public sealed class FloatingVisualsComponent : Component
{
/// <summary>
/// How long it takes to go from the bottom of the animation to the top.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("animationTime")]
public float AnimationTime = 2f;
/// <summary>
/// How far it goes in any direction.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("offset")]
public Vector2 Offset = new(0, 0.2f);
[ViewVariables(VVAccess.ReadWrite)]
public bool CanFloat = false;
public readonly string AnimationKey = "gravity";
}
[Serializable, NetSerializable]
public sealed class SharedFloatingVisualsComponentState : ComponentState
{
public float AnimationTime;
public Vector2 Offset;
public bool HasGravity;
public SharedFloatingVisualsComponentState(float animationTime, Vector2 offset, bool hasGravity)
{
AnimationTime = animationTime;
Offset = offset;
HasGravity = hasGravity;
}
}

View File

@@ -0,0 +1,78 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Gravity;
/// <summary>
/// Handles offsetting a sprite when there is no gravity
/// </summary>
public abstract class SharedFloatingVisualizerSystem : EntitySystem
{
[Dependency] private readonly SharedGravitySystem GravitySystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FloatingVisualsComponent, ComponentStartup>(OnComponentStartup);
SubscribeLocalEvent<GravityChangedEvent>(OnGravityChanged);
SubscribeLocalEvent<FloatingVisualsComponent, EntParentChangedMessage>(OnEntParentChanged);
SubscribeLocalEvent<FloatingVisualsComponent, ComponentGetState>(OnComponentGetState);
SubscribeLocalEvent<FloatingVisualsComponent, ComponentHandleState>(OnComponentHandleState);
}
/// <summary>
/// Offsets a sprite with a linear interpolation animation
/// </summary>
public virtual void FloatAnimation(EntityUid uid, Vector2 offset, string animationKey, float animationTime, bool stop = false) { }
protected bool CanFloat(EntityUid uid, FloatingVisualsComponent component, TransformComponent? transform = null)
{
component.CanFloat = GravitySystem.IsWeightless(uid, xform: transform);
Dirty(component);
return component.CanFloat;
}
private void OnComponentStartup(EntityUid uid, FloatingVisualsComponent component, ComponentStartup args)
{
if (CanFloat(uid, component))
FloatAnimation(uid, component.Offset, component.AnimationKey, component.AnimationTime);
}
private void OnGravityChanged(ref GravityChangedEvent args)
{
foreach (var (floating, transform) in EntityQuery<FloatingVisualsComponent, TransformComponent>(true))
{
if (transform.GridUid != args.ChangedGridIndex)
continue;
floating.CanFloat = !args.HasGravity;
Dirty(floating);
var uid = floating.Owner;
if (!args.HasGravity)
FloatAnimation(uid, floating.Offset, floating.AnimationKey, floating.AnimationTime);
}
}
private void OnEntParentChanged(EntityUid uid, FloatingVisualsComponent component, ref EntParentChangedMessage args)
{
var transform = args.Transform;
if (CanFloat(uid, component, transform))
FloatAnimation(uid, component.Offset, component.AnimationKey, component.AnimationTime);
}
private void OnComponentGetState(EntityUid uid, FloatingVisualsComponent component, ref ComponentGetState args)
{
args.State = new SharedFloatingVisualsComponentState(component.AnimationTime, component.Offset, component.CanFloat);
}
private void OnComponentHandleState(EntityUid uid, FloatingVisualsComponent component, ref ComponentHandleState args)
{
if (args.Current is not SharedFloatingVisualsComponentState state)
return;
component.AnimationTime = state.AnimationTime;
component.Offset = state.Offset;
component.CanFloat = state.HasGravity;
}
}

View File

@@ -222,4 +222,6 @@
bloodMaxVolume: 150 bloodMaxVolume: 150
- type: MobPrice - type: MobPrice
price: 150 price: 150
- type: Appearance
- type: FloatingVisuals

View File

@@ -229,6 +229,7 @@
- type: CreamPiedVisualizer - type: CreamPiedVisualizer
state: creampie_human state: creampie_human
- type: RotationVisuals - type: RotationVisuals
- type: FloatingVisuals
- type: FireVisuals - type: FireVisuals
sprite: Mobs/Effects/onfire.rsi sprite: Mobs/Effects/onfire.rsi
normalState: Generic_mob_burning normalState: Generic_mob_burning