From 67ed59a50d4f052f6b1919fd34188ab0f67db5c9 Mon Sep 17 00:00:00 2001
From: AJCM-git <60196617+AJCM-git@users.noreply.github.com>
Date: Tue, 17 Jan 2023 18:01:53 -0400
Subject: [PATCH] Makes mobs visually float when weightless (#13391)
---
.../Gravity/FloatingVisualizerSystem.cs | 61 +++++++++++++++
.../Components/PointingArrowComponent.cs | 21 ++++-
Content.Client/Pointing/PointingSystem.cs | 50 ++----------
.../Gravity/FloatingVisualizerSystem.cs | 7 ++
.../Gravity/FloatingVisualsComponent.cs | 43 ++++++++++
.../Gravity/SharedFloatingVisualizerSystem.cs | 78 +++++++++++++++++++
.../Entities/Mobs/NPCs/simplemob.yml | 2 +
.../Prototypes/Entities/Mobs/Species/base.yml | 1 +
8 files changed, 217 insertions(+), 46 deletions(-)
create mode 100644 Content.Client/Gravity/FloatingVisualizerSystem.cs
create mode 100644 Content.Server/Gravity/FloatingVisualizerSystem.cs
create mode 100644 Content.Shared/Gravity/FloatingVisualsComponent.cs
create mode 100644 Content.Shared/Gravity/SharedFloatingVisualizerSystem.cs
diff --git a/Content.Client/Gravity/FloatingVisualizerSystem.cs b/Content.Client/Gravity/FloatingVisualizerSystem.cs
new file mode 100644
index 0000000000..9a18670b2f
--- /dev/null
+++ b/Content.Client/Gravity/FloatingVisualizerSystem.cs
@@ -0,0 +1,61 @@
+using Content.Shared.Gravity;
+using Robust.Client.GameObjects;
+using Robust.Client.Animations;
+using Robust.Shared.Animations;
+
+namespace Content.Client.Gravity;
+
+///
+public sealed class FloatingVisualizerSystem : SharedFloatingVisualizerSystem
+{
+ [Dependency] private readonly AnimationPlayerSystem AnimationSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnAnimationCompleted);
+ }
+
+ ///
+ 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);
+ }
+}
diff --git a/Content.Client/Pointing/Components/PointingArrowComponent.cs b/Content.Client/Pointing/Components/PointingArrowComponent.cs
index 3c82e62588..26a47b1925 100644
--- a/Content.Client/Pointing/Components/PointingArrowComponent.cs
+++ b/Content.Client/Pointing/Components/PointingArrowComponent.cs
@@ -1,7 +1,22 @@
using Content.Shared.Pointing.Components;
-namespace Content.Client.Pointing.Components
+namespace Content.Client.Pointing.Components;
+[RegisterComponent]
+public sealed class PointingArrowComponent : SharedPointingArrowComponent
{
- [RegisterComponent]
- public sealed class PointingArrowComponent : SharedPointingArrowComponent {}
+ ///
+ /// How long it takes to go from the bottom of the animation to the top.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("animationTime")]
+ public readonly float AnimationTime = 0.5f;
+
+ ///
+ /// How far it goes in any direction.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("offset")]
+ public readonly Vector2 Offset = (0, 0.25f);
+
+ public readonly string AnimationKey = "pointingarrow";
}
diff --git a/Content.Client/Pointing/PointingSystem.cs b/Content.Client/Pointing/PointingSystem.cs
index a151600edd..7978f604ec 100644
--- a/Content.Client/Pointing/PointingSystem.cs
+++ b/Content.Client/Pointing/PointingSystem.cs
@@ -1,53 +1,17 @@
using Content.Client.Pointing.Components;
+using Content.Client.Gravity;
using Content.Shared.Mobs.Systems;
using Content.Shared.Pointing;
using Content.Shared.Verbs;
-using Robust.Client.Animations;
using Robust.Client.GameObjects;
-using Robust.Shared.Animations;
using DrawDepth = Content.Shared.DrawDepth.DrawDepth;
namespace Content.Client.Pointing;
public sealed class PointingSystem : SharedPointingSystem
{
- [Dependency] private readonly AnimationPlayerSystem _player = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
-
- private const string AnimationKey = "pointingarrow";
-
- ///
- /// How far it goes in any direction.
- ///
- private const float Offset = 0.25f;
-
- ///
- /// How long it takes to go from the bottom of the animation to the top.
- ///
- private const float UpTime = 0.5f;
-
- ///
- /// Starts at the bottom then goes up and comes back down. Seems to look nicer than starting in the middle.
- ///
- 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),
- }
- }
- }
- };
+ [Dependency] private readonly FloatingVisualizerSystem _floatingSystem = default!;
public override void Initialize()
{
@@ -61,7 +25,7 @@ public sealed class PointingSystem : SharedPointingSystem
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 args)
@@ -98,19 +62,19 @@ public sealed class PointingSystem : SharedPointingSystem
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(uid, out var sprite))
{
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)
{
- if (EntityManager.TryGetComponent(uid, out SpriteComponent? sprite))
+ if (TryComp(uid, out var sprite))
{
sprite.DrawDepth = (int) DrawDepth.Overlays;
sprite.NoRotation = false;
diff --git a/Content.Server/Gravity/FloatingVisualizerSystem.cs b/Content.Server/Gravity/FloatingVisualizerSystem.cs
new file mode 100644
index 0000000000..94e783f9e5
--- /dev/null
+++ b/Content.Server/Gravity/FloatingVisualizerSystem.cs
@@ -0,0 +1,7 @@
+using Content.Shared.Gravity;
+using Robust.Shared.GameStates;
+
+namespace Content.Server.Gravity;
+
+///
+public sealed class FloatingVisualizerSystem : SharedFloatingVisualizerSystem { }
diff --git a/Content.Shared/Gravity/FloatingVisualsComponent.cs b/Content.Shared/Gravity/FloatingVisualsComponent.cs
new file mode 100644
index 0000000000..40186db844
--- /dev/null
+++ b/Content.Shared/Gravity/FloatingVisualsComponent.cs
@@ -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
+{
+ ///
+ /// How long it takes to go from the bottom of the animation to the top.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("animationTime")]
+ public float AnimationTime = 2f;
+
+ ///
+ /// How far it goes in any direction.
+ ///
+ [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;
+ }
+}
diff --git a/Content.Shared/Gravity/SharedFloatingVisualizerSystem.cs b/Content.Shared/Gravity/SharedFloatingVisualizerSystem.cs
new file mode 100644
index 0000000000..f4db37a585
--- /dev/null
+++ b/Content.Shared/Gravity/SharedFloatingVisualizerSystem.cs
@@ -0,0 +1,78 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Gravity;
+
+///
+/// Handles offsetting a sprite when there is no gravity
+///
+public abstract class SharedFloatingVisualizerSystem : EntitySystem
+{
+ [Dependency] private readonly SharedGravitySystem GravitySystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnComponentStartup);
+ SubscribeLocalEvent(OnGravityChanged);
+ SubscribeLocalEvent(OnEntParentChanged);
+ SubscribeLocalEvent(OnComponentGetState);
+ SubscribeLocalEvent(OnComponentHandleState);
+ }
+
+ ///
+ /// Offsets a sprite with a linear interpolation animation
+ ///
+ 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(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;
+ }
+}
diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml
index 6b8112f820..34b3d18bd3 100644
--- a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml
+++ b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml
@@ -222,4 +222,6 @@
bloodMaxVolume: 150
- type: MobPrice
price: 150
+ - type: Appearance
+ - type: FloatingVisuals
diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml
index 2ac1577bea..d2ecd7a9b5 100644
--- a/Resources/Prototypes/Entities/Mobs/Species/base.yml
+++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml
@@ -229,6 +229,7 @@
- type: CreamPiedVisualizer
state: creampie_human
- type: RotationVisuals
+ - type: FloatingVisuals
- type: FireVisuals
sprite: Mobs/Effects/onfire.rsi
normalState: Generic_mob_burning