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
{
///
/// A system for applying a jitter animation to any entity.
///
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;
///
/// List of jitter components to be removed, cached so we don't allocate it every tick.
///
private readonly List _removeList = new();
public override void Initialize()
{
SubscribeLocalEvent(OnGetState);
SubscribeLocalEvent(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;
}
///
/// Applies a jitter effect to the specified entity.
/// You can apply this to any entity whatsoever, so be careful what you use it on!
///
///
/// If the entity is already jittering, the jitter values will be updated but only if they're greater
/// than the current ones and is false.
///
/// Entity in question.
/// For how much time to apply the effect.
/// Jitteriness of the animation. See and .
/// Frequency for jittering. See and .
/// Whether to change any existing jitter value even if they're greater than the ones we're setting.
public void DoJitter(EntityUid uid, TimeSpan time, float amplitude = 10f, float frequency = 4f, bool forceValueChange = false)
{
var jittering = EntityManager.EnsureComponent(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();
}
///
/// Immediately stops any jitter animation from an entity.
///
/// The entity in question.
public void StopJitter(EntityUid uid)
{
if (!EntityManager.HasComponent(uid))
return;
EntityManager.RemoveComponent(uid);
}
public override void Update(float frameTime)
{
foreach (var jittering in EntityManager.EntityQuery())
{
if(jittering.EndTime <= GameTiming.CurTime)
_removeList.Add(jittering);
}
if (_removeList.Count == 0)
return;
foreach (var jittering in _removeList)
{
jittering.Owner.RemoveComponent();
}
_removeList.Clear();
}
}
}