using Robust.Client.Graphics;
-using Robust.Client.Player;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
public sealed partial class BlackAndWhiteOverlay : Overlay
{
- [Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- [Dependency] private readonly IPlayerManager _playerManager = default!;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public override bool RequestScreenTexture => true;
ZIndex = 10; // draw this over the DamageOverlay, RainbowOverlay etc.
}
- protected override bool BeforeDraw(in OverlayDrawArgs args)
- {
- if (!_entityManager.TryGetComponent(_playerManager.LocalEntity, out EyeComponent? eyeComp))
- return false;
-
- if (args.Viewport.Eye != eyeComp.Eye)
- return false;
-
- return true;
- }
-
protected override void Draw(in OverlayDrawArgs args)
{
if (ScreenTexture == null)
using Content.Shared.Inventory.Events;
using Content.Shared.Overlays;
using Robust.Client.Graphics;
-using Robust.Client.Player;
namespace Content.Client.Overlays;
--- /dev/null
+using Robust.Client.Graphics;
+using Robust.Shared.Enums;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Overlays;
+
+public sealed partial class NoirOverlay : Overlay
+{
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+
+ public override OverlaySpace Space => OverlaySpace.WorldSpace;
+ public override bool RequestScreenTexture => true;
+ private readonly ShaderInstance _noirShader;
+
+ public NoirOverlay()
+ {
+ IoCManager.InjectDependencies(this);
+ _noirShader = _prototypeManager.Index<ShaderPrototype>("Noir").InstanceUnique();
+ ZIndex = 9; // draw this over the DamageOverlay, RainbowOverlay etc, but before the black and white shader
+ }
+
+ protected override void Draw(in OverlayDrawArgs args)
+ {
+ if (ScreenTexture == null)
+ return;
+
+ var handle = args.WorldHandle;
+ _noirShader.SetParameter("SCREEN_TEXTURE", ScreenTexture);
+ handle.UseShader(_noirShader);
+ handle.DrawRect(args.WorldBounds, Color.White);
+ handle.UseShader(null);
+ }
+}
--- /dev/null
+using Content.Shared.Inventory.Events;
+using Content.Shared.Overlays;
+using Robust.Client.Graphics;
+
+namespace Content.Client.Overlays;
+
+public sealed partial class NoirOverlaySystem : EquipmentHudSystem<NoirOverlayComponent>
+{
+ [Dependency] private readonly IOverlayManager _overlayMan = default!;
+
+ private NoirOverlay _overlay = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ _overlay = new();
+ }
+
+ protected override void UpdateInternal(RefreshEquipmentHudEvent<NoirOverlayComponent> component)
+ {
+ base.UpdateInternal(component);
+
+ _overlayMan.AddOverlay(_overlay);
+ }
+
+ protected override void DeactivateInternal()
+ {
+ base.DeactivateInternal();
+
+ _overlayMan.RemoveOverlay(_overlay);
+ }
+}
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowSyndicateIconsComponent>>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowCriminalRecordIconsComponent>>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<BlackAndWhiteOverlayComponent>>(RefRelayInventoryEvent);
+ SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<NoirOverlayComponent>>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, GetVerbsEvent<EquipmentVerb>>(OnGetEquipmentVerbs);
SubscribeLocalEvent<InventoryComponent, GetVerbsEvent<InnateVerb>>(OnGetInnateVerbs);
--- /dev/null
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Overlays;
+
+/// <summary>
+/// Makes the entity see everything with a sin city shader (everything in black and white, except red) by adding an overlay.
+/// When added to a clothing item it will also grant the wearer the same overlay.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class NoirOverlayComponent : Component;
- type: FlashImmunity
- type: EyeProtection
protectionTime: 5
- - type: BlackAndWhiteOverlay
+ - type: NoirOverlay
- type: Tag
tags:
- HamsterWearable
kind: source
path: "/Textures/Shaders/rainbow.swsl"
+# sin city effect: everything is greyscale, except red
+- type: shader
+ id: Noir
+ kind: source
+ path: "/Textures/Shaders/noir.swsl"
+
- type: shader
id: CameraStatic
kind: source
- type: shader
id: Hologram
kind: source
- path: "/Textures/Shaders/hologram.swsl"
\ No newline at end of file
+ path: "/Textures/Shaders/hologram.swsl"
--- /dev/null
+// Sin City style shader.
+// Makes everything black and white, but keeps red colors.
+
+uniform sampler2D SCREEN_TEXTURE;
+// Sensitivity, in degrees.
+const highp float RedHueRange = 5;
+const highp float MinSaturation = 0.4;
+
+// https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB
+highp vec3 rgb2hsv(highp vec3 c) {
+ highp float xMax = max(c.r, max(c.g, c.b));
+ highp float xMin = min(c.r, min(c.g, c.b));
+ highp float delta = xMax - xMin;
+
+ highp float hue = 0.0;
+ if (delta > 0.0) {
+ if (xMax == c.r) {
+ hue = mod((c.g - c.b) / delta, 6.0);
+ } else if (xMax == c.g) {
+ hue = (c.b - c.r) / delta + 2.0;
+ } else {
+ hue = (c.r - c.g) / delta + 4.0;
+ }
+ hue *= 60.0;
+ if (hue < 0.0) hue += 360.0;
+ }
+
+ highp float sat = (xMax == 0.0) ? 0.0 : delta / xMax;
+ return vec3(hue, sat, xMax);
+}
+
+void fragment() {
+ highp vec4 color = zTextureSpec(SCREEN_TEXTURE, UV);
+ highp vec3 gray = vec3(zGrayscale(color.rgb));
+ highp vec3 hsv = rgb2hsv(color.rgb);
+
+ // Red is near 0 or 360 in hue
+ bool is_red = hsv.x < RedHueRange || hsv.x > (360.0 - RedHueRange);
+ bool saturated_enough = hsv.y > MinSaturation; // Avoid desaturated pinks/greys
+
+ if (is_red && saturated_enough) {
+ COLOR = color;
+ } else {
+ COLOR = vec4(gray, color.a);
+ }
+}