mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-09-29 15:47:26 +00:00
feat: Fancy Screenshake from Ephemeral Space (#5503)
* 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>
This commit is contained in:
co-authored by
neomoth
mirrorcult
parent
3a05ef885c
commit
9146c59ecf
@@ -36,6 +36,9 @@ public sealed class EyeLerpingSystem : EntitySystem
|
|||||||
UpdatesAfter.Add(typeof(TransformSystem));
|
UpdatesAfter.Add(typeof(TransformSystem));
|
||||||
UpdatesAfter.Add(typeof(Robust.Client.Physics.PhysicsSystem));
|
UpdatesAfter.Add(typeof(Robust.Client.Physics.PhysicsSystem));
|
||||||
UpdatesBefore.Add(typeof(SharedEyeSystem));
|
UpdatesBefore.Add(typeof(SharedEyeSystem));
|
||||||
|
// ES START
|
||||||
|
UpdatesBefore.Add(typeof(EyeSystem));
|
||||||
|
// ES END
|
||||||
UpdatesOutsidePrediction = true;
|
UpdatesOutsidePrediction = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +71,11 @@ public sealed class EyeLerpingSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
_eye.SetRotation(uid, lerpInfo.TargetRotation, component);
|
_eye.SetRotation(uid, lerpInfo.TargetRotation, component);
|
||||||
_eye.SetZoom(uid, lerpInfo.TargetZoom, component);
|
_eye.SetZoom(uid, lerpInfo.TargetZoom, component);
|
||||||
|
|
||||||
|
// Starlight begin - fix ES Screenshake triggering on spawn
|
||||||
|
if (TryComp<ContentEyeComponent>(uid, out var contentEye))
|
||||||
|
contentEye.BaseRotation = lerpInfo.TargetRotation;
|
||||||
|
// Starlight end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,9 +183,15 @@ public sealed class EyeLerpingSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
var tickFraction = (float) _gameTiming.TickFraction / ushort.MaxValue;
|
var tickFraction = (float) _gameTiming.TickFraction / ushort.MaxValue;
|
||||||
const double lerpMinimum = 0.00001;
|
const double lerpMinimum = 0.00001;
|
||||||
var query = AllEntityQuery<LerpingEyeComponent, EyeComponent, TransformComponent>();
|
// ES START
|
||||||
|
// add contenteye bc we only want to modify the fuckin base rotation
|
||||||
|
var query = AllEntityQuery<LerpingEyeComponent, EyeComponent, ContentEyeComponent, TransformComponent>();
|
||||||
|
// ES END
|
||||||
|
|
||||||
while (query.MoveNext(out var entity, out var lerpInfo, out var eye, out var xform))
|
// ES START
|
||||||
|
// contenteye
|
||||||
|
while (query.MoveNext(out var entity, out var lerpInfo, out var eye, out var contentEye, out var xform))
|
||||||
|
// ES END
|
||||||
{
|
{
|
||||||
// Handle zoom
|
// Handle zoom
|
||||||
var zoomDiff = Vector2.Lerp(lerpInfo.LastZoom, lerpInfo.TargetZoom, tickFraction);
|
var zoomDiff = Vector2.Lerp(lerpInfo.LastZoom, lerpInfo.TargetZoom, tickFraction);
|
||||||
@@ -191,6 +205,14 @@ public sealed class EyeLerpingSystem : EntitySystem
|
|||||||
_eye.SetZoom(entity, zoomDiff, eye);
|
_eye.SetZoom(entity, zoomDiff, eye);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ES START
|
||||||
|
// UHHHHHHHHHHHh
|
||||||
|
// shit is fucked idk
|
||||||
|
// we need to alter baserotation not regular eye rotation and also i added baserotation because uhh
|
||||||
|
// there was no good way to separate 'how the eye should be oriented because of the grid/turning/etc' and 'the eye rotating on top of that'
|
||||||
|
// had to add it
|
||||||
|
// ES END
|
||||||
|
|
||||||
// Handle Rotation
|
// Handle Rotation
|
||||||
TryComp<InputMoverComponent>(entity, out var mover);
|
TryComp<InputMoverComponent>(entity, out var mover);
|
||||||
|
|
||||||
@@ -199,7 +221,10 @@ public sealed class EyeLerpingSystem : EntitySystem
|
|||||||
|
|
||||||
if (!NeedsLerp(mover))
|
if (!NeedsLerp(mover))
|
||||||
{
|
{
|
||||||
_eye.SetRotation(entity, lerpInfo.TargetRotation, eye);
|
// ES START
|
||||||
|
// contenteye
|
||||||
|
contentEye.BaseRotation = lerpInfo.TargetRotation;
|
||||||
|
// ES END
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,11 +232,17 @@ public sealed class EyeLerpingSystem : EntitySystem
|
|||||||
|
|
||||||
if (Math.Abs(shortest.Theta) < lerpMinimum)
|
if (Math.Abs(shortest.Theta) < lerpMinimum)
|
||||||
{
|
{
|
||||||
_eye.SetRotation(entity, lerpInfo.TargetRotation, eye);
|
// ES START
|
||||||
|
// contenteye
|
||||||
|
contentEye.BaseRotation = lerpInfo.TargetRotation;
|
||||||
|
// ES END
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_eye.SetRotation(entity, shortest * tickFraction + lerpInfo.LastRotation, eye);
|
// ES START
|
||||||
|
// contenteye
|
||||||
|
contentEye.BaseRotation = shortest * tickFraction + lerpInfo.LastRotation;
|
||||||
|
// ES END
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ public sealed class ContentEyeSystem : SharedContentEyeSystem
|
|||||||
while (eyeEntities.MoveNext(out var entity, out ContentEyeComponent? contentComponent, out EyeComponent? eyeComponent))
|
while (eyeEntities.MoveNext(out var entity, out ContentEyeComponent? contentComponent, out EyeComponent? eyeComponent))
|
||||||
{
|
{
|
||||||
UpdateEyeOffset((entity, eyeComponent));
|
UpdateEyeOffset((entity, eyeComponent));
|
||||||
|
// ES START
|
||||||
|
UpdateEyeRotation((entity, eyeComponent));
|
||||||
|
// ES END
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +74,9 @@ public sealed class ContentEyeSystem : SharedContentEyeSystem
|
|||||||
while (eyeEntities.MoveNext(out var entity, out ContentEyeComponent? contentComponent, out EyeComponent? eyeComponent))
|
while (eyeEntities.MoveNext(out var entity, out ContentEyeComponent? contentComponent, out EyeComponent? eyeComponent))
|
||||||
{
|
{
|
||||||
UpdateEyeOffset((entity, eyeComponent));
|
UpdateEyeOffset((entity, eyeComponent));
|
||||||
|
// ES START
|
||||||
|
UpdateEyeRotation((entity, eyeComponent));
|
||||||
|
// ES END
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<Label Text="{Loc 'ui-options-accessability-header-visuals'}"
|
<Label Text="{Loc 'ui-options-accessability-header-visuals'}"
|
||||||
StyleClasses="LabelKeyText"/>
|
StyleClasses="LabelKeyText"/>
|
||||||
<CheckBox Name="ReducedMotionCheckBox" Text="{Loc 'ui-options-reduced-motion'}" />
|
<CheckBox Name="ReducedMotionCheckBox" Text="{Loc 'ui-options-reduced-motion'}" />
|
||||||
|
<CheckBox Name="DisableScreenshakeCheckBox" Text="{Loc 'ui-options-es-screen-shake-toggle'}" /> <!-- DeltaV - ES Screenshake Port -->
|
||||||
<CheckBox Name="EnableColorNameCheckBox" Text="{Loc 'ui-options-enable-color-name'}" />
|
<CheckBox Name="EnableColorNameCheckBox" Text="{Loc 'ui-options-enable-color-name'}" />
|
||||||
<CheckBox Name="ColorblindFriendlyCheckBox" Text="{Loc 'ui-options-colorblind-friendly'}" />
|
<CheckBox Name="ColorblindFriendlyCheckBox" Text="{Loc 'ui-options-colorblind-friendly'}" />
|
||||||
<ui:OptionSlider Name="ScreenShakeIntensitySlider" Title="{Loc 'ui-options-screen-shake-intensity'}" />
|
<ui:OptionSlider Name="ScreenShakeIntensitySlider" Title="{Loc 'ui-options-screen-shake-intensity'}" />
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Content.Shared._DV.CCVars; // DeltaV - ES Screenshake Port
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
@@ -15,6 +16,7 @@ public sealed partial class AccessibilityTab : Control
|
|||||||
Control.AddOptionCheckBox(CCVars.ChatEnableColorName, EnableColorNameCheckBox);
|
Control.AddOptionCheckBox(CCVars.ChatEnableColorName, EnableColorNameCheckBox);
|
||||||
Control.AddOptionCheckBox(CCVars.AccessibilityColorblindFriendly, ColorblindFriendlyCheckBox);
|
Control.AddOptionCheckBox(CCVars.AccessibilityColorblindFriendly, ColorblindFriendlyCheckBox);
|
||||||
Control.AddOptionCheckBox(CCVars.ReducedMotion, ReducedMotionCheckBox);
|
Control.AddOptionCheckBox(CCVars.ReducedMotion, ReducedMotionCheckBox);
|
||||||
|
Control.AddOptionCheckBox(DCCVars.EsScreenshakeDisabled, DisableScreenshakeCheckBox); // DeltaV - ES Screenshake Port
|
||||||
Control.AddOptionPercentSlider(CCVars.ScreenShakeIntensity, ScreenShakeIntensitySlider);
|
Control.AddOptionPercentSlider(CCVars.ScreenShakeIntensity, ScreenShakeIntensitySlider);
|
||||||
Control.AddOptionPercentSlider(CCVars.ChatWindowOpacity, ChatWindowOpacitySlider);
|
Control.AddOptionPercentSlider(CCVars.ChatWindowOpacity, ChatWindowOpacitySlider);
|
||||||
Control.AddOptionPercentSlider(CCVars.SpeechBubbleTextOpacity, SpeechBubbleTextOpacitySlider);
|
Control.AddOptionPercentSlider(CCVars.SpeechBubbleTextOpacity, SpeechBubbleTextOpacitySlider);
|
||||||
|
|||||||
@@ -280,7 +280,10 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
if (!Timing.IsFirstTimePredicted || user == null || recoil == Vector2.Zero || recoilScalar == 0)
|
if (!Timing.IsFirstTimePredicted || user == null || recoil == Vector2.Zero || recoilScalar == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_recoil.KickCamera(user.Value, recoil.Normalized() * 0.5f * recoilScalar);
|
// ES START
|
||||||
|
// reduce recoil even further
|
||||||
|
_recoil.KickCamera(user.Value, recoil.Normalized() * 0.13f * recoilScalar);
|
||||||
|
// ES END
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Popup(string message, EntityUid? uid, EntityUid? user)
|
protected override void Popup(string message, EntityUid? uid, EntityUid? user)
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Shared._DV.CCVars;
|
||||||
|
using Content.Shared._ES.Camera;
|
||||||
|
using Content.Shared.Camera;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.Noise;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Client._ES;
|
||||||
|
|
||||||
|
// DeltaV - Anything not marked DeltaV is Ephemeral Space code moved from SharedESScreenshakeSystem
|
||||||
|
public sealed class ESScreenshakeSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IConfigurationManager _config = default!; // DeltaV
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly SharedESScreenshakeSystem _shared = default!;
|
||||||
|
|
||||||
|
private bool _disabled; // DeltaV
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<ESScreenshakeComponent, GetEyeOffsetEvent>(OnGetEyeOffset);
|
||||||
|
SubscribeLocalEvent<ESScreenshakeComponent, ESGetEyeRotationEvent>(OnGetEyeRotation);
|
||||||
|
|
||||||
|
_config.OnValueChanged(DCCVars.EsScreenshakeDisabled, OnDisabledChanged, true); // DeltaV
|
||||||
|
}
|
||||||
|
private void OnDisabledChanged(bool obj)
|
||||||
|
{
|
||||||
|
_disabled = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetEyeOffset(Entity<ESScreenshakeComponent> ent, ref GetEyeOffsetEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp<EyeComponent>(ent, out var eye) || _disabled) // DeltaV - check if disabled
|
||||||
|
return;
|
||||||
|
|
||||||
|
var noise = new FastNoiseLite(67);
|
||||||
|
noise.SetNoiseType(FastNoiseLite.NoiseType.OpenSimplex2);
|
||||||
|
|
||||||
|
var accumulatedOffset = Vector2.Zero;
|
||||||
|
var maxOffset = new Vector2(0.15f, 0.15f);
|
||||||
|
foreach (var command in ent.Comp.Commands)
|
||||||
|
{
|
||||||
|
if (command.Translational == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var trauma =
|
||||||
|
_shared.CalculateTraumaValueForCurrentTime(command.Translational, command.Start);
|
||||||
|
if (trauma <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
noise.SetFrequency(command.Translational.Frequency);
|
||||||
|
|
||||||
|
// using the starst c ommand for y pos kinda doesnt work in the case where multiple shakes get sent at the same time
|
||||||
|
// and the shakes are identical otherwise. but like dont do that or something idk
|
||||||
|
var offsetX = (maxOffset.X * trauma) * noise.GetNoise((float)_timing.RealTime.TotalMilliseconds,
|
||||||
|
(float)command.Start.TotalMilliseconds);
|
||||||
|
noise.SetSeed(68);
|
||||||
|
var offsetY = (maxOffset.Y * trauma) * noise.GetNoise((float)_timing.RealTime.TotalMilliseconds,
|
||||||
|
(float)command.Start.TotalMilliseconds);
|
||||||
|
noise.SetSeed(67);
|
||||||
|
accumulatedOffset += new Vector2(offsetX, offsetY);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.Offset += accumulatedOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetEyeRotation(Entity<ESScreenshakeComponent> ent, ref ESGetEyeRotationEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp<EyeComponent>(ent, out var eye) || _disabled) // DeltaV - check if disabled
|
||||||
|
return;
|
||||||
|
|
||||||
|
var noise = new FastNoiseLite(67 + 420); // Epic bacon
|
||||||
|
noise.SetNoiseType(FastNoiseLite.NoiseType.OpenSimplex2);
|
||||||
|
|
||||||
|
// 20deg max
|
||||||
|
var accumulatedAngle = Angle.Zero;
|
||||||
|
var maxAngleDegrees = 20f;
|
||||||
|
foreach (var command in ent.Comp.Commands)
|
||||||
|
{
|
||||||
|
if (command.Rotational == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var trauma =
|
||||||
|
_shared.CalculateTraumaValueForCurrentTime(command.Rotational, command.Start);
|
||||||
|
if (trauma <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
noise.SetFrequency(command.Rotational.Frequency);
|
||||||
|
|
||||||
|
var angle = (maxAngleDegrees * trauma) * noise.GetNoise((float)_timing.RealTime.TotalMilliseconds, (float)command.Start.TotalMilliseconds);
|
||||||
|
accumulatedAngle += Angle.FromDegrees(angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO ughhh this shit breaks with something idk
|
||||||
|
args.Rotation += accumulatedAngle;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using Content.Server.Administration.Logs;
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.Weapons.Ranged.Systems;
|
using Content.Server.Weapons.Ranged.Systems;
|
||||||
|
using Content.Shared._ES.Camera; // ES - Screenshake
|
||||||
using Content.Shared.Camera;
|
using Content.Shared.Camera;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
@@ -15,6 +16,9 @@ namespace Content.Server.Damage.Systems;
|
|||||||
|
|
||||||
public sealed class DamageOtherOnHitSystem : SharedDamageOtherOnHitSystem
|
public sealed class DamageOtherOnHitSystem : SharedDamageOtherOnHitSystem
|
||||||
{
|
{
|
||||||
|
// ES START
|
||||||
|
[Dependency] private readonly SharedESScreenshakeSystem _shake = default!;
|
||||||
|
// ES END
|
||||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
[Dependency] private readonly GunSystem _guns = default!;
|
[Dependency] private readonly GunSystem _guns = default!;
|
||||||
[Dependency] private readonly Shared.Damage.Systems.DamageableSystem _damageable = default!;
|
[Dependency] private readonly Shared.Damage.Systems.DamageableSystem _damageable = default!;
|
||||||
@@ -48,7 +52,12 @@ public sealed class DamageOtherOnHitSystem : SharedDamageOtherOnHitSystem
|
|||||||
if (TryComp<PhysicsComponent>(uid, out var body) && body.LinearVelocity.LengthSquared() > 0f)
|
if (TryComp<PhysicsComponent>(uid, out var body) && body.LinearVelocity.LengthSquared() > 0f)
|
||||||
{
|
{
|
||||||
var direction = body.LinearVelocity.Normalized();
|
var direction = body.LinearVelocity.Normalized();
|
||||||
_sharedCameraRecoil.KickCamera(args.Target, direction);
|
// ES START
|
||||||
|
// lower recoil + shake
|
||||||
|
_sharedCameraRecoil.KickCamera(args.Target, direction * 0.1f);
|
||||||
|
var otherHitShake = new ESScreenshakeParameters() { Trauma = 0.35f, DecayRate = 1.4f, Frequency = 0.014f };
|
||||||
|
_shake.Screenshake(args.Target, otherHitShake, null);
|
||||||
|
// ES END
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Content.Server.Destructible;
|
|||||||
using Content.Server.NodeContainer.EntitySystems;
|
using Content.Server.NodeContainer.EntitySystems;
|
||||||
using Content.Server.NPC.Pathfinding;
|
using Content.Server.NPC.Pathfinding;
|
||||||
using Content.Server.Station.Systems; // DeltaV - Admin QOL
|
using Content.Server.Station.Systems; // DeltaV - Admin QOL
|
||||||
|
using Content.Shared._ES.Camera; // ES - Screenshake
|
||||||
using Content.Shared.Atmos.Components;
|
using Content.Shared.Atmos.Components;
|
||||||
using Content.Shared.Camera;
|
using Content.Shared.Camera;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
@@ -35,6 +36,10 @@ namespace Content.Server.Explosion.EntitySystems;
|
|||||||
|
|
||||||
public sealed partial class ExplosionSystem : SharedExplosionSystem
|
public sealed partial class ExplosionSystem : SharedExplosionSystem
|
||||||
{
|
{
|
||||||
|
// ES START
|
||||||
|
[Dependency] private readonly SharedESScreenshakeSystem _shake = default!;
|
||||||
|
// ES END
|
||||||
|
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
||||||
@@ -378,7 +383,9 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
|
|||||||
var visualEnt = CreateExplosionVisualEntity(pos, queued.Proto.ID, spaceMatrix, spaceData, gridData.Values, iterationIntensity);
|
var visualEnt = CreateExplosionVisualEntity(pos, queued.Proto.ID, spaceMatrix, spaceData, gridData.Values, iterationIntensity);
|
||||||
|
|
||||||
// camera shake
|
// camera shake
|
||||||
CameraShake(iterationIntensity.Count * 4f, pos, queued.TotalIntensity);
|
// ES START
|
||||||
|
// CameraShake(iterationIntensity.Count * 4f, pos, queued.TotalIntensity);
|
||||||
|
// ES END
|
||||||
|
|
||||||
//For whatever bloody reason, sound system requires ENTITY coordinates.
|
//For whatever bloody reason, sound system requires ENTITY coordinates.
|
||||||
var mapEntityCoords = _transformSystem.ToCoordinates(_map.GetMap(pos.MapId), pos);
|
var mapEntityCoords = _transformSystem.ToCoordinates(_map.GetMap(pos.MapId), pos);
|
||||||
@@ -409,6 +416,13 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
|
|||||||
? queued.Proto.SmallSoundFar
|
? queued.Proto.SmallSoundFar
|
||||||
: queued.Proto.SoundFar;
|
: queued.Proto.SoundFar;
|
||||||
|
|
||||||
|
// ES START
|
||||||
|
var farTranslationShake = iterationIntensity.Count < queued.Proto.SmallSoundIterationThreshold
|
||||||
|
? new ESScreenshakeParameters() { Trauma = 0.4f, DecayRate = 0.2f, Frequency = 0.014f }
|
||||||
|
: new ESScreenshakeParameters() { Trauma = 0.6f, DecayRate = 0.05f, Frequency = 0.014f };
|
||||||
|
_shake.Screenshake(filter, farTranslationShake, null);
|
||||||
|
// ES END
|
||||||
|
|
||||||
_audio.PlayGlobal(farSound, farFilter, true, farSound.Params);
|
_audio.PlayGlobal(farSound, farFilter, true, farSound.Params);
|
||||||
|
|
||||||
return new Explosion(this,
|
return new Explosion(this,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Content.Server.Administration.Logs;
|
|||||||
using Content.Server.Destructible;
|
using Content.Server.Destructible;
|
||||||
using Content.Server.Effects;
|
using Content.Server.Effects;
|
||||||
using Content.Server.Weapons.Ranged.Systems;
|
using Content.Server.Weapons.Ranged.Systems;
|
||||||
|
using Content.Shared._ES.Camera; // ES - Screenshake
|
||||||
using Content.Shared.Camera;
|
using Content.Shared.Camera;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
@@ -17,6 +18,9 @@ namespace Content.Server.Projectiles;
|
|||||||
|
|
||||||
public sealed class ProjectileSystem : SharedProjectileSystem
|
public sealed class ProjectileSystem : SharedProjectileSystem
|
||||||
{
|
{
|
||||||
|
// ES START
|
||||||
|
[Dependency] private readonly SharedESScreenshakeSystem _shake = default!;
|
||||||
|
// ES END
|
||||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
[Dependency] private readonly ColorFlashEffectSystem _color = default!;
|
[Dependency] private readonly ColorFlashEffectSystem _color = default!;
|
||||||
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||||
@@ -81,8 +85,13 @@ public sealed class ProjectileSystem : SharedProjectileSystem
|
|||||||
{
|
{
|
||||||
_guns.PlayImpactSound(target, damage, component.SoundHit, component.ForceSound);
|
_guns.PlayImpactSound(target, damage, component.SoundHit, component.ForceSound);
|
||||||
|
|
||||||
|
// ES START
|
||||||
|
// shake + lower recoil
|
||||||
|
var otherShakeTranslation = new ESScreenshakeParameters() { Trauma = 0.45f, DecayRate = 1.1f, Frequency = 0.04f };
|
||||||
if (!args.OurBody.LinearVelocity.IsLengthZero())
|
if (!args.OurBody.LinearVelocity.IsLengthZero())
|
||||||
_sharedCameraRecoil.KickCamera(target, args.OurBody.LinearVelocity.Normalized());
|
_sharedCameraRecoil.KickCamera(target, args.OurBody.LinearVelocity.Normalized() * 0.08f);
|
||||||
|
_shake.Screenshake(target, otherShakeTranslation, null);
|
||||||
|
// ES END
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component.DeleteOnCollide && component.ProjectileSpent)
|
if (component.DeleteOnCollide && component.ProjectileSpent)
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
|
using Content.Shared._ES.Camera; // ES
|
||||||
|
using Content.Shared.GameTicking; // ES
|
||||||
|
using Robust.Shared.Player; // ES
|
||||||
|
using Robust.Shared.Timing; // ES
|
||||||
|
|
||||||
namespace Content.Shared.Gravity;
|
namespace Content.Shared.Gravity;
|
||||||
|
|
||||||
public abstract partial class SharedGravitySystem
|
public abstract partial class SharedGravitySystem
|
||||||
{
|
{
|
||||||
|
// ES START
|
||||||
|
[Dependency] private readonly SharedGameTicker _ticker = default!;
|
||||||
|
[Dependency] private readonly SharedESScreenshakeSystem _shake = default!;
|
||||||
|
// ES END
|
||||||
|
|
||||||
protected const float GravityKick = 100.0f;
|
protected const float GravityKick = 100.0f;
|
||||||
protected const float ShakeCooldown = 0.2f;
|
protected const float ShakeCooldown = 0.2f;
|
||||||
|
|
||||||
@@ -37,6 +47,20 @@ public abstract partial class SharedGravitySystem
|
|||||||
if (!Resolve(uid, ref gravity, false))
|
if (!Resolve(uid, ref gravity, false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// ES START
|
||||||
|
// do not shake grid if the round just started
|
||||||
|
// i did not want to have to think this logic through more. this is the simplest solution i could think of
|
||||||
|
if (Timing.CurTime - _ticker.RoundStartTimeSpan < TimeSpan.FromSeconds(30))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// ES SCREENSHAKE LOGIC
|
||||||
|
// instead of poopass camera kick
|
||||||
|
var translation = new ESScreenshakeParameters() { Trauma = 0.8f, DecayRate = 0.04f, Frequency = 0.015f };
|
||||||
|
var filter = Filter.BroadcastGrid(uid);
|
||||||
|
_shake.Screenshake(filter, translation, null);
|
||||||
|
|
||||||
|
return;
|
||||||
|
// ES END
|
||||||
if (!TryComp<GravityShakeComponent>(uid, out var shake))
|
if (!TryComp<GravityShakeComponent>(uid, out var shake))
|
||||||
{
|
{
|
||||||
shake = AddComp<GravityShakeComponent>(uid);
|
shake = AddComp<GravityShakeComponent>(uid);
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ namespace Content.Shared.Movement.Components;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Holds SS14 eye data not relevant for engine, e.g. lerp targets.
|
/// Holds SS14 eye data not relevant for engine, e.g. lerp targets.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedContentEyeSystem))]
|
// ES START
|
||||||
|
// STOP adding ACCESS to shit for NO REASON
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
// ES END
|
||||||
public sealed partial class ContentEyeComponent : Component
|
public sealed partial class ContentEyeComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -21,4 +24,13 @@ public sealed partial class ContentEyeComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("maxZoom"), AutoNetworkedField]
|
[ViewVariables(VVAccess.ReadWrite), DataField("maxZoom"), AutoNetworkedField]
|
||||||
public Vector2 MaxZoom = Vector2.One;
|
public Vector2 MaxZoom = Vector2.One;
|
||||||
|
|
||||||
|
// ES START
|
||||||
|
// uhh fuckk
|
||||||
|
/// <summary>
|
||||||
|
/// Base rotation of this eye, because of grid movement/turning/etc. Modified by eye lerp.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public Angle BaseRotation = Angle.Zero;
|
||||||
|
// ES END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Content.Shared._ES.Camera; // ES - Screenshake
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Administration.Managers;
|
using Content.Shared.Administration.Managers;
|
||||||
using Content.Shared.Camera;
|
using Content.Shared.Camera;
|
||||||
@@ -160,6 +161,20 @@ public abstract class SharedContentEyeSystem : EntitySystem
|
|||||||
_eye.SetOffset(eye, ev.Offset + evRelayed.Offset, eye);
|
_eye.SetOffset(eye, ev.Offset + evRelayed.Offset, eye);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ES START
|
||||||
|
public void UpdateEyeRotation(Entity<EyeComponent> eye)
|
||||||
|
{
|
||||||
|
var baseAngle = Angle.Zero;
|
||||||
|
if (TryComp<ContentEyeComponent>(eye, out var contentEye))
|
||||||
|
baseAngle = contentEye.BaseRotation;
|
||||||
|
|
||||||
|
var ev = new ESGetEyeRotationEvent();
|
||||||
|
RaiseLocalEvent(eye, ref ev);
|
||||||
|
|
||||||
|
_eye.SetRotation(eye, baseAngle + ev.Rotation, eye);
|
||||||
|
}
|
||||||
|
// ES END
|
||||||
|
|
||||||
public void UpdatePvsScale(EntityUid uid, ContentEyeComponent? contentEye = null, EyeComponent? eye = null)
|
public void UpdatePvsScale(EntityUid uid, ContentEyeComponent? contentEye = null, EyeComponent? eye = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref contentEye) || !Resolve(uid, ref eye))
|
if (!Resolve(uid, ref contentEye) || !Resolve(uid, ref eye))
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Content.Shared._ES.Camera; // ES - Screenshake
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
using Content.Shared.Actions.Events;
|
using Content.Shared.Actions.Events;
|
||||||
using Content.Shared.Administration.Components;
|
using Content.Shared.Administration.Components;
|
||||||
@@ -67,6 +68,9 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
|||||||
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
|
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
|
||||||
[Dependency] private readonly SharedStaminaSystem _stamina = default!;
|
[Dependency] private readonly SharedStaminaSystem _stamina = default!;
|
||||||
[Dependency] private readonly DamageExamineSystem _damageExamine = default!;
|
[Dependency] private readonly DamageExamineSystem _damageExamine = default!;
|
||||||
|
// ES START
|
||||||
|
[Dependency] private readonly SharedESScreenshakeSystem _shake = default!;
|
||||||
|
// ES END
|
||||||
|
|
||||||
private const int AttackMask = (int) (CollisionGroup.MobMask | CollisionGroup.Opaque);
|
private const int AttackMask = (int) (CollisionGroup.MobMask | CollisionGroup.Opaque);
|
||||||
|
|
||||||
@@ -594,6 +598,18 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
|||||||
if (damageResult.GetTotal() > FixedPoint2.Zero)
|
if (damageResult.GetTotal() > FixedPoint2.Zero)
|
||||||
{
|
{
|
||||||
DoDamageEffect(targets, user, targetXform);
|
DoDamageEffect(targets, user, targetXform);
|
||||||
|
|
||||||
|
// ES START
|
||||||
|
// dog shit copy plaste but thats melee for you
|
||||||
|
var userShakeRotation = new ESScreenshakeParameters()
|
||||||
|
{ Trauma = 0.08f, DecayRate = 1.0f, Frequency = 0.009f };
|
||||||
|
var otherShakeTranslation = new ESScreenshakeParameters() { Trauma = 0.45f, DecayRate = 1.1f, Frequency = 0.04f };
|
||||||
|
_shake.Screenshake(user, null, userShakeRotation);
|
||||||
|
foreach (var shakeTarget in targets)
|
||||||
|
{
|
||||||
|
_shake.Screenshake(shakeTarget, otherShakeTranslation, null);
|
||||||
|
}
|
||||||
|
// ES END
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -759,6 +775,18 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
|||||||
_meleeSound.PlayHitSound(target, user, GetHighestDamageSound(appliedDamage, _protoManager), hitEvent.HitSoundOverride, component);
|
_meleeSound.PlayHitSound(target, user, GetHighestDamageSound(appliedDamage, _protoManager), hitEvent.HitSoundOverride, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ES START
|
||||||
|
// dog shit copy plaste but thats melee for you
|
||||||
|
var userShakeRotation = new ESScreenshakeParameters()
|
||||||
|
{ Trauma = 0.08f, DecayRate = 1.0f, Frequency = 0.009f };
|
||||||
|
var otherShakeTranslation = new ESScreenshakeParameters() { Trauma = 0.45f, DecayRate = 1.1f, Frequency = 0.04f };
|
||||||
|
_shake.Screenshake(user, null, userShakeRotation);
|
||||||
|
foreach (var shakeTarget in targets)
|
||||||
|
{
|
||||||
|
_shake.Screenshake(shakeTarget, otherShakeTranslation, null);
|
||||||
|
}
|
||||||
|
// ES END
|
||||||
|
|
||||||
if (appliedDamage.GetTotal() > FixedPoint2.Zero)
|
if (appliedDamage.GetTotal() > FixedPoint2.Zero)
|
||||||
{
|
{
|
||||||
DoDamageEffect(targets, user, Transform(targets[0]));
|
DoDamageEffect(targets, user, Transform(targets[0]));
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Content.Shared._ES.Camera; // ES
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
using Content.Shared.Actions;
|
using Content.Shared.Actions;
|
||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
@@ -41,6 +42,9 @@ namespace Content.Shared.Weapons.Ranged.Systems;
|
|||||||
|
|
||||||
public abstract partial class SharedGunSystem : EntitySystem
|
public abstract partial class SharedGunSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
// ES START
|
||||||
|
[Dependency] private SharedESScreenshakeSystem _shake = default!;
|
||||||
|
// ES END
|
||||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||||
[Dependency] private readonly INetManager _netManager = default!;
|
[Dependency] private readonly INetManager _netManager = default!;
|
||||||
@@ -423,6 +427,12 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
var shotEv = new GunShotEvent(user, ev.Ammo);
|
var shotEv = new GunShotEvent(user, ev.Ammo);
|
||||||
RaiseLocalEvent(gun, ref shotEv);
|
RaiseLocalEvent(gun, ref shotEv);
|
||||||
|
|
||||||
|
// ES START
|
||||||
|
// this is a suspicious place to do this but whatever.
|
||||||
|
var gunShakeRotation = new ESScreenshakeParameters() { Trauma = 0.085f * gun.Comp.CameraRecoilScalarModified, DecayRate = 1.2f, Frequency = 0.008f};
|
||||||
|
_shake.Screenshake(user, null, gunShakeRotation);
|
||||||
|
// ES END
|
||||||
|
|
||||||
if (!userImpulse || !TryComp<PhysicsComponent>(user, out var userPhysics))
|
if (!userImpulse || !TryComp<PhysicsComponent>(user, out var userPhysics))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|||||||
@@ -301,4 +301,11 @@ public sealed partial class DCCVars
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly CVarDef<int> CosmicCultFinaleDelaySeconds =
|
public static readonly CVarDef<int> CosmicCultFinaleDelaySeconds =
|
||||||
CVarDef.Create("cosmiccult.extra_entropy_for_finale", 1, CVar.SERVER);
|
CVarDef.Create("cosmiccult.extra_entropy_for_finale", 1, CVar.SERVER);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the screenshake ported from ES should be disabled.
|
||||||
|
/// False by default, so enabled. Players can change this in accessiblity settings.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly CVarDef<bool> EsScreenshakeDisabled =
|
||||||
|
CVarDef.Create("deltav.es_screenshake.disabled", false, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using Content.Shared.Camera;
|
||||||
|
using Content.Shared.Movement.Systems;
|
||||||
|
|
||||||
|
namespace Content.Shared._ES.Camera;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised directed by-ref when <see cref="SharedContentEyeSystem.UpdateEyeRotation"/> is called.
|
||||||
|
/// Should be subscribed to by any systems that want to modify an entity's eye rotation,
|
||||||
|
/// so that they do not override each other.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Counterpart of <see cref="GetEyeOffsetEvent"/>, but for rotation, to use for screenshake.
|
||||||
|
/// </remarks>
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct ESGetEyeRotationEvent(Angle Rotation);
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared._ES.Camera;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marks an entity which is actively screenshaking because of a screenshake command being given.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This doesn't mark an entity which *can* screenshake--all entities can, by default, as long as a client is controlling them.
|
||||||
|
/// </remarks>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
public sealed partial class ESScreenshakeComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A set of screenshake commands which this entity is currently processing.
|
||||||
|
/// Trauma is 0..1, with 0 being no shake and 1 being maximum shake.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is a set, because order doesn't matter, and we don't want to accidentally readd the same command twice.
|
||||||
|
/// </remarks>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public HashSet<ESScreenshakeCommand> Commands = new();
|
||||||
|
|
||||||
|
public override bool SendOnlyToOwner => true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a single screenshake command. These are stored and networked on <see cref="ESScreenshakeComponent"/>,
|
||||||
|
/// and the client that controls that entity will use the trauma values in each command, and their start time,
|
||||||
|
/// to calculate multipliers on the current eye offset & rotation modifiers.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Translational">Parameters of translational screenshake (offset-based)</param>
|
||||||
|
/// <param name="Rotational">Parameters of rotational screenshake (rotation-based)</param>
|
||||||
|
/// <param name="Start">Time this screenshake command was added.</param>
|
||||||
|
/// <param name="CalculatedEnd">The end time for this command, calculated from trauma, decay rate, and start time.</param>
|
||||||
|
[DataRecord, Serializable, NetSerializable]
|
||||||
|
public partial record ESScreenshakeCommand(ESScreenshakeParameters? Translational, ESScreenshakeParameters? Rotational, TimeSpan Start, TimeSpan CalculatedEnd);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the parameters of a single instance of screenshake, which may apply to translational or rotational shake.
|
||||||
|
/// </summary>
|
||||||
|
[DataDefinition, Serializable, NetSerializable]
|
||||||
|
public partial record ESScreenshakeParameters()
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Strength of the shake.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public float Trauma = 0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How fast the shake decays.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public float DecayRate = 1.2f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How frantically the shake oscillates.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public float Frequency = 0.01f;
|
||||||
|
};
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using Content.Shared.Trigger.Components.Effects;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared._ES.Camera.Trigger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Screenshakes the user on trigger.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
public sealed partial class ESScreenshakeUserOnTriggerComponent : BaseXOnTriggerComponent
|
||||||
|
{
|
||||||
|
[DataField]
|
||||||
|
public ESScreenshakeParameters? Translation;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public ESScreenshakeParameters? Rotation;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using Content.Shared.Trigger;
|
||||||
|
|
||||||
|
namespace Content.Shared._ES.Camera.Trigger;
|
||||||
|
|
||||||
|
public sealed class ESScreenshakeUserOnTriggerSystem : XOnTriggerSystem<ESScreenshakeUserOnTriggerComponent>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedESScreenshakeSystem _screenShake = default!;
|
||||||
|
|
||||||
|
protected override void OnTrigger(Entity<ESScreenshakeUserOnTriggerComponent> ent, EntityUid target, ref TriggerEvent args)
|
||||||
|
{
|
||||||
|
if (args.User == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_screenShake.Screenshake(args.User.Value, ent.Comp.Translation, ent.Comp.Rotation);
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,6 +27,8 @@ For MIT this means you **must not** remove the copy of the MIT license from any
|
|||||||
|
|
||||||
- Code taken from [the Starlight project](https://github.com/ss14Starlight/space-station-14) in the `_Starlight` namespace was taken under [their custom MIT-like license](/LICENSE-Starlight.txt) and sublicensed to AGPLv3.
|
- Code taken from [the Starlight project](https://github.com/ss14Starlight/space-station-14) in the `_Starlight` namespace was taken under [their custom MIT-like license](/LICENSE-Starlight.txt) and sublicensed to AGPLv3.
|
||||||
|
|
||||||
|
- Code from [Ephemeral Space](https://github.com/EphemeralSpace/ephemeral-space) in the `_ES` namespace was taken under [the MIT license](/LICENSE-MIT.txt).
|
||||||
|
|
||||||
Assets have distinct licenses in `attributions.yml` and `meta.json` files which must be followed individually.
|
Assets have distinct licenses in `attributions.yml` and `meta.json` files which must be followed individually.
|
||||||
|
|
||||||
## Warranty
|
## Warranty
|
||||||
|
|||||||
@@ -18,3 +18,7 @@ ui-options-function-open-c-help = Open curator help
|
|||||||
ui-options-delta-tips = Tips
|
ui-options-delta-tips = Tips
|
||||||
ui-options-delta-disable-tips = Disable tips
|
ui-options-delta-disable-tips = Disable tips
|
||||||
ui-options-delta-reset-tips = Reset all seen tips
|
ui-options-delta-reset-tips = Reset all seen tips
|
||||||
|
|
||||||
|
## DeltaV Screenshake Accessiblity Options
|
||||||
|
ui-options-screen-shake-intensity = Camera kick intensity
|
||||||
|
ui-options-es-screen-shake-toggle = Disable Screenshake
|
||||||
|
|||||||
@@ -363,7 +363,8 @@ ui-options-accessability-header-content = Content
|
|||||||
ui-options-enable-color-name = Add colors to character names
|
ui-options-enable-color-name = Add colors to character names
|
||||||
ui-options-colorblind-friendly = Colorblind friendly mode
|
ui-options-colorblind-friendly = Colorblind friendly mode
|
||||||
ui-options-reduced-motion = Reduce motion of visual effects
|
ui-options-reduced-motion = Reduce motion of visual effects
|
||||||
ui-options-screen-shake-intensity = Screen shake intensity
|
# DeltaV - Moved to _DV file
|
||||||
|
# ui-options-screen-shake-intensity = Screen shake intensity
|
||||||
|
|
||||||
ui-options-chat-window-opacity = Chat window opacity
|
ui-options-chat-window-opacity = Chat window opacity
|
||||||
ui-options-speech-bubble-text-opacity = Speech bubble text opacity
|
ui-options-speech-bubble-text-opacity = Speech bubble text opacity
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
- type: AmmoCounter
|
- type: AmmoCounter
|
||||||
- type: Gun
|
- type: Gun
|
||||||
fireRate: 2
|
fireRate: 2
|
||||||
|
cameraRecoilScalar: 0.5 # DeltaV - most laser guns have lower screenshake
|
||||||
selectedMode: SemiAuto
|
selectedMode: SemiAuto
|
||||||
availableModes:
|
availableModes:
|
||||||
- SemiAuto
|
- SemiAuto
|
||||||
@@ -260,6 +261,7 @@
|
|||||||
shader: unshaded
|
shader: unshaded
|
||||||
- type: Gun
|
- type: Gun
|
||||||
projectileSpeed: 10
|
projectileSpeed: 10
|
||||||
|
cameraRecoilScalar: 1.0 # DeltaV
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Effects/Lightning/lightningshock.ogg
|
path: /Audio/Effects/Lightning/lightningshock.ogg
|
||||||
params:
|
params:
|
||||||
@@ -471,6 +473,7 @@
|
|||||||
sprite: Objects/Weapons/Guns/Battery/laser_cannon.rsi
|
sprite: Objects/Weapons/Guns/Battery/laser_cannon.rsi
|
||||||
- type: Gun
|
- type: Gun
|
||||||
fireRate: .7 # Delta V - was 1.5
|
fireRate: .7 # Delta V - was 1.5
|
||||||
|
cameraRecoilScalar: 1.0 # DeltaV
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
||||||
- type: BatteryAmmoProvider
|
- type: BatteryAmmoProvider
|
||||||
@@ -507,6 +510,7 @@
|
|||||||
map: ["enum.GunVisualLayers.Base"]
|
map: ["enum.GunVisualLayers.Base"]
|
||||||
- type: Gun
|
- type: Gun
|
||||||
fireRate: 0.5
|
fireRate: 0.5
|
||||||
|
cameraRecoilScalar: 1.0 # DeltaV
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/emitter.ogg
|
path: /Audio/Weapons/emitter.ogg
|
||||||
params:
|
params:
|
||||||
@@ -539,6 +543,7 @@
|
|||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Objects/Weapons/Guns/Battery/xray.rsi
|
sprite: Objects/Weapons/Guns/Battery/xray.rsi
|
||||||
- type: Gun
|
- type: Gun
|
||||||
|
cameraRecoilScalar: 1.0 # DeltaV
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/laser3.ogg
|
path: /Audio/Weapons/Guns/Gunshots/laser3.ogg
|
||||||
- type: BatteryAmmoProvider
|
- type: BatteryAmmoProvider
|
||||||
@@ -579,6 +584,7 @@
|
|||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 100
|
price: 100
|
||||||
- type: Gun
|
- type: Gun
|
||||||
|
cameraRecoilScalar: 0.25 # DeltaV
|
||||||
fireRate: 2
|
fireRate: 2
|
||||||
projectileSpeed: 35 # any higher and this causes issues in lag
|
projectileSpeed: 35 # any higher and this causes issues in lag
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
@@ -691,6 +697,7 @@
|
|||||||
- type: Gun
|
- type: Gun
|
||||||
fireRate: 0.2 # DeltaV - No taser spam, was 0.5
|
fireRate: 0.2 # DeltaV - No taser spam, was 0.5
|
||||||
resetOnHandSelected: false # DeltaV - Makes it so you dont have to wait 10 seconds to shoot a taser after pulling it out
|
resetOnHandSelected: false # DeltaV - Makes it so you dont have to wait 10 seconds to shoot a taser after pulling it out
|
||||||
|
cameraRecoilScalar: 0.25 # DeltaV
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/taser.ogg
|
path: /Audio/Weapons/Guns/Gunshots/taser.ogg
|
||||||
projectileSpeed: 25
|
projectileSpeed: 25
|
||||||
@@ -927,6 +934,7 @@
|
|||||||
sprite: Objects/Weapons/Guns/Battery/energy_shotgun.rsi
|
sprite: Objects/Weapons/Guns/Battery/energy_shotgun.rsi
|
||||||
- type: Gun
|
- type: Gun
|
||||||
fireRate: 2
|
fireRate: 2
|
||||||
|
cameraRecoilScalar: 1.0 # DeltaV
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
||||||
- type: BatteryAmmoProvider
|
- type: BatteryAmmoProvider
|
||||||
@@ -983,6 +991,7 @@
|
|||||||
- type: StealTarget
|
- type: StealTarget
|
||||||
stealGroup: HoSSidearm # Delta V - to work along side the X-01
|
stealGroup: HoSSidearm # Delta V - to work along side the X-01
|
||||||
- type: Gun
|
- type: Gun
|
||||||
|
cameraRecoilScalar: 1.0 # DeltaV
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
||||||
- type: BatteryAmmoProvider
|
- type: BatteryAmmoProvider
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
selectedMode: SemiAuto
|
selectedMode: SemiAuto
|
||||||
availableModes:
|
availableModes:
|
||||||
- SemiAuto
|
- SemiAuto
|
||||||
|
cameraRecoilScalar: 1.5 # DeltaV - higher screenshake on snipers
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/sniper.ogg
|
path: /Audio/Weapons/Guns/Gunshots/sniper.ogg
|
||||||
- type: BallisticAmmoProvider
|
- type: BallisticAmmoProvider
|
||||||
|
|||||||
@@ -124,6 +124,7 @@
|
|||||||
availableModes:
|
availableModes:
|
||||||
- SemiAuto
|
- SemiAuto
|
||||||
- FullAuto
|
- FullAuto
|
||||||
|
cameraRecoilScalar: 1.0 # DeltaV
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Effects/thunk.ogg
|
path: /Audio/Effects/thunk.ogg
|
||||||
soundEmpty:
|
soundEmpty:
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
- type: entity
|
||||||
|
name: screenshake debug doohickey
|
||||||
|
parent: BaseItem
|
||||||
|
id: ESDebugScreenshake
|
||||||
|
description: ohhh my head horts
|
||||||
|
suffix: ES, DEBUG
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Weapons/Melee/debug.rsi
|
||||||
|
state: icon
|
||||||
|
- type: ESScreenshakeUserOnTrigger
|
||||||
|
# VV to have an effect
|
||||||
|
translation:
|
||||||
|
trauma: 0.4
|
||||||
|
decayRate: 1.4
|
||||||
|
frequency: 0.04
|
||||||
|
- type: TriggerOnUse
|
||||||
|
- type: Item
|
||||||
|
size: Tiny
|
||||||
|
sprite: Objects/Weapons/Melee/debug.rsi
|
||||||
Reference in New Issue
Block a user