Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5bb5fb5
Beginning work on mono plates
FFValor Mar 15, 2026
2505b89
More work on mono plates
FFValor Mar 16, 2026
a3d91a2
Finalized porting of monolith plates.
FFValor Mar 21, 2026
3e8b026
small refactor to make plate carriers use container slots instead of …
NotLivyathan Mar 22, 2026
ded47da
Beginning port NVG/IR gog/functions for future use
FFValor Mar 24, 2026
81e0236
More work on IR/NVG system, porting sys from White
FFValor Mar 25, 2026
95d6573
Rename of ported IR/NV system to avoid SL conflict
FFValor Mar 25, 2026
5252854
Finalized porting IR/NV Huds, separate from SL's
FFValor Mar 25, 2026
bbb62cb
Beginning work on porting/added equipment.
FFValor Mar 28, 2026
446b5f5
More work w ported armor, need to fix PowerCell.cs
FFValor Mar 28, 2026
1ba0e94
Trimming ported hardsuit list.
FFValor Apr 5, 2026
d21db12
Readds cut sprites. Undo file changes for e-plates
FFValor Apr 5, 2026
94dbb55
Fixes battery breaking. To-do, fix e-plate examine
FFValor Apr 6, 2026
ac2e8c2
Adds % display of rechargeable e-plates.
FFValor Apr 6, 2026
914187d
tidying up extra RSI, adds merc medic hardsuit.
FFValor Apr 11, 2026
fe08829
Adds various exosuits, work on yml implementations
FFValor Apr 25, 2026
1687f3b
Work on wearable medibot
FFValor Apr 27, 2026
083d6ea
Fixing battery system
FFValor May 1, 2026
0176bda
More work on wearable medibot, pending assistance.
FFValor May 1, 2026
0603606
Adds ToggleableClothingSpeedModifierComponent, etc
FFValor May 3, 2026
a44c0a7
Work on suit abilities
FFValor May 4, 2026
0d645f9
To-do, start from scratch with wearable medibot.
FFValor May 4, 2026
b7fa988
Merge branch 'HardLightSector:master' into FFVEquipmentReworkV2
FFValor May 4, 2026
ac1d5a2
Reverting attempted medibot additions
FFValor May 6, 2026
3e6fa23
Merge branch 'FFVEquipmentReworkV2' of https://github.com/FFValor/FFV…
FFValor May 6, 2026
6f86847
adds more wip built in abilities. Needs .cs work
FFValor May 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
11 changes: 11 additions & 0 deletions Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Content.Client.Movement.Components;
using Content.Client.Viewport;
using Content.Shared.Camera;
using Content.Shared.Item.ItemToggle.Components;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Shared.Map;
Expand All @@ -22,6 +23,7 @@ public override void Initialize()
base.Initialize();

SubscribeLocalEvent<EyeCursorOffsetComponent, GetEyeOffsetEvent>(OnGetEyeOffsetEvent);
// SubscribeLocalEvent<EyeCursorOffsetComponent, ItemToggledEvent>(OnToggled);
}

private void OnGetEyeOffsetEvent(EntityUid uid, EyeCursorOffsetComponent component, ref GetEyeOffsetEvent args)
Expand All @@ -33,6 +35,15 @@ private void OnGetEyeOffsetEvent(EntityUid uid, EyeCursorOffsetComponent compone
args.Offset += offset.Value;
}

// private void OnToggled(EntityUid uid, EyeCursorOffsetComponent component, ref ItemToggledEvent args)
// {
// var offset = OffsetAfterMouse(uid, component);
// if (offset == null)
// return;

// args.Offset += offset.Value;
// }

public Vector2? OffsetAfterMouse(EntityUid uid, EyeCursorOffsetComponent? component)
{
// We need the main viewport where the game content is displayed, as certain UI layouts (e.g. Separated HUD) can make it a different size to the game window.
Expand Down
13 changes: 8 additions & 5 deletions Content.Client/Overlays/EquipmentHudSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,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 @@ -87,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