forked from space-syndicate/space-station-14
-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Night and thermal vision device (#104)
* first * fix * fix * Apply suggestions from code review Co-authored-by: FN <[email protected]> * fix * Apply suggestions from code review * fix --------- Co-authored-by: AwareFoxy <[email protected]> Co-authored-by: FN <[email protected]>
- Loading branch information
1 parent
38269e4
commit e1a05f2
Showing
42 changed files
with
922 additions
and
3 deletions.
There are no files selected for viewing
47 changes: 47 additions & 0 deletions
47
Content.Client/_CorvaxNext/Overlays/BaseSwitchableOverlay.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
using Content.Shared._CorvaxNext.Overlays; | ||
using Robust.Client.Graphics; | ||
using Robust.Client.Player; | ||
using Robust.Shared.Enums; | ||
using Robust.Shared.Prototypes; | ||
using System.Numerics; | ||
|
||
namespace Content.Client._CorvaxNext.Overlays; | ||
|
||
public class BaseSwitchableOverlay<TComp> : Overlay | ||
where TComp : SwitchableOverlayComponent | ||
{ | ||
[Dependency] private readonly IPrototypeManager _prototype = default!; | ||
[Dependency] private readonly IPlayerManager _player = default!; | ||
[Dependency] private readonly IEntityManager _entity = default!; | ||
|
||
public override bool RequestScreenTexture => true; | ||
public override OverlaySpace Space => OverlaySpace.WorldSpace; | ||
private readonly ShaderInstance _shader; | ||
|
||
public BaseSwitchableOverlay() | ||
{ | ||
IoCManager.InjectDependencies(this); | ||
_shader = _prototype.Index<ShaderPrototype>("NightVision").Instance().Duplicate(); | ||
} | ||
|
||
protected override void Draw(in OverlayDrawArgs args) | ||
{ | ||
if (ScreenTexture is null | ||
|| _player.LocalEntity is null | ||
|| !_entity.TryGetComponent<TComp>(_player.LocalEntity.Value, out var component) | ||
|| !component.IsActive) | ||
return; | ||
|
||
_shader.SetParameter("SCREEN_TEXTURE", ScreenTexture); | ||
_shader.SetParameter("tint", component.Tint); | ||
_shader.SetParameter("luminance_threshold", component.Strength); | ||
_shader.SetParameter("noise_amount", component.Noise); | ||
|
||
var worldHandle = args.WorldHandle; | ||
|
||
worldHandle.SetTransform(Matrix3x2.Identity); | ||
worldHandle.UseShader(_shader); | ||
worldHandle.DrawRect(args.WorldBounds, component.Color); | ||
worldHandle.UseShader(null); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
using Content.Shared._CorvaxNext.Overlays; | ||
using Content.Shared.GameTicking; | ||
using Robust.Client.Graphics; | ||
using Robust.Client.Player; | ||
using Robust.Shared.Player; | ||
|
||
namespace Content.Client._CorvaxNext.Overlays; | ||
|
||
public sealed class NightVisionSystem : SwitchableOverlaySystem<NightVisionComponent, ToggleNightVisionEvent> | ||
{ | ||
[Dependency] private readonly IPlayerManager _player = default!; | ||
[Dependency] private readonly IOverlayManager _overlayMan = default!; | ||
[Dependency] private readonly ILightManager _lightManager = default!; | ||
|
||
private BaseSwitchableOverlay<NightVisionComponent> _overlay = default!; | ||
|
||
public override void Initialize() | ||
{ | ||
base.Initialize(); | ||
|
||
SubscribeLocalEvent<NightVisionComponent, PlayerAttachedEvent>(OnPlayerAttached); | ||
SubscribeLocalEvent<NightVisionComponent, PlayerDetachedEvent>(OnPlayerDetached); | ||
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRestart); | ||
|
||
_overlay = new BaseSwitchableOverlay<NightVisionComponent>(); | ||
} | ||
|
||
private void OnPlayerAttached(EntityUid uid, NightVisionComponent component, PlayerAttachedEvent args) | ||
{ | ||
if (!component.IsActive) | ||
return; | ||
|
||
UpdateVision(args.Player, component.IsActive); | ||
} | ||
|
||
private void OnPlayerDetached(EntityUid uid, NightVisionComponent component, PlayerDetachedEvent args) | ||
{ | ||
UpdateVision(args.Player, false); | ||
} | ||
|
||
private void OnRestart(RoundRestartCleanupEvent ev) | ||
{ | ||
_overlayMan.RemoveOverlay(_overlay); | ||
_lightManager.DrawLighting = true; | ||
} | ||
|
||
protected override void UpdateVision(EntityUid uid, bool active) | ||
{ | ||
if (_player.LocalSession?.AttachedEntity != uid) | ||
return; | ||
|
||
UpdateOverlay(active); | ||
UpdateNightVision(active); | ||
} | ||
|
||
private void UpdateVision(ICommonSession player, bool active) | ||
{ | ||
if (_player.LocalSession != player) | ||
return; | ||
|
||
UpdateOverlay(active); | ||
UpdateNightVision(active); | ||
} | ||
|
||
private void UpdateNightVision(bool active) | ||
{ | ||
_lightManager.DrawLighting = !active; | ||
} | ||
|
||
private void UpdateOverlay(bool active) | ||
{ | ||
if (_player.LocalEntity == null) | ||
{ | ||
_overlayMan.RemoveOverlay(_overlay); | ||
return; | ||
} | ||
|
||
active |= TryComp<NightVisionComponent>(_player.LocalEntity.Value, out var component) && component.IsActive; | ||
|
||
if (active) | ||
_overlayMan.AddOverlay(_overlay); | ||
else | ||
_overlayMan.RemoveOverlay(_overlay); | ||
} | ||
} |
130 changes: 130 additions & 0 deletions
130
Content.Client/_CorvaxNext/Overlays/ThermalVisionOverlay.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
using System.Linq; | ||
using System.Numerics; | ||
using Content.Client.Stealth; | ||
using Content.Shared._CorvaxNext.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; | ||
|
||
namespace Content.Client._CorvaxNext.Overlays; | ||
|
||
public sealed class ThermalVisionOverlay : Overlay | ||
{ | ||
[Dependency] private readonly IEntityManager _entity = default!; | ||
[Dependency] private readonly IPlayerManager _players = default!; | ||
|
||
private readonly TransformSystem _transform; | ||
private readonly OccluderSystem _occluder; | ||
private readonly StealthSystem _stealth; | ||
private readonly ContainerSystem _container; | ||
|
||
public override bool RequestScreenTexture => true; | ||
public override OverlaySpace Space => OverlaySpace.WorldSpace; | ||
|
||
private readonly List<ThermalVisionRenderEntry> _entries = []; | ||
|
||
public ThermalVisionOverlay() | ||
{ | ||
IoCManager.InjectDependencies(this); | ||
|
||
_container = _entity.System<ContainerSystem>(); | ||
_transform = _entity.System<TransformSystem>(); | ||
_occluder = _entity.System<OccluderSystem>(); | ||
_stealth = _entity.System<StealthSystem>(); | ||
|
||
ZIndex = -1; | ||
} | ||
|
||
protected override void Draw(in OverlayDrawArgs args) | ||
{ | ||
if (ScreenTexture is null | ||
|| _players.LocalEntity is null | ||
|| !_entity.TryGetComponent<ThermalVisionComponent>(_players.LocalEntity.Value, out var component) | ||
|| !component.IsActive) | ||
return; | ||
|
||
var worldHandle = args.WorldHandle; | ||
var eye = args.Viewport.Eye; | ||
|
||
if (eye is null) | ||
return; | ||
|
||
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)) | ||
continue; | ||
|
||
var entity = uid; | ||
|
||
if (_container.TryGetOuterContainer(uid, xform, out var container)) | ||
{ | ||
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; | ||
} | ||
} | ||
|
||
if (_entries.Any(e => e.Ent.Item1 == entity)) | ||
continue; | ||
|
||
_entries.Add(new ThermalVisionRenderEntry((entity, sprite, xform, body), mapId, eyeRot)); | ||
} | ||
|
||
foreach (var entry in _entries) | ||
{ | ||
Render(entry.Ent, entry.Map, worldHandle, entry.EyeRot); | ||
} | ||
|
||
worldHandle.SetTransform(Matrix3x2.Identity); | ||
} | ||
|
||
private void Render(Entity<SpriteComponent, TransformComponent, BodyComponent> ent, | ||
MapId? map, | ||
DrawingHandleWorld handle, | ||
Angle eyeRot) | ||
{ | ||
var (uid, sprite, xform, body) = ent; | ||
if (xform.MapID != map || HasOccluders(uid) || !CanSee(uid, sprite, body)) | ||
return; | ||
|
||
var position = _transform.GetWorldPosition(xform); | ||
var rotation = _transform.GetWorldRotation(xform); | ||
|
||
sprite.Render(handle, eyeRot, rotation, position: position); | ||
} | ||
|
||
private bool CanSee(EntityUid uid, SpriteComponent sprite, BodyComponent body) | ||
{ | ||
return sprite.Visible | ||
&& !_entity.HasComponent<ThermalInvisibilityComponent>(uid) | ||
&& (!_entity.TryGetComponent(uid, out StealthComponent? stealth) | ||
|| _stealth.GetVisibility(uid, stealth) > 0.5f); | ||
} | ||
|
||
private bool HasOccluders(EntityUid uid) | ||
{ | ||
var mapCoordinates = _transform.GetMapCoordinates(uid); | ||
var occluders = _occluder.QueryAabb(mapCoordinates.MapId, | ||
Box2.CenteredAround(mapCoordinates.Position, new Vector2(0.3f, 0.3f))); | ||
|
||
return occluders.Any(o => o.Component.Enabled); | ||
} | ||
} | ||
|
||
public record struct ThermalVisionRenderEntry( | ||
(EntityUid, SpriteComponent, TransformComponent, BodyComponent) Ent, | ||
MapId? Map, | ||
Angle EyeRot); |
81 changes: 81 additions & 0 deletions
81
Content.Client/_CorvaxNext/Overlays/ThermalVisionSystem.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
using Content.Shared._CorvaxNext.Overlays; | ||
using Content.Shared.GameTicking; | ||
using Robust.Client.Graphics; | ||
using Robust.Client.Player; | ||
using Robust.Shared.Player; | ||
|
||
namespace Content.Client._CorvaxNext.Overlays; | ||
|
||
public sealed class ThermalVisionSystem : SwitchableOverlaySystem<ThermalVisionComponent, ToggleThermalVisionEvent> | ||
{ | ||
[Dependency] private readonly IPlayerManager _player = default!; | ||
[Dependency] private readonly IOverlayManager _overlayMan = default!; | ||
|
||
private ThermalVisionOverlay _thermalOverlay = default!; | ||
private BaseSwitchableOverlay<ThermalVisionComponent> _overlay = default!; | ||
|
||
public override void Initialize() | ||
{ | ||
base.Initialize(); | ||
|
||
SubscribeLocalEvent<ThermalVisionComponent, PlayerAttachedEvent>(OnPlayerAttached); | ||
SubscribeLocalEvent<ThermalVisionComponent, PlayerDetachedEvent>(OnPlayerDetached); | ||
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRestart); | ||
|
||
_thermalOverlay = new ThermalVisionOverlay(); | ||
_overlay = new BaseSwitchableOverlay<ThermalVisionComponent>(); | ||
} | ||
|
||
private void OnPlayerAttached(EntityUid uid, ThermalVisionComponent component, PlayerAttachedEvent args) | ||
{ | ||
if (!component.IsActive) | ||
return; | ||
|
||
UpdateVision(args.Player, component.IsActive); | ||
} | ||
|
||
private void OnPlayerDetached(EntityUid uid, ThermalVisionComponent component, PlayerDetachedEvent args) | ||
{ | ||
UpdateVision(args.Player, false); | ||
} | ||
|
||
private void OnRestart(RoundRestartCleanupEvent ev) | ||
{ | ||
_overlayMan.RemoveOverlay(_thermalOverlay); | ||
_overlayMan.RemoveOverlay(_overlay); | ||
} | ||
|
||
protected override void UpdateVision(EntityUid uid, bool active) | ||
{ | ||
if (_player.LocalSession?.AttachedEntity != uid) | ||
return; | ||
|
||
UpdateOverlay(active, _thermalOverlay); | ||
UpdateOverlay(active, _overlay); | ||
} | ||
|
||
private void UpdateVision(ICommonSession player, bool active) | ||
{ | ||
if (_player.LocalSession != player) | ||
return; | ||
|
||
UpdateOverlay(active, _thermalOverlay); | ||
UpdateOverlay(active, _overlay); | ||
} | ||
|
||
private void UpdateOverlay(bool active, Overlay overlay) | ||
{ | ||
if (_player.LocalEntity == null) | ||
{ | ||
_overlayMan.RemoveOverlay(overlay); | ||
return; | ||
} | ||
|
||
active |= TryComp<ThermalVisionComponent>(_player.LocalEntity.Value, out var component) && component.IsActive; | ||
|
||
if (active) | ||
_overlayMan.AddOverlay(overlay); | ||
else | ||
_overlayMan.RemoveOverlay(overlay); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
using Content.Shared._CorvaxNext.Overlays; | ||
|
||
namespace Content.Server._CorvaxNext.Overlays; | ||
|
||
public sealed class NightVisionSystem : SwitchableOverlaySystem<NightVisionComponent, ToggleNightVisionEvent>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
using Content.Shared._CorvaxNext.Overlays; | ||
|
||
namespace Content.Server._CorvaxNext.Overlays; | ||
|
||
public sealed class ThermalVisionSystem : SwitchableOverlaySystem<ThermalVisionComponent, ToggleThermalVisionEvent>; |
14 changes: 14 additions & 0 deletions
14
Content.Shared/_CorvaxNext/Clothing/Components/ClothingGrantComponentComponent.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using Robust.Shared.Prototypes; | ||
|
||
namespace Content.Shared._CorvaxNext.Clothing | ||
{ | ||
[RegisterComponent] | ||
public sealed partial class ClothingGrantComponentComponent : Component | ||
{ | ||
[DataField("component")] | ||
[AlwaysPushInheritance] | ||
public ComponentRegistry Components { get; private set; } = new(); | ||
|
||
public bool IsActive = false; | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
Content.Shared/_CorvaxNext/Clothing/Components/ClothingGrantTagComponent.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
namespace Content.Shared._CorvaxNext.Clothing; | ||
|
||
[RegisterComponent] | ||
public sealed partial class ClothingGrantTagComponent : Component | ||
{ | ||
[DataField("tag")] | ||
public string Tag = ""; | ||
|
||
public bool IsActive = false; | ||
} |
Oops, something went wrong.