From c32ccf787b4af54bb1a8cd4c0982a3f58ef5b6c1 Mon Sep 17 00:00:00 2001 From: Matt Idzik Date: Fri, 17 Jul 2026 11:27:19 -0500 Subject: [PATCH] Greatly reduced how much the stealth shimmer changes over time for ninja and stealth boxes (#35575) * Added ShimmerFrequency to stealth component and shader * Reduced stealth shimmer frequency for ninja and stealth boxes * Undid pointless change. * cleanup --------- Co-authored-by: Pok245 <113675512+Pok27@users.noreply.github.com> --- Content.Client/Stealth/StealthSystem.cs | 1 + .../Stealth/Components/StealthComponent.cs | 44 +++++++++---------- Content.Shared/Stealth/SharedStealthSystem.cs | 3 +- .../Entities/Clothing/OuterClothing/suits.yml | 1 + .../Structures/Storage/Closets/big_boxes.yml | 1 + .../Structures/Storage/Crates/crates.yml | 1 + Resources/Textures/Shaders/stealth.swsl | 14 +++--- 7 files changed, 34 insertions(+), 31 deletions(-) diff --git a/Content.Client/Stealth/StealthSystem.cs b/Content.Client/Stealth/StealthSystem.cs index d31b998458..a33b11f0af 100644 --- a/Content.Client/Stealth/StealthSystem.cs +++ b/Content.Client/Stealth/StealthSystem.cs @@ -92,6 +92,7 @@ public sealed partial class StealthSystem : SharedStealthSystem _shader.SetParameter("reference", reference); _shader.SetParameter("visibility", visibility); + _shader.SetParameter("shimmer_frequency", component.ShimmerFrequency); visibility = MathF.Max(0, visibility); _sprite.SetColor((uid, args.Sprite), new Color(visibility, visibility, 1, 1)); diff --git a/Content.Shared/Stealth/Components/StealthComponent.cs b/Content.Shared/Stealth/Components/StealthComponent.cs index 1a8a647768..6d32fc2bfa 100644 --- a/Content.Shared/Stealth/Components/StealthComponent.cs +++ b/Content.Shared/Stealth/Components/StealthComponent.cs @@ -3,6 +3,7 @@ using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Stealth.Components; + /// /// Add this component to an entity that you want to be cloaked. /// It overlays a shader on the entity to give them an invisibility cloaked effect. @@ -16,25 +17,25 @@ public sealed partial class StealthComponent : Component /// /// Whether or not the stealth effect should currently be applied. /// - [DataField("enabled")] + [DataField] public bool Enabled = true; /// /// The creature will continue invisible at death. /// - [DataField("enabledOnDeath")] + [DataField] public bool EnabledOnDeath = true; /// /// Whether or not the entity previously had an interaction outline prior to cloaking. /// - [DataField("hadOutline")] + [DataField] public bool HadOutline; /// /// Minimum visibility before the entity becomes unexaminable (and thus no longer appears on context menus). /// - [DataField("examineThreshold")] + [DataField] public float ExamineThreshold = 0.5f; /// @@ -43,48 +44,47 @@ public sealed partial class StealthComponent : Component /// get the actual current visibility, use /// If you don't have anything else updating the stealth, this will just stay at a constant value, which can be useful. /// - [DataField("lastVisibility")] + [DataField] [Access(typeof(SharedStealthSystem), Other = AccessPermissions.None)] public float LastVisibility = 1; - /// /// Time at which was set. Null implies the entity is currently paused and not /// accumulating any visibility change. /// - [DataField("lastUpdate", customTypeSerializer:typeof(TimeOffsetSerializer))] + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] public TimeSpan? LastUpdated; /// /// Minimum visibility. Note that the visual effect caps out at -1, but this value is allowed to be larger or smaller. /// - [DataField("minVisibility")] + [DataField] public float MinVisibility = -1f; /// /// Maximum visibility. Note that the visual effect caps out at +1, but this value is allowed to be larger or smaller. /// - [DataField("maxVisibility")] + [DataField] public float MaxVisibility = 1.5f; /// - /// Localization string for how you'd like to describe this effect. + /// The frequency of the shimmer effect. 0 disables the shimmering, leaving only a static distortion. /// - [DataField("examinedDesc")] + [DataField] + public float ShimmerFrequency = 1f; + + /// + /// Localization string for how you'd like to describe this effect. + /// + [DataField] public string ExaminedDesc = "stealth-visual-effect"; } [Serializable, NetSerializable] -public sealed class StealthComponentState : ComponentState +public sealed class StealthComponentState(float stealthLevel, TimeSpan? lastUpdated, bool enabled, float shimmerFrequency) : ComponentState { - public readonly float Visibility; - public readonly TimeSpan? LastUpdated; - public readonly bool Enabled; - - public StealthComponentState(float stealthLevel, TimeSpan? lastUpdated, bool enabled) - { - Visibility = stealthLevel; - LastUpdated = lastUpdated; - Enabled = enabled; - } + public readonly float Visibility = stealthLevel; + public readonly TimeSpan? LastUpdated = lastUpdated; + public readonly bool Enabled = enabled; + public readonly float ShimmerFrequency = shimmerFrequency; } diff --git a/Content.Shared/Stealth/SharedStealthSystem.cs b/Content.Shared/Stealth/SharedStealthSystem.cs index 44f0d4ff11..c4ae0665c4 100644 --- a/Content.Shared/Stealth/SharedStealthSystem.cs +++ b/Content.Shared/Stealth/SharedStealthSystem.cs @@ -98,7 +98,7 @@ public abstract partial class SharedStealthSystem : EntitySystem private void OnStealthGetState(EntityUid uid, StealthComponent component, ref ComponentGetState args) { - args.State = new StealthComponentState(component.LastVisibility, component.LastUpdated, component.Enabled); + args.State = new StealthComponentState(component.LastVisibility, component.LastUpdated, component.Enabled, component.ShimmerFrequency); } private void OnStealthHandleState(EntityUid uid, StealthComponent component, ref ComponentHandleState args) @@ -109,6 +109,7 @@ public abstract partial class SharedStealthSystem : EntitySystem SetEnabled(uid, cast.Enabled, component); component.LastVisibility = cast.Visibility; component.LastUpdated = cast.LastUpdated; + component.ShimmerFrequency = cast.ShimmerFrequency; } private void OnMove(EntityUid uid, StealthOnMoveComponent component, ref MoveEvent args) diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml index c16f8a0801..29c4e78f89 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml @@ -179,6 +179,7 @@ - type: Stealth minVisibility: 0.1 lastVisibility: 0.1 + shimmerFrequency: 0.05 - type: PowerCellDraw drawRate: 1.8 # 200 seconds on the default cell - type: ToggleCellDraw diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/big_boxes.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/big_boxes.yml index 31e3e211d2..8439e76b4d 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/big_boxes.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/big_boxes.yml @@ -70,6 +70,7 @@ quiet: true - type: Stealth hadOutline: true + shimmerFrequency: 0.05 - type: StealthOnMove passiveVisibilityRate: -1 # very useful for going around the station concealed, if you start jitterstrafing you get seen movementVisibilityRate: 0.20 diff --git a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml index 16fbfb2e68..d7eb050100 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml @@ -865,6 +865,7 @@ components: - type: Stealth hadOutline: true + shimmerFrequency: 0.05 - type: StealthOnMove passiveVisibilityRate: -0.10 movementVisibilityRate: 0.10 diff --git a/Resources/Textures/Shaders/stealth.swsl b/Resources/Textures/Shaders/stealth.swsl index ba4a4db3c2..ba67429e63 100644 --- a/Resources/Textures/Shaders/stealth.swsl +++ b/Resources/Textures/Shaders/stealth.swsl @@ -2,6 +2,7 @@ light_mode unshaded; uniform sampler2D SCREEN_TEXTURE; uniform highp float visibility; // number between -1 and 1 +uniform highp float shimmer_frequency; uniform mediump vec2 reference; const mediump float time_scale = 0.25; @@ -17,20 +18,17 @@ void fragment() { // get distortion magnitude. hand crafted from a random jumble of trig functions highp vec2 coords = (FRAGCOORD.xy + reference) * distance_scale; - highp float w = sin(TIME + (coords.x + coords.y + 2.0*sin(TIME*time_scale) * sin(TIME*time_scale + coords.x - coords.y)) ); + highp float t = TIME * shimmer_frequency; + highp float w = sin(t + (coords.x + coords.y + 2.0 * sin(t * time_scale) * sin(t * time_scale + coords.x - coords.y))); // visualize distortion via: - // COLOR = vec4(w,w,w,1.0); + // COLOR = vec4(w, w, w, 1.0); w *= (3.0 + visibility * 2.0); - highp vec4 background = zTextureSpec(SCREEN_TEXTURE, ( FRAGCOORD.xy + vec2(w) ) * SCREEN_PIXEL_SIZE ); + highp vec4 background = zTextureSpec(SCREEN_TEXTURE, (FRAGCOORD.xy + vec2(w)) * SCREEN_PIXEL_SIZE); - lowp float alpha; - if (visibility>0.0) - alpha = sprite.a * visibility; - else - alpha = 0.0; + lowp float alpha = sprite.a * max(visibility, 0.0); COLOR.xyz = mix(background.xyz, sprite.xyz, alpha); COLOR.a = 1.0;