Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions Content.Client/Overlays/EquipmentHudSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public abstract class EquipmentHudSystem<T> : EntitySystem where T : IComponent
{
[Dependency] private readonly IPlayerManager _player = default!;

[ViewVariables]
protected bool IsActive;
protected virtual SlotFlags TargetSlots => ~SlotFlags.POCKET;

Expand All @@ -32,8 +33,8 @@ public override void Initialize()

SubscribeLocalEvent<T, RefreshEquipmentHudEvent<T>>(OnRefreshComponentHud);
SubscribeLocalEvent<T, InventoryRelayedEvent<RefreshEquipmentHudEvent<T>>>(OnRefreshEquipmentHud);
/*
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart); */

SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
}

private void Update(RefreshEquipmentHudEvent<T> ev)
Expand Down Expand Up @@ -86,14 +87,17 @@ private void OnCompUnequip(Entity<T> ent, ref GotUnequippedEvent args)
RefreshOverlay();
}

/* private void OnRoundRestart(RoundRestartCleanupEvent args)
private void OnRoundRestart(RoundRestartCleanupEvent args)
{
Deactivate();
} */
}

protected virtual void OnRefreshEquipmentHud(Entity<T> ent, ref InventoryRelayedEvent<RefreshEquipmentHudEvent<T>> args)
{
OnRefreshComponentHud(ent, ref args.Args);
// Goob edit start
args.Args.Active = true;
args.Args.Components.Add(ent);
// Goob edit end
}

protected virtual void OnRefreshComponentHud(Entity<T> ent, ref RefreshEquipmentHudEvent<T> args)
Expand Down
48 changes: 48 additions & 0 deletions Content.Client/_White/Overlays/BaseSwitchableOverlay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System.Numerics;
using Content.Shared._White.Overlays;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;

namespace Content.Client._White.Overlays;

public sealed class BaseSwitchableOverlay<TComp> : Overlay where TComp : SwitchableVisionOverlayComponent
{
[Dependency] private readonly IPrototypeManager _prototype = default!;

public override bool RequestScreenTexture => true;
public override OverlaySpace Space => OverlaySpace.WorldSpace;

private readonly ShaderInstance _shader;

public TComp? Comp = null;

public bool IsActive = true;

public BaseSwitchableOverlay()
{
IoCManager.InjectDependencies(this);
_shader = _prototype.Index<ShaderPrototype>("NVHud").InstanceUnique();
}

protected override void Draw(in OverlayDrawArgs args)
{
if (ScreenTexture is null || Comp is null || !IsActive)
return;

_shader.SetParameter("SCREEN_TEXTURE", ScreenTexture);
_shader.SetParameter("tint", Comp.Tint);
_shader.SetParameter("luminance_threshold", Comp.Strength);
_shader.SetParameter("noise_amount", Comp.Noise);

var worldHandle = args.WorldHandle;

var accumulator = Math.Clamp(Comp.PulseAccumulator, 0f, Comp.PulseTime);
var alpha = Comp.PulseTime <= 0f ? 1f : float.Lerp(1f, 0f, accumulator / Comp.PulseTime);

worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.UseShader(_shader);
worldHandle.DrawRect(args.WorldBounds, Comp.Color.WithAlpha(alpha));
worldHandle.UseShader(null);
}
}
166 changes: 166 additions & 0 deletions Content.Client/_White/Overlays/IRHudOverlay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
using System.Linq;
using System.Numerics;
using Content.Client.Stealth;
using Content.Shared._White.Overlays;
using Content.Shared.Body.Components;
using Content.Shared.Stealth.Components;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
using Robust.Shared.Map;
using Robust.Shared.Timing;

namespace Content.Client._White.Overlays;

public sealed class IRHudOverlay : Overlay
{
[Dependency] private readonly IEntityManager _entity = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IGameTiming _timing = default!;

private readonly TransformSystem _transform;
private readonly StealthSystem _stealth;
private readonly ContainerSystem _container;
private readonly SharedPointLightSystem _light;

public override bool RequestScreenTexture => true;
public override OverlaySpace Space => OverlaySpace.WorldSpace;

private readonly List<IRHudRenderEntry> _entries = [];

private EntityUid? _lightEntity;

public float LightRadius;

public IRHudComponent? Comp;

public IRHudOverlay()
{
IoCManager.InjectDependencies(this);

_container = _entity.System<ContainerSystem>();
_transform = _entity.System<TransformSystem>();
_stealth = _entity.System<StealthSystem>();
_light = _entity.System<SharedPointLightSystem>();

ZIndex = -1;
}

protected override void Draw(in OverlayDrawArgs args)
{
if (ScreenTexture is null || Comp is null)
return;

var worldHandle = args.WorldHandle;
var eye = args.Viewport.Eye;

if (eye == null)
return;

var player = _player.LocalEntity;

if (!_entity.TryGetComponent(player, out TransformComponent? playerXform))
return;

var accumulator = Math.Clamp(Comp.PulseAccumulator, 0f, Comp.PulseTime);
var alpha = Comp.PulseTime <= 0f ? 1f : float.Lerp(1f, 0f, accumulator / Comp.PulseTime);

// Thermal vision grants some night vision (clientside light)
if (LightRadius > 0)
{
_lightEntity ??= _entity.SpawnAttachedTo(null, playerXform.Coordinates);
_transform.SetParent(_lightEntity.Value, player.Value);
var light = _entity.EnsureComponent<PointLightComponent>(_lightEntity.Value);
_light.SetRadius(_lightEntity.Value, LightRadius, light);
_light.SetEnergy(_lightEntity.Value, alpha, light);
_light.SetColor(_lightEntity.Value, Comp.Color, light);
}
else
ResetLight();

var mapId = eye.Position.MapId;
var eyeRot = eye.Rotation;

_entries.Clear();
var entities = _entity.EntityQueryEnumerator<BodyComponent, SpriteComponent, TransformComponent>();
while (entities.MoveNext(out var uid, out var body, out var sprite, out var xform))
{
if (!CanSee(uid, sprite) || !body.ThermalVisibility)
continue;

var entity = uid;

if (_container.TryGetOuterContainer(uid, xform, out var container))
{
continue; // Mono

// Mono edit, Thermals don't reveal people in lockers
/*
var owner = container.Owner;
if (_entity.TryGetComponent<SpriteComponent>(owner, out var ownerSprite)
&& _entity.TryGetComponent<TransformComponent>(owner, out var ownerXform))
{
entity = owner;
sprite = ownerSprite;
xform = ownerXform;
}
*/
// Mono End
}

if (_entries.Any(e => e.Ent.Owner == entity))
continue;

_entries.Add(new IRHudRenderEntry((entity, sprite, xform), mapId, eyeRot));
}

foreach (var entry in _entries)
{
Render(entry.Ent, entry.Map, worldHandle, entry.EyeRot, Comp.Color, alpha);
}

worldHandle.SetTransform(Matrix3x2.Identity);
}

private void Render(Entity<SpriteComponent, TransformComponent> ent,
MapId? map,
DrawingHandleWorld handle,
Angle eyeRot,
Color color,
float alpha)
{
var (uid, sprite, xform) = ent;
if (xform.MapID != map || !CanSee(uid, sprite))
return;

var position = _transform.GetWorldPosition(xform);
var rotation = _transform.GetWorldRotation(xform);


var originalColor = sprite.Color;
sprite.Color = color.WithAlpha(alpha);
sprite.Render(handle, eyeRot, rotation, position: position);
sprite.Color = originalColor;
}

private bool CanSee(EntityUid uid, SpriteComponent sprite)
{
return sprite.Visible && (!_entity.TryGetComponent(uid, out StealthComponent? stealth) ||
_stealth.GetVisibility(uid, stealth) > 0.5f);
}

public void ResetLight(bool checkFirstTimePredicted = true)
{
if (_lightEntity == null || checkFirstTimePredicted && !_timing.IsFirstTimePredicted)
return;

_entity.DeleteEntity(_lightEntity);
_lightEntity = null;
}
}

public record struct IRHudRenderEntry(
Entity<SpriteComponent, TransformComponent> Ent,
MapId? Map,
Angle EyeRot);
112 changes: 112 additions & 0 deletions Content.Client/_White/Overlays/IRHudSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using Content.Client.Overlays;
using Content.Shared._White.Overlays;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
using Robust.Client.Graphics;

namespace Content.Client._White.Overlays;

public sealed class IRHudSystem : EquipmentHudSystem<IRHudComponent>
{
[Dependency] private readonly IOverlayManager _overlayMan = default!;

private IRHudOverlay _IRHudOverlay = default!;
private BaseSwitchableOverlay<IRHudComponent> _overlay = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<IRHudComponent, SwitchableOverlayToggledEvent>(OnToggle);

_IRHudOverlay = new IRHudOverlay();
_overlay = new BaseSwitchableOverlay<IRHudComponent>();
}

protected override void OnRefreshComponentHud(Entity<IRHudComponent> ent,
ref RefreshEquipmentHudEvent<IRHudComponent> args)
{
if (!ent.Comp.IsEquipment)
base.OnRefreshComponentHud(ent, ref args);
}

protected override void OnRefreshEquipmentHud(Entity<IRHudComponent> ent,
ref InventoryRelayedEvent<RefreshEquipmentHudEvent<IRHudComponent>> args)
{
if (ent.Comp.IsEquipment)
base.OnRefreshEquipmentHud(ent, ref args);
}

private void OnToggle(Entity<IRHudComponent> ent, ref SwitchableOverlayToggledEvent args)
{
RefreshOverlay();
}

protected override void UpdateInternal(RefreshEquipmentHudEvent<IRHudComponent> args)
{
base.UpdateInternal(args);
IRHudComponent? tvComp = null;
var lightRadius = 0f;
foreach (var comp in args.Components)
{
if (!comp.IsActive && (comp.PulseTime <= 0f || comp.PulseAccumulator >= comp.PulseTime))
continue;

if (tvComp == null)
tvComp = comp;
else if (!tvComp.DrawOverlay && comp.DrawOverlay)
tvComp = comp;
else if (tvComp.DrawOverlay == comp.DrawOverlay && tvComp.PulseTime > 0f && comp.PulseTime <= 0f)
tvComp = comp;

lightRadius = MathF.Max(lightRadius, comp.LightRadius);
}

UpdateIRHudOverlay(tvComp, lightRadius);
UpdateOverlay(tvComp);
}

protected override void DeactivateInternal()
{
base.DeactivateInternal();

_IRHudOverlay.ResetLight(false);
UpdateOverlay(null);
UpdateIRHudOverlay(null, 0f);
}

private void UpdateIRHudOverlay(IRHudComponent? comp, float lightRadius)
{
_IRHudOverlay.LightRadius = lightRadius;
_IRHudOverlay.Comp = comp;

switch (comp)
{
case not null when !_overlayMan.HasOverlay<IRHudOverlay>():
_overlayMan.AddOverlay(_IRHudOverlay);
break;
case null:
_overlayMan.RemoveOverlay(_IRHudOverlay);
_IRHudOverlay.ResetLight();
break;
}
}

private void UpdateOverlay(IRHudComponent? tvComp)
{
_overlay.Comp = tvComp;

switch (tvComp)
{
case { DrawOverlay: true } when !_overlayMan.HasOverlay<BaseSwitchableOverlay<IRHudComponent>>():
_overlayMan.AddOverlay(_overlay);
break;
case null or { DrawOverlay: false }:
_overlayMan.RemoveOverlay(_overlay);
break;
}

// Night vision overlay is prioritized
_overlay.IsActive = !_overlayMan.HasOverlay<BaseSwitchableOverlay<NVHudComponent>>();
}
}
Loading
Loading