Adds jittering. (#4809)
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
This commit is contained in:
committed by
GitHub
parent
05cd30b564
commit
67fd509c9f
103
Content.Client/Jittering/JitteringSystem.cs
Normal file
103
Content.Client/Jittering/JitteringSystem.cs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using Content.Shared.Jittering;
|
||||||
|
using Robust.Client.Animations;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Shared.Animations;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Client.Jittering
|
||||||
|
{
|
||||||
|
public class JitteringSystem : SharedJitteringSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
|
private readonly float[] _sign = { -1, 1 };
|
||||||
|
private readonly string _jitterAnimationKey = "jittering";
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<JitteringComponent, ComponentStartup>(OnStartup);
|
||||||
|
SubscribeLocalEvent<JitteringComponent, ComponentShutdown>(OnShutdown);
|
||||||
|
SubscribeLocalEvent<JitteringComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStartup(EntityUid uid, JitteringComponent jittering, ComponentStartup args)
|
||||||
|
{
|
||||||
|
if (!EntityManager.TryGetComponent(uid, out ISpriteComponent? sprite))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var animationPlayer = EntityManager.EnsureComponent<AnimationPlayerComponent>(uid);
|
||||||
|
|
||||||
|
animationPlayer.Play(GetAnimation(jittering, sprite), _jitterAnimationKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnShutdown(EntityUid uid, JitteringComponent jittering, ComponentShutdown args)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent(uid, out AnimationPlayerComponent? animationPlayer))
|
||||||
|
animationPlayer.Stop(_jitterAnimationKey);
|
||||||
|
|
||||||
|
if (EntityManager.TryGetComponent(uid, out SpriteComponent? sprite))
|
||||||
|
sprite.Offset = Vector2.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAnimationCompleted(EntityUid uid, JitteringComponent jittering, AnimationCompletedEvent args)
|
||||||
|
{
|
||||||
|
if(args.Key != _jitterAnimationKey || jittering.EndTime <= GameTiming.CurTime)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(EntityManager.TryGetComponent(uid, out AnimationPlayerComponent? animationPlayer)
|
||||||
|
&& EntityManager.TryGetComponent(uid, out ISpriteComponent? sprite))
|
||||||
|
animationPlayer.Play(GetAnimation(jittering, sprite), _jitterAnimationKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Animation GetAnimation(JitteringComponent jittering, ISpriteComponent sprite)
|
||||||
|
{
|
||||||
|
var amplitude = MathF.Min(4f, jittering.Amplitude / 100f + 1f) / 10f;
|
||||||
|
var offset = new Vector2(_random.NextFloat(amplitude/4f, amplitude),
|
||||||
|
_random.NextFloat(amplitude / 4f, amplitude / 3f));
|
||||||
|
|
||||||
|
offset.X *= _random.Pick(_sign);
|
||||||
|
offset.Y *= _random.Pick(_sign);
|
||||||
|
|
||||||
|
if (Math.Sign(offset.X) == Math.Sign(jittering.LastJitter.X)
|
||||||
|
|| Math.Sign(offset.Y) == Math.Sign(jittering.LastJitter.Y))
|
||||||
|
{
|
||||||
|
// If the sign is the same as last time on both axis we flip one randomly
|
||||||
|
// to avoid jitter staying in one quadrant too much.
|
||||||
|
if (_random.Prob(0.5f))
|
||||||
|
offset.X *= -1;
|
||||||
|
else
|
||||||
|
offset.Y *= -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animation length shouldn't be too high so we will cap it at 2 seconds...
|
||||||
|
var length = Math.Min((1f/jittering.Frequency), 2f);
|
||||||
|
|
||||||
|
jittering.LastJitter = offset;
|
||||||
|
|
||||||
|
return new Animation()
|
||||||
|
{
|
||||||
|
Length = TimeSpan.FromSeconds(length),
|
||||||
|
AnimationTracks =
|
||||||
|
{
|
||||||
|
new AnimationTrackComponentProperty()
|
||||||
|
{
|
||||||
|
ComponentType = typeof(ISpriteComponent),
|
||||||
|
Property = nameof(ISpriteComponent.Offset),
|
||||||
|
KeyFrames =
|
||||||
|
{
|
||||||
|
new AnimationTrackProperty.KeyFrame(sprite.Offset, 0f),
|
||||||
|
new AnimationTrackProperty.KeyFrame(offset, length),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ using Content.Server.Nutrition.EntitySystems;
|
|||||||
using Content.Server.Stunnable.Components;
|
using Content.Server.Stunnable.Components;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Jittering;
|
||||||
using Content.Shared.MobState;
|
using Content.Shared.MobState;
|
||||||
using Content.Shared.Nutrition.Components;
|
using Content.Shared.Nutrition.Components;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
@@ -71,6 +72,11 @@ namespace Content.Server.Administration.Commands
|
|||||||
{
|
{
|
||||||
EntitySystem.Get<CreamPieSystem>().SetCreamPied(target.Uid, creamPied, false);
|
EntitySystem.Get<CreamPieSystem>().SetCreamPied(target.Uid, creamPied, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target.HasComponent<JitteringComponent>())
|
||||||
|
{
|
||||||
|
target.RemoveComponent<JitteringComponent>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
Content.Server/Jittering/JitteringSystem.cs
Normal file
9
Content.Server/Jittering/JitteringSystem.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using Content.Shared.Jittering;
|
||||||
|
|
||||||
|
namespace Content.Server.Jittering
|
||||||
|
{
|
||||||
|
public class JitteringSystem : SharedJitteringSystem
|
||||||
|
{
|
||||||
|
// This entity system only exists on the server so it will be registered, otherwise we can't use SharedJitteringSystem...
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.Items;
|
using Content.Server.Items;
|
||||||
|
using Content.Server.Jittering;
|
||||||
using Content.Server.PowerCell.Components;
|
using Content.Server.PowerCell.Components;
|
||||||
using Content.Server.Stunnable.Components;
|
using Content.Server.Stunnable.Components;
|
||||||
using Content.Server.Weapon.Melee;
|
using Content.Server.Weapon.Melee;
|
||||||
@@ -70,6 +72,8 @@ namespace Content.Server.Stunnable
|
|||||||
if (!Get<ActionBlockerSystem>().CanUse(args.User))
|
if (!Get<ActionBlockerSystem>().CanUse(args.User))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Get<JitteringSystem>().DoJitter(args.User.Uid, TimeSpan.FromMinutes(1), 20f, 8f);
|
||||||
|
|
||||||
if (comp.Activated)
|
if (comp.Activated)
|
||||||
{
|
{
|
||||||
TurnOff(comp);
|
TurnOff(comp);
|
||||||
|
|||||||
44
Content.Shared/Jittering/JitteringComponent.cs
Normal file
44
Content.Shared/Jittering/JitteringComponent.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using Robust.Shared.Analyzers;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
|
namespace Content.Shared.Jittering
|
||||||
|
{
|
||||||
|
[Friend(typeof(SharedJitteringSystem))]
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public class JitteringComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "Jittering";
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public TimeSpan EndTime { get; set; }
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float Amplitude { get; set; }
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float Frequency { get; set; }
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public Vector2 LastJitter { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class JitteringComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public TimeSpan EndTime { get; }
|
||||||
|
public float Amplitude { get; }
|
||||||
|
public float Frequency { get; }
|
||||||
|
|
||||||
|
public JitteringComponentState(TimeSpan endTime, float amplitude, float frequency)
|
||||||
|
{
|
||||||
|
EndTime = endTime;
|
||||||
|
Amplitude = amplitude;
|
||||||
|
Frequency = frequency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
114
Content.Shared/Jittering/SharedJitteringSystem.cs
Normal file
114
Content.Shared/Jittering/SharedJitteringSystem.cs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Shared.Jittering
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A system for applying a jitter animation to any entity.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SharedJitteringSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] protected readonly IGameTiming GameTiming = default!;
|
||||||
|
|
||||||
|
public float MaxAmplitude = 300f;
|
||||||
|
public float MinAmplitude = 1f;
|
||||||
|
|
||||||
|
public float MaxFrequency = 10f;
|
||||||
|
public float MinFrequency = 1f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of jitter components to be removed, cached so we don't allocate it every tick.
|
||||||
|
/// </summary>
|
||||||
|
private readonly List<JitteringComponent> _removeList = new();
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<JitteringComponent, ComponentGetState>(OnGetState);
|
||||||
|
SubscribeLocalEvent<JitteringComponent, ComponentHandleState>(OnHandleState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetState(EntityUid uid, JitteringComponent component, ref ComponentGetState args)
|
||||||
|
{
|
||||||
|
args.State = new JitteringComponentState(component.EndTime, component.Amplitude, component.Frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHandleState(EntityUid uid, JitteringComponent component, ref ComponentHandleState args)
|
||||||
|
{
|
||||||
|
if (args.Current is not JitteringComponentState jitteringState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
component.EndTime = jitteringState.EndTime;
|
||||||
|
component.Amplitude = jitteringState.Amplitude;
|
||||||
|
component.Frequency = jitteringState.Frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies a jitter effect to the specified entity.
|
||||||
|
/// You can apply this to any entity whatsoever, so be careful what you use it on!
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If the entity is already jittering, the jitter values will be updated but only if they're greater
|
||||||
|
/// than the current ones and <see cref="forceValueChange"/> is false.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="uid">Entity in question.</param>
|
||||||
|
/// <param name="time">For how much time to apply the effect.</param>
|
||||||
|
/// <param name="amplitude">Jitteriness of the animation. See <see cref="MaxAmplitude"/> and <see cref="MinAmplitude"/>.</param>
|
||||||
|
/// <param name="frequency">Frequency for jittering. See <see cref="MaxFrequency"/> and <see cref="MinFrequency"/>.</param>
|
||||||
|
/// <param name="forceValueChange">Whether to change any existing jitter value even if they're greater than the ones we're setting.</param>
|
||||||
|
public void DoJitter(EntityUid uid, TimeSpan time, float amplitude = 10f, float frequency = 4f, bool forceValueChange = false)
|
||||||
|
{
|
||||||
|
var jittering = EntityManager.EnsureComponent<JitteringComponent>(uid);
|
||||||
|
|
||||||
|
var endTime = GameTiming.CurTime + time;
|
||||||
|
|
||||||
|
amplitude = Math.Clamp(amplitude, MinAmplitude, MaxAmplitude);
|
||||||
|
frequency = Math.Clamp(frequency, MinFrequency, MaxFrequency);
|
||||||
|
|
||||||
|
if (forceValueChange || jittering.EndTime < endTime)
|
||||||
|
jittering.EndTime = endTime;
|
||||||
|
|
||||||
|
if(forceValueChange || jittering.Amplitude < amplitude)
|
||||||
|
jittering.Amplitude = amplitude;
|
||||||
|
|
||||||
|
if (forceValueChange || jittering.Frequency < frequency)
|
||||||
|
jittering.Frequency = frequency;
|
||||||
|
|
||||||
|
jittering.Dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Immediately stops any jitter animation from an entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">The entity in question.</param>
|
||||||
|
public void StopJitter(EntityUid uid)
|
||||||
|
{
|
||||||
|
if (!EntityManager.HasComponent<JitteringComponent>(uid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
EntityManager.RemoveComponent<JitteringComponent>(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
foreach (var jittering in EntityManager.EntityQuery<JitteringComponent>())
|
||||||
|
{
|
||||||
|
if(jittering.EndTime <= GameTiming.CurTime)
|
||||||
|
_removeList.Add(jittering);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_removeList.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var jittering in _removeList)
|
||||||
|
{
|
||||||
|
jittering.Owner.RemoveComponent<JitteringComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
_removeList.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Submodule RobustToolbox updated: 39f82694bf...22e0fbc6c1
@@ -224,6 +224,8 @@
|
|||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=hardcode/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=hardcode/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=hbox/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=hbox/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=inhand/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=inhand/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Jitteriness/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Jittering/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Juiceable/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Juiceable/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=keybind/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=keybind/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=keybinds/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=keybinds/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
|||||||
Reference in New Issue
Block a user