mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-09-29 15:47:26 +00:00
* cherry-pick: Screenshake (EphemeralSpace/ephemeral-space#627) * commit your shit at some point maybe * y * who knows * rename? * debugging + shit api * i hope no one fucks my ass * erm * p * replace with shit that makes way more sense * stuff * pretty sure this is just fine * actual fix noise shit * uhhh hmaybe if i feel like it wrong then later * explosion shake * grav shake * gun shake * idk * projectile melee Co-authored-by: DisposableCrewmember42 <disposablecrewmember42@proton.me> * tweak: tune recoil on weapons * docs: add _ES license info to LEGAL.md * fix: screenshake on spawn fix from ss14Starlight/space-station-14#3664 Co-authored-by: neomoth <69372252+neomoth@users.noreply.github.com> * feat: add client cvar to disable screenshake * feat: add screenshake toggle to accessibility settings * fix: upmerge compatibility * docs: properly document cvar * docs: add missing import comments --------- Co-authored-by: mirrorcult <mirrorcult@pm.me> Co-authored-by: neomoth <69372252+neomoth@users.noreply.github.com>
139 lines
4.9 KiB
C#
139 lines
4.9 KiB
C#
using System.Linq;
|
|
using System.Numerics;
|
|
using Content.Shared.Camera;
|
|
using Robust.Shared.Noise;
|
|
using Robust.Shared.Player;
|
|
using Robust.Shared.Random;
|
|
using Robust.Shared.Timing;
|
|
|
|
namespace Content.Shared._ES.Camera;
|
|
|
|
/// <summary>
|
|
/// Handles sending rotational or translational screenshake to an entity, managing the screenshake commands
|
|
/// of every entity currently screenshaking, and setting offset/rotation when updated
|
|
/// </summary>
|
|
// DeltaV - renamed from ESScreenshakeSystem to SharedESScreenshakeSystem
|
|
public sealed class SharedESScreenshakeSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly IRobustRandom _random = default!;
|
|
[Dependency] private readonly IGameTiming _timing = default!;
|
|
|
|
#region Internal
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
// SubscribeLocalEvent<ESScreenshakeComponent, ESGetEyeRotationEvent>(OnGetEyeRotation); // DeltaV - Moved to Client system
|
|
// SubscribeLocalEvent<ESScreenshakeComponent, GetEyeOffsetEvent>(OnGetEyeOffset); // DeltaV - Moved to Client system
|
|
SubscribeLocalEvent<ESScreenshakeComponent, EntityUnpausedEvent>(OnEntityUnpaused);
|
|
}
|
|
|
|
|
|
public override void Update(float frameTime)
|
|
{
|
|
base.Update(frameTime);
|
|
|
|
// TODO mirror might make sense to never remove individual commands and only remove the comp if theyre all > calculatedend instead.
|
|
var shakers = EntityQueryEnumerator<EyeComponent, ESScreenshakeComponent>();
|
|
while (shakers.MoveNext(out var ent, out var eye, out var shake))
|
|
{
|
|
if (shake.Commands.Count == 0)
|
|
{
|
|
RemCompDeferred<ESScreenshakeComponent>(ent);
|
|
continue;
|
|
}
|
|
|
|
foreach (var command in shake.Commands.ToList())
|
|
{
|
|
// handle removing old commands
|
|
if (_timing.CurTime < command.CalculatedEnd)
|
|
continue;
|
|
shake.Commands.Remove(command);
|
|
Dirty(ent, shake);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnEntityUnpaused(Entity<ESScreenshakeComponent> ent, ref EntityUnpausedEvent args)
|
|
{
|
|
// rebuild screenshake commands but with offset times
|
|
var newSet = new HashSet<ESScreenshakeCommand>();
|
|
foreach (var command in ent.Comp.Commands)
|
|
{
|
|
var newCommand = command with
|
|
{
|
|
CalculatedEnd = command.CalculatedEnd + args.PausedTime,
|
|
Start = command.Start + args.PausedTime,
|
|
};
|
|
|
|
newSet.Add(newCommand);
|
|
}
|
|
|
|
ent.Comp.Commands = newSet;
|
|
Dirty(ent);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates when both traumas will be at least = 0 given the decay rate and start time.
|
|
/// </summary>
|
|
private TimeSpan CalculateEndTimeForCommand(Entity<ESScreenshakeComponent> ent, ESScreenshakeParameters? translation, ESScreenshakeParameters? rotation, TimeSpan start)
|
|
{
|
|
// https://www.desmos.com/calculator/optip8eucx
|
|
var secsUntilRotationalEnd = rotation != null ? MathF.Sqrt(rotation.Trauma / rotation.DecayRate) : 0f;
|
|
var secsUntilTranslationalEnd = translation != null ? MathF.Sqrt(translation.Trauma / translation.DecayRate) : 0f;
|
|
var larger = secsUntilTranslationalEnd >= secsUntilRotationalEnd
|
|
? secsUntilTranslationalEnd
|
|
: secsUntilRotationalEnd;
|
|
|
|
return start + TimeSpan.FromSeconds(larger);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the trauma value for the current time, given the decay rate and start time.
|
|
/// </summary>
|
|
// DeltaV - make public
|
|
public float CalculateTraumaValueForCurrentTime(ESScreenshakeParameters parameters, TimeSpan start)
|
|
{
|
|
var timeDiff = _timing.CurTime - start;
|
|
|
|
// erm
|
|
if (timeDiff < TimeSpan.Zero)
|
|
return 0f;
|
|
|
|
// trauma decays quadratically with seconds passed
|
|
// https://www.desmos.com/calculator/optip8eucx
|
|
var totalSecsSquared = (float) (timeDiff.TotalSeconds * timeDiff.TotalSeconds);
|
|
return -totalSecsSquared * parameters.DecayRate + parameters.Trauma;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public API
|
|
|
|
public void Screenshake(EntityUid uid, ESScreenshakeParameters? translation, ESScreenshakeParameters? rotation)
|
|
{
|
|
if (!HasComp<EyeComponent>(uid))
|
|
return;
|
|
|
|
var comp = EnsureComp<ESScreenshakeComponent>(uid);
|
|
var time = _timing.CurTime;
|
|
var end = CalculateEndTimeForCommand((uid, comp), translation, rotation, time);
|
|
var command = new ESScreenshakeCommand(translation, rotation, time, end);
|
|
|
|
comp.Commands.Add(command);
|
|
Dirty(uid, comp);
|
|
}
|
|
|
|
public void Screenshake(Filter filter, ESScreenshakeParameters? translation, ESScreenshakeParameters? rotation)
|
|
{
|
|
foreach (var player in filter.Recipients)
|
|
{
|
|
if (player.AttachedEntity is {} ent)
|
|
Screenshake(ent, translation, rotation);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|