From 02e9751013fa00cff51bc152ec0ecdf12c81ff7f Mon Sep 17 00:00:00 2001 From: Marchy <89603088+M4rchy-S@users.noreply.github.com> Date: Sat, 28 Mar 2026 23:06:30 +0100 Subject: [PATCH] "Reduce motion of visual effects" now works for blood loss effect (#41878) * "Reduce motion of visual effects" now works for blood loss effect * Improve strenght * Make motion less strong * Stop motion effect * cleanup and rebalancing * fix warning --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Content.Client/Drunk/DrunkOverlay.cs | 37 +++++++++++++++++++-------- Content.Client/Drunk/DrunkSystem.cs | 6 +++++ Resources/Textures/Shaders/drunk.swsl | 29 +++++++++++++-------- 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/Content.Client/Drunk/DrunkOverlay.cs b/Content.Client/Drunk/DrunkOverlay.cs index 692232776aa..44d82f3794b 100644 --- a/Content.Client/Drunk/DrunkOverlay.cs +++ b/Content.Client/Drunk/DrunkOverlay.cs @@ -1,8 +1,8 @@ +using Content.Shared.CCVar; using Content.Shared.Drunk; -using Content.Shared.StatusEffect; -using Content.Shared.StatusEffectNew; using Robust.Client.Graphics; using Robust.Client.Player; +using Robust.Shared.Configuration; using Robust.Shared.Enums; using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -11,19 +11,23 @@ namespace Content.Client.Drunk; public sealed class DrunkOverlay : Overlay { - private static readonly ProtoId Shader = "Drunk"; + private static readonly ProtoId DrunkShader = "Drunk"; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly IEntitySystemManager _sysMan = default!; [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IConfigurationManager _configManager = default!; + private readonly Shared.StatusEffectNew.StatusEffectsSystem _statusEffectsSystem; public override OverlaySpace Space => OverlaySpace.WorldSpace; public override bool RequestScreenTexture => true; private readonly ShaderInstance _drunkShader; public float CurrentBoozePower = 0.0f; + // Starting phase for the rotation effect. + // Needed so it doesn't always look the same for 0 motion. + public float Phase = 0f; private const float VisualThreshold = 10.0f; private const float PowerDivisor = 250.0f; @@ -37,12 +41,22 @@ public sealed class DrunkOverlay : Overlay private const float BoozePowerScale = 8f; - private float _visualScale = 0; + private float _visualScale = 0f; + private float _timeScale = 1f; + private float _distortionScale = 1f; public DrunkOverlay() { IoCManager.InjectDependencies(this); - _drunkShader = _prototypeManager.Index(Shader).InstanceUnique(); + _statusEffectsSystem = _entityManager.System(); + _drunkShader = _prototypeManager.Index(DrunkShader).InstanceUnique(); + _configManager.OnValueChanged(CCVars.ReducedMotion, OnReducedMotionChanged, invokeImmediately: true); + } + + private void OnReducedMotionChanged(bool reducedMotion) + { + _timeScale = reducedMotion ? 0.0f : 1.0f; + _distortionScale = reducedMotion ? 4.0f : 1.0f; // Make the offset stronger to compensate the lack of motion. } protected override void FrameUpdate(FrameEventArgs args) @@ -53,15 +67,14 @@ public sealed class DrunkOverlay : Overlay if (playerEntity == null) return; - var statusSys = _sysMan.GetEntitySystem(); - if (!statusSys.TryGetMaxTime(playerEntity.Value, out var status)) + if (!_statusEffectsSystem.TryGetMaxTime(playerEntity.Value, out var status)) return; var time = status.Item2; - var power = time == null ? MaxBoozePower : (float) Math.Min((time - _timing.CurTime).Value.TotalSeconds, MaxBoozePower); + var power = time == null ? MaxBoozePower : (float)Math.Min((time - _timing.CurTime).Value.TotalSeconds, MaxBoozePower); - CurrentBoozePower += BoozePowerScale * (power - CurrentBoozePower) * args.DeltaSeconds / (power+1); + CurrentBoozePower += BoozePowerScale * (power - CurrentBoozePower) * args.DeltaSeconds / (power + 1); } protected override bool BeforeDraw(in OverlayDrawArgs args) @@ -82,8 +95,12 @@ public sealed class DrunkOverlay : Overlay return; var handle = args.WorldHandle; + _drunkShader.SetParameter("SCREEN_TEXTURE", ScreenTexture); _drunkShader.SetParameter("boozePower", _visualScale); + _drunkShader.SetParameter("timeScale", _timeScale); + _drunkShader.SetParameter("distortionScale", _distortionScale); + _drunkShader.SetParameter("phase", Phase); handle.UseShader(_drunkShader); handle.DrawRect(args.WorldBounds, Color.White); handle.UseShader(null); diff --git a/Content.Client/Drunk/DrunkSystem.cs b/Content.Client/Drunk/DrunkSystem.cs index c5fab75e7dd..2e1f8157aa4 100644 --- a/Content.Client/Drunk/DrunkSystem.cs +++ b/Content.Client/Drunk/DrunkSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.StatusEffectNew; using Robust.Client.Graphics; using Robust.Client.Player; using Robust.Shared.Player; +using Robust.Shared.Random; namespace Content.Client.Drunk; @@ -10,6 +11,7 @@ public sealed class DrunkSystem : SharedDrunkSystem { [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IOverlayManager _overlayMan = default!; + [Dependency] private readonly IRobustRandom _random = default!; private DrunkOverlay _overlay = default!; @@ -29,7 +31,10 @@ public sealed class DrunkSystem : SharedDrunkSystem private void OnStatusApplied(Entity entity, ref StatusEffectAppliedEvent args) { if (!_overlayMan.HasOverlay()) + { + _overlay.Phase = _random.NextFloat(MathF.Tau); // random starting phase for movement effect _overlayMan.AddOverlay(_overlay); + } } private void OnStatusRemoved(Entity entity, ref StatusEffectRemovedEvent args) @@ -47,6 +52,7 @@ public sealed class DrunkSystem : SharedDrunkSystem private void OnPlayerAttached(Entity entity, ref StatusEffectRelayedEvent args) { _overlayMan.AddOverlay(_overlay); + } private void OnPlayerDetached(Entity entity, ref StatusEffectRelayedEvent args) diff --git a/Resources/Textures/Shaders/drunk.swsl b/Resources/Textures/Shaders/drunk.swsl index c2657c18a5e..1410baa8237 100644 --- a/Resources/Textures/Shaders/drunk.swsl +++ b/Resources/Textures/Shaders/drunk.swsl @@ -1,22 +1,29 @@ uniform sampler2D SCREEN_TEXTURE; uniform highp float boozePower; -const highp float TimeScale = 0.5; -const highp float DistortionScale = 0.01; +// How fast to do the rotating motion. +// 1 for normal effect. +// 0 for for no motion. +uniform highp float timeScale; +// Starting phase for the rotation effect. +// Needed so it doesn't always look the same for 0 motion. +uniform highp float phase; +// Multiplier for the amplitude of the offset. 1 for normal strength. +uniform highp float distortionScale; void fragment() { - highp float mod = mix(0.0, DistortionScale, boozePower); + highp float amplitudeMod = mix(0.0, distortionScale * 0.01, boozePower); highp vec2 coord = FRAGCOORD.xy * SCREEN_PIXEL_SIZE.xy; - highp float time = TIME * TimeScale; + highp float time = TIME * timeScale * 0.5 + phase; - highp vec2 offset = vec2((mod * 1.5) * sin(time * 1.5), (mod * 2.0) * cos(time * 1.5 - 0.2)); + highp vec2 offset = vec2((amplitudeMod * 1.5) * sin(time * 1.5), (amplitudeMod * 2.0) * cos(time * 1.5 - 0.2)); highp vec4 tex1 = zTextureSpec(SCREEN_TEXTURE, coord + offset); - - if (boozePower > 0.5) { - offset = vec2((mod * 2.0 - DistortionScale) * sin(time * 0.333 - 0.2), (mod * 2.0 - DistortionScale) * cos(time * 0.333)); - tex1 = mix(tex1, zTextureSpec(SCREEN_TEXTURE, coord + offset), mix(0.0, 0.3, boozePower*2.0-1.0)); + + if (boozePower > 0.25) { + offset = vec2((amplitudeMod * 2.0) * sin(time * 0.333 - 0.2), (amplitudeMod * 2.0) * cos(time * 0.333)); + tex1 = mix(tex1, zTextureSpec(SCREEN_TEXTURE, coord + offset), mix(0.0, 0.3, boozePower * 2.0 - 0.5)); } - - offset = vec2((mod * 1.0) * sin(time * 1.0 + 0.1), (mod * 1.0) * cos(time * 1.0)); + + offset = vec2(amplitudeMod * sin(time * 1.0 + 0.1), amplitudeMod * cos(time * 1.0)); COLOR = mix(tex1, zTextureSpec(SCREEN_TEXTURE, coord + offset), mix(0.0, 0.5, boozePower)); }