Night And Thermal Vision (#1462)

<!--
This is a semi-strict format, you can add/remove sections as needed but
the order/format should be kept the same
Remove these comments before submitting
-->

# Description

<!--
Explain this PR in as much detail as applicable

Some example prompts to consider:
How might this affect the game? The codebase?
What might be some alternatives to this?
How/Who does this benefit/hurt [the game/codebase]?
-->

Port from WWDP. Refactor from
[Goob](https://github.com/Goob-Station/Goob-Station/pull/1251)

---

<!--
This is default collapsed, readers click to expand it and see all your
media
The PR media section can get very large at times, so this is a good way
to keep it clean
The title is written using HTML tags
The title must be within the <summary> tags or you won't see it
-->

<details><summary><h1>Media</h1></summary>
<p>

![image](https://github.com/user-attachments/assets/ee60eb59-0432-477e-8aee-25a56032e58e)

Night vision goggles:

![image](https://github.com/user-attachments/assets/f9269e1a-97ed-456f-bd45-7015b723fb3e)

Zealot's blindfold:

![image](https://github.com/user-attachments/assets/7c60011e-1d0a-4fb2-92cb-fded8c747555)

Animal vision:

![image](https://github.com/user-attachments/assets/14f1153d-b771-4316-9faa-fee951d884ce)

Thermal vision goggles:

![image](https://github.com/user-attachments/assets/b167ef8b-e1b7-477e-a08d-b217fd2e38c5)

Deathsquad helmet:

![image](https://github.com/user-attachments/assets/2e15ab15-6d23-45c2-b51e-3c16dc3a135d)

Xeno vision:

![image](https://github.com/user-attachments/assets/1677b69e-013f-464a-baaf-af3b5f1b6488)

</p>
</details>

---

# Changelog

<!--
You can add an author after the `🆑` to change the name that appears
in the changelog (ex: `🆑 Death`)
Leaving it blank will default to your GitHub display name
This includes all available types for the changelog
-->

🆑 @Aviu00, Spatison, @PuroSlavKing
- add: Added night vision goggle
- add: Added thermal vision goggle
- add: Deathsquad helmet now grants night and thermal vision.
- add: Ninja visor now grants night vision.
- tweak: Some animals have gained night vision.
- tweak: Xenos have gained night vision.

---------

Signed-off-by: Spatison <137375981+Spatison@users.noreply.github.com>
Co-authored-by: PuroSlavKing <103608145+puroslavking@users.noreply.github.com>

(cherry picked from commit 0f481422a54a197923f4bf03db1b5733e481965f)
This commit is contained in:
Spatison
2025-01-10 07:41:32 +03:00
parent 7bb1c2912a
commit 2e649fd74b
81 changed files with 1384 additions and 9 deletions

View File

@@ -102,7 +102,7 @@ public abstract class EquipmentHudSystem<T> : EntitySystem where T : IComponent
args.Components.Add(component);
}
private void RefreshOverlay(EntityUid uid)
protected void RefreshOverlay(EntityUid uid)
{
if (uid != _player.LocalSession?.AttachedEntity)
return;

View File

@@ -0,0 +1,48 @@
using System.Numerics;
using Content.Shared.Overlays.Switchable;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
namespace Content.Client.Overlays.Switchable;
public sealed class BaseSwitchableOverlay<TComp> : Overlay where TComp : SwitchableOverlayComponent
{
[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>("NightVision").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);
}
}

View File

@@ -0,0 +1,87 @@
using Content.Shared.Inventory.Events;
using Content.Shared.Overlays.Switchable;
using Robust.Client.Graphics;
namespace Content.Client.Overlays.Switchable;
public sealed class NightVisionSystem : EquipmentHudSystem<NightVisionComponent>
{
[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, SwitchableOverlayToggledEvent>(OnToggle);
_overlay = new BaseSwitchableOverlay<NightVisionComponent>();
}
private void OnToggle(Entity<NightVisionComponent> ent, ref SwitchableOverlayToggledEvent args)
{
RefreshOverlay(args.User);
}
protected override void UpdateInternal(RefreshEquipmentHudEvent<NightVisionComponent> args)
{
base.UpdateInternal(args);
var active = false;
NightVisionComponent? nvComp = null;
foreach (var comp in args.Components)
{
if (comp.IsActive || comp.PulseTime > 0f && comp.PulseAccumulator < comp.PulseTime)
active = true;
else
continue;
if (comp.DrawOverlay)
{
if (nvComp == null)
nvComp = comp;
else if (nvComp.PulseTime > 0f && comp.PulseTime <= 0f)
nvComp = comp;
}
if (active && nvComp is { PulseTime: <= 0 })
break;
}
UpdateNightVision(active);
UpdateOverlay(nvComp);
}
protected override void DeactivateInternal()
{
base.DeactivateInternal();
UpdateNightVision(false);
UpdateOverlay(null);
}
private void UpdateNightVision(bool active)
{
_lightManager.DrawLighting = !active;
}
private void UpdateOverlay(NightVisionComponent? nvComp)
{
_overlay.Comp = nvComp;
switch (nvComp)
{
case not null when !_overlayMan.HasOverlay<BaseSwitchableOverlay<NightVisionComponent>>():
_overlayMan.AddOverlay(_overlay);
break;
case null:
_overlayMan.RemoveOverlay(_overlay);
break;
}
if (_overlayMan.TryGetOverlay<BaseSwitchableOverlay<ThermalVisionComponent>>(out var overlay))
overlay.IsActive = nvComp == null;
}
}

View File

@@ -0,0 +1,159 @@
using System.Linq;
using System.Numerics;
using Content.Client.Stealth;
using Content.Shared.Body.Components;
using Content.Shared.Overlays.Switchable;
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.Overlays.Switchable;
public sealed class ThermalVisionOverlay : 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<ThermalVisionRenderEntry> _entries = [];
private EntityUid? _lightEntity;
public float LightRadius;
public ThermalVisionComponent? Comp;
public ThermalVisionOverlay()
{
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))
{
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.Owner == entity))
continue;
_entries.Add(new ThermalVisionRenderEntry((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()
{
if (_lightEntity == null || !_timing.IsFirstTimePredicted)
return;
_entity.DeleteEntity(_lightEntity);
_lightEntity = null;
}
}
public record struct ThermalVisionRenderEntry(
Entity<SpriteComponent, TransformComponent> Ent,
MapId? Map,
Angle EyeRot);

View File

@@ -0,0 +1,95 @@
using Content.Shared.Inventory.Events;
using Content.Shared.Overlays.Switchable;
using Robust.Client.Graphics;
namespace Content.Client.Overlays.Switchable;
public sealed class ThermalVisionSystem : EquipmentHudSystem<ThermalVisionComponent>
{
[Dependency] private readonly IOverlayManager _overlayMan = default!;
private ThermalVisionOverlay _thermalOverlay = default!;
private BaseSwitchableOverlay<ThermalVisionComponent> _overlay = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ThermalVisionComponent, SwitchableOverlayToggledEvent>(OnToggle);
_thermalOverlay = new ThermalVisionOverlay();
_overlay = new BaseSwitchableOverlay<ThermalVisionComponent>();
}
private void OnToggle(Entity<ThermalVisionComponent> ent, ref SwitchableOverlayToggledEvent args)
{
RefreshOverlay(args.User);
}
protected override void UpdateInternal(RefreshEquipmentHudEvent<ThermalVisionComponent> args)
{
base.UpdateInternal(args);
ThermalVisionComponent? 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);
}
UpdateThermalOverlay(tvComp, lightRadius);
UpdateOverlay(tvComp);
}
protected override void DeactivateInternal()
{
base.DeactivateInternal();
UpdateOverlay(null);
UpdateThermalOverlay(null, 0f);
}
private void UpdateThermalOverlay(ThermalVisionComponent? comp, float lightRadius)
{
_thermalOverlay.LightRadius = lightRadius;
_thermalOverlay.Comp = comp;
switch (comp)
{
case not null when !_overlayMan.HasOverlay<ThermalVisionOverlay>():
_overlayMan.AddOverlay(_thermalOverlay);
break;
case null:
_overlayMan.RemoveOverlay(_thermalOverlay);
_thermalOverlay.ResetLight();
break;
}
}
private void UpdateOverlay(ThermalVisionComponent? tvComp)
{
_overlay.Comp = tvComp;
switch (tvComp)
{
case { DrawOverlay: true } when !_overlayMan.HasOverlay<BaseSwitchableOverlay<ThermalVisionComponent>>():
_overlayMan.AddOverlay(_overlay);
break;
case null or { DrawOverlay: false }:
_overlayMan.RemoveOverlay(_overlay);
break;
}
// Night vision overlay is prioritized
_overlay.IsActive = !_overlayMan.HasOverlay<BaseSwitchableOverlay<NightVisionComponent>>();
}
}

View File

@@ -14,6 +14,7 @@ using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -23,6 +24,8 @@ public abstract class SharedActionsSystem : EntitySystem
{
[Dependency] protected readonly IGameTiming GameTiming = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!;
@@ -774,7 +777,7 @@ public abstract class SharedActionsSystem : EntitySystem
if (!ResolveActionData(actionId, ref action))
return false;
DebugTools.Assert(action.Container == null ||
DebugTools.Assert(_net.IsClient || action.Container == null ||
(TryComp(action.Container, out ActionsContainerComponent? containerComp)
&& containerComp.Container.Contains(actionId)));

View File

@@ -41,4 +41,7 @@ public sealed partial class BodyComponent : Component
[ViewVariables]
[DataField, AutoNetworkedField]
public HashSet<EntityUid> LegEntities = new();
[DataField, AutoNetworkedField]
public bool ThermalVisibility = true;
}

View File

@@ -21,6 +21,8 @@ using Content.Shared.Temperature;
using Content.Shared.Verbs;
using Content.Shared.Weapons.Ranged.Events;
using Content.Shared.Chat;
using Content.Shared.Overlays.Switchable;
namespace Content.Shared.Inventory;
@@ -65,6 +67,8 @@ public partial class InventorySystem
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowMindShieldIconsComponent>>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowSyndicateIconsComponent>>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowCriminalRecordIconsComponent>>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<NightVisionComponent>>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ThermalVisionComponent>>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, GetVerbsEvent<EquipmentVerb>>(OnGetEquipmentVerbs);
}

View File

@@ -0,0 +1,16 @@
namespace Content.Shared.Overlays;
public abstract partial class BaseOverlayComponent : Component
{
[DataField, ViewVariables(VVAccess.ReadOnly)]
public virtual Vector3 Tint { get; set; } = new(0.3f, 0.3f, 0.3f);
[DataField, ViewVariables(VVAccess.ReadOnly)]
public virtual float Strength { get; set; } = 2f;
[DataField, ViewVariables(VVAccess.ReadOnly)]
public virtual float Noise { get; set; } = 0.5f;
[DataField, ViewVariables(VVAccess.ReadOnly)]
public virtual Color Color { get; set; } = Color.White;
}

View File

@@ -0,0 +1,14 @@
using Content.Shared.Actions;
using Robust.Shared.GameStates;
namespace Content.Shared.Overlays.Switchable;
[RegisterComponent, NetworkedComponent]
public sealed partial class NightVisionComponent : SwitchableOverlayComponent
{
public override string? ToggleAction { get; set; } = "ToggleNightVision";
public override Color Color { get; set; } = Color.FromHex("#98FB98");
}
public sealed partial class ToggleNightVisionEvent : InstantActionEvent;

View File

@@ -0,0 +1,3 @@
namespace Content.Shared.Overlays.Switchable;
public sealed class NightVisionSystem : SwitchableOverlaySystem<NightVisionComponent, ToggleNightVisionEvent>;

View File

@@ -0,0 +1,48 @@
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared.Overlays.Switchable;
public abstract partial class SwitchableOverlayComponent : BaseOverlayComponent
{
[DataField, AutoNetworkedField]
public bool IsActive;
[DataField]
public bool DrawOverlay = true;
/// <summary>
/// If it is greater than 0, overlay isn't toggled but pulsed instead
/// </summary>
[DataField]
public float PulseTime;
[ViewVariables(VVAccess.ReadOnly)]
public float PulseAccumulator;
[DataField]
public virtual SoundSpecifier? ActivateSound { get; set; } =
new SoundPathSpecifier("/Audio/Items/Goggles/activate.ogg");
[DataField]
public virtual SoundSpecifier? DeactivateSound { get; set; } =
new SoundPathSpecifier("/Audio/Items/Goggles/deactivate.ogg");
[DataField]
public virtual string? ToggleAction { get; set; }
[ViewVariables]
public EntityUid? ToggleActionEntity;
}
[Serializable, NetSerializable]
public sealed class SwitchableVisionOverlayComponentState : IComponentState
{
public Color Color;
public bool IsActive;
public SoundSpecifier? ActivateSound;
public SoundSpecifier? DeactivateSound;
public EntProtoId? ToggleAction;
public float LightRadius;
}

View File

@@ -0,0 +1,166 @@
using Content.Shared.Actions;
using Content.Shared.Inventory;
using Robust.Shared.Audio.Systems;
using Robust.Shared.GameStates;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Timing;
namespace Content.Shared.Overlays.Switchable;
public abstract class SwitchableOverlaySystem<TComp, TEvent> : EntitySystem
where TComp : SwitchableOverlayComponent
where TEvent : InstantActionEvent
{
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly INetManager _net = default!;
public override void Initialize()
{
SubscribeLocalEvent<TComp, TEvent>(OnToggle);
SubscribeLocalEvent<TComp, ComponentInit>(OnInit);
SubscribeLocalEvent<TComp, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<TComp, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<TComp, GetItemActionsEvent>(OnGetItemActions);
SubscribeLocalEvent<TComp, ComponentGetState>(OnGetState);
SubscribeLocalEvent<TComp, ComponentHandleState>(OnHandleState);
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
if (_net.IsClient)
ActiveTick(frameTime);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
if (_net.IsServer)
ActiveTick(frameTime);
}
private void ActiveTick(float frameTime)
{
var query = EntityQueryEnumerator<TComp>();
while (query.MoveNext(out var uid, out var comp))
{
if (comp.PulseTime <= 0f || comp.PulseAccumulator >= comp.PulseTime)
continue;
comp.PulseAccumulator += frameTime;
if (comp.PulseAccumulator < comp.PulseTime)
continue;
Toggle(uid, comp, false, false);
RaiseSwitchableOverlayToggledEvent(uid, uid, comp.IsActive);
RaiseSwitchableOverlayToggledEvent(uid, Transform(uid).ParentUid, comp.IsActive);
}
}
private void OnGetState(EntityUid uid, TComp component, ref ComponentGetState args)
{
args.State = new SwitchableVisionOverlayComponentState
{
Color = component.Color,
IsActive = component.IsActive,
ActivateSound = component.ActivateSound,
DeactivateSound = component.DeactivateSound,
ToggleAction = component.ToggleAction,
LightRadius = component is ThermalVisionComponent thermal ? thermal.LightRadius : 0f,
};
}
private void OnHandleState(EntityUid uid, TComp component, ref ComponentHandleState args)
{
if (args.Current is not SwitchableVisionOverlayComponentState state)
return;
component.Color = state.Color;
component.ActivateSound = state.ActivateSound;
component.DeactivateSound = state.DeactivateSound;
if (component.ToggleAction != state.ToggleAction)
{
_actions.RemoveAction(uid, component.ToggleActionEntity);
component.ToggleAction = state.ToggleAction;
if (component.ToggleAction != null)
_actions.AddAction(uid, ref component.ToggleActionEntity, component.ToggleAction);
}
if (component is ThermalVisionComponent thermal)
thermal.LightRadius = state.LightRadius;
if (component.IsActive == state.IsActive)
return;
component.IsActive = state.IsActive;
RaiseSwitchableOverlayToggledEvent(uid, uid, component.IsActive);
RaiseSwitchableOverlayToggledEvent(uid, Transform(uid).ParentUid, component.IsActive);
}
private void OnGetItemActions(Entity<TComp> ent, ref GetItemActionsEvent args)
{
if (ent.Comp.ToggleAction != null && args.SlotFlags is not SlotFlags.POCKET and not null)
args.AddAction(ref ent.Comp.ToggleActionEntity, ent.Comp.ToggleAction);
}
private void OnShutdown(EntityUid uid, TComp component, ComponentShutdown args)
{
_actions.RemoveAction(uid, component.ToggleActionEntity);
}
private void OnInit(EntityUid uid, TComp component, ComponentInit args)
{
component.PulseAccumulator = component.PulseTime;
}
private void OnMapInit(EntityUid uid, TComp component, MapInitEvent args)
{
if (component.ToggleActionEntity == null && component.ToggleAction != null)
_actions.AddAction(uid, ref component.ToggleActionEntity, component.ToggleAction);
}
private void OnToggle(EntityUid uid, TComp component, TEvent args)
{
Toggle(uid, component, !component.IsActive);
RaiseSwitchableOverlayToggledEvent(uid, args.Performer, component.IsActive);
args.Handled = true;
}
private void Toggle(EntityUid uid, TComp component, bool activate, bool playSound = true)
{
if (playSound && _net.IsClient && _timing.IsFirstTimePredicted)
{
_audio.PlayEntity(activate ? component.ActivateSound : component.DeactivateSound,
Filter.Local(),
uid,
false);
}
if (component.PulseTime > 0f)
{
component.PulseAccumulator = activate ? 0f : component.PulseTime;
return;
}
component.IsActive = activate;
Dirty(uid, component);
}
private void RaiseSwitchableOverlayToggledEvent(EntityUid uid, EntityUid user, bool activated)
{
var ev = new SwitchableOverlayToggledEvent(user, activated);
RaiseLocalEvent(uid, ref ev);
}
}
[ByRefEvent]
public record struct SwitchableOverlayToggledEvent(EntityUid User, bool Activated);

View File

@@ -0,0 +1,17 @@
using Content.Shared.Actions;
using Robust.Shared.GameStates;
namespace Content.Shared.Overlays.Switchable;
[RegisterComponent, NetworkedComponent]
public sealed partial class ThermalVisionComponent : SwitchableOverlayComponent
{
public override string? ToggleAction { get; set; } = "ToggleThermalVision";
public override Color Color { get; set; } = Color.FromHex("#F84742");
[DataField]
public float LightRadius = 5f;
}
public sealed partial class ToggleThermalVisionEvent : InstantActionEvent;

View File

@@ -0,0 +1,3 @@
namespace Content.Shared.Overlays.Switchable;
public sealed class SharedThermalVisionSystem : SwitchableOverlaySystem<ThermalVisionComponent, ToggleThermalVisionEvent>;

Binary file not shown.

View File

@@ -0,0 +1,9 @@
- files: ["activate.ogg"]
license: "CC-BY-NC-SA-3.0"
copyright: "Taken from TGstation"
source: "https://github.com/tgstation/tgstation"
- files: ["deactivate.ogg"]
license: "CC-BY-NC-SA-3.0"
copyright: "Taken from TGstation"
source: "https://github.com/tgstation/tgstation"

Binary file not shown.

View File

@@ -53,6 +53,8 @@ research-technology-advanced-anomaly-research = Advanced Anomaly Research
research-technology-rped = Rapid Part Exchange
research-technology-super-parts = Super Parts
research-technology-deterrence = Deterrence Technologies
research-technology-night-vision = Night vision
research-technology-thermal-vision = Thermal vision
research-technology-janitorial-equipment = Janitorial Equipment
research-technology-laundry-tech = Laundry Tech

View File

@@ -329,6 +329,12 @@ uplink-hardsuit-syndieelite-desc = An elite version of the blood-red hardsuit, w
uplink-clothing-outer-hardsuit-juggernaut-name = Cybersun Juggernaut Suit
uplink-clothing-outer-hardsuit-juggernaut-desc = Hyper resilient armor made of materials tested in the Tau chromosphere facility. The only thing that's going to be slowing you down is this suit... and tasers.
uplink-night-vision-name = Night vision goggles
uplink-night-vision-desc = They allow you to see in the dark, all while looking like normal sunglasses!
uplink-thermal-vision-name = Thermal vision goggles
uplink-thermal-vision-desc = They allow you to see living creatures regardless of obstacles, all while looking like normal sunglasses!
# Misc
uplink-cyberpen-name = Cybersun Pen
uplink-cyberpen-desc = Cybersun's legal department pen, invaluable for forging documents and escaping prisons. Smells vaguely of hard-light and war profiteering.

View File

@@ -48,3 +48,12 @@ ent-ActionToggleEyes = Открыть/закрыть глаза
.desc = Закройте глаза, чтобы защитить их, или откройте, чтобы насладиться яркими цветами.
ent-ActionToggleWagging = action-name-toggle-wagging
.desc = action-description-toggle-wagging
ent-ToggleNightVision = Переключить ночное зрение
.desc = Переключает ночное зрение.
ent-ToggleThermalVision = Переключить тепловизионное зрение
.desc = Переключает тепловизионное зрение.
ent-PulseThermalVision = Подать термальный импульт
.desc = Временно активируйте тепловизионное зрение.

View File

@@ -0,0 +1,23 @@
# Night Vision Goggles
ent-ClothingEyesNightVisionGoggles = ПНВ
.desc = Усовершенствованный дисплей, который обеспечивает видимость в полной темноте.
ent-ClothingEyesNightVisionSecurityGoggles = ПНВ службы безопасности
.desc = { ent-ClothingEyesNightVisionGoggles.desc }
ent-ClothingEyesNightVisionMedicalGoggles = ПНВ медицинский
.desc = { ent-ClothingEyesNightVisionGoggles.desc }
ent-ClothingEyesNightVisionDiagnosticGoggles = ПНВ диагностический
.desc = { ent-ClothingEyesNightVisionGoggles.desc }
ent-ClothingEyesNightVisionGogglesSyndie = { ent-ClothingEyesNightVisionGoggles }
.desc = { ent-ClothingEyesNightVisionGoggles.desc }
ent-ClothingEyesNightVisionGogglesNukie = { ent-ClothingEyesNightVisionGoggles }
.desc = { ent-ClothingEyesNightVisionGoggles.desc }
# Thermal Vision Goggles
ent-ClothingEyesThermalVisionGoggles = ПТВ
.desc = Термальность - это как отношения, важно не только наличие тепла, но и его распределение.
ent-ClothingEyesThermalVisionMonocle = термонокль
.desc = Видение сквозь стены ещё никогда не было таким нежным и личностным.
ent-ClothingEyesThermalVisionGogglesSyndie = { ent-ClothingEyesThermalVisionGoggles }
.desc = { ent-ClothingEyesThermalVisionGoggles.desc }
ent-ClothingEyesThermalVisionGogglesNukie = { ent-ClothingEyesThermalVisionGoggles }
.desc = { ent-ClothingEyesThermalVisionGoggles.desc }

View File

@@ -0,0 +1,2 @@
research-technology-night-vision = Ночное видение
research-technology-thermal-vision = Термальное видение

View File

@@ -341,6 +341,13 @@ uplink-hardsuit-syndieelite-desc = Элитная версия кроваво-к
uplink-clothing-outer-hardsuit-juggernaut-name = Костюм джаггернаута Cybersun
uplink-clothing-outer-hardsuit-juggernaut-desc = Сверхпрочная броня из материалов, испытанных в хромосферном комплексе Тау. Единственное, что будет замедлять вас - это сам костюм... и тазеры.
uplink-night-vision-name = Прибор ночного видения
uplink-night-vision-desc = Позволяет вам видеть в темноте, при этом выглядя как обычные солнцезащитные очки!
uplink-thermal-vision-name = Прибор термального видения
uplink-thermal-vision-desc = Позволяет вам видеть живых существ независимо от преград, при этом выглядя как обычные солнцезащитные очки!
# Misc
uplink-cyberpen-name = Ручка Cybersun
uplink-cyberpen-desc = Ручка юридического отдела "Cybersun". Бесценная для подделки документов и побегов из тюрем. Смутно пахнет хардлайтом и наживой на ВПК.
@@ -443,4 +450,4 @@ uplink-hypodart-name = Гиподротик
uplink-hypodart-desc = Неприметный на первый взгляд дротик с увеличенным резервуаром для химических веществ. Он вмещает в себя до 7 ед. реагентов и мгновенно впрыскивает их при попадании в цель. Изначально пуст.
uplink-helmet-name = Шлем спецназа
uplink-helmet-desc = Чрезвычайно прочный шлем, обычно используемый военизированными формированиями. Он украшен гнусным рисунком в красную и черную полоску.
uplink-helmet-desc = Чрезвычайно прочный шлем, обычно используемый военизированными формированиями. Он украшен гнусным рисунком в красную и черную полоску.

View File

@@ -405,3 +405,42 @@
event:
!type:FabricateActionEvent
fabrication: FoodGumball
- type: entity
id: ToggleNightVision
name: Switch night vision
description: Switches night vision.
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
itemIconStyle: BigAction
priority: -20
icon:
sprite: Clothing/Eyes/Goggles/nightvision.rsi
state: icon
event: !type:ToggleNightVisionEvent
- type: entity
id: ToggleThermalVision
name: Switch Thermal vision
description: Switches Thermal vision.
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
itemIconStyle: BigAction
priority: -20
icon:
sprite: Clothing/Eyes/Goggles/thermal.rsi
state: icon
event: !type:ToggleThermalVisionEvent
- type: entity
id: PulseThermalVision
parent: ToggleThermalVision
name: Pulse Thermal Vision
description: Activate thermal vision temporarily.
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
useDelay: 4

View File

@@ -44,7 +44,7 @@
prob: 0.2
- !type:EntSelector
id: WeaponFlareGun
prob: 0.1
prob: 0.05
- !type:EntSelector
id: BoxMRE
prob: 0.1
@@ -165,6 +165,10 @@
- !type:EntSelector
id: WeaponSniperMosin
weight: 2
- !type:EntSelector
id: ClothingEyesNightVisionGogglesSyndie
- !type:EntSelector
id: ClothingEyesThermalVisionGogglesSyndie
- type: entityTable
id: MaintenanceLockerLoot

View File

@@ -1532,6 +1532,78 @@
components:
- SurplusBundle
- type: listing
id: UplinkNightGoggles
name: uplink-night-vision-name
description: uplink-night-vision-desc
productEntity: ClothingEyesNightVisionGogglesSyndie
discountCategory: rareDiscounts
discountDownTo:
Telecrystal: 1
cost:
Telecrystal: 3
categories:
- UplinkWearables
conditions:
- !type:StoreWhitelistCondition
blacklist:
tags:
- NukeOpsUplink
- type: listing
id: UplinkNightGogglesNukie
name: uplink-night-vision-name
description: uplink-night-vision-desc
productEntity: ClothingEyesNightVisionGogglesNukie
discountCategory: rareDiscounts
discountDownTo:
Telecrystal: 1
cost:
Telecrystal: 3
categories:
- UplinkWearables
conditions:
- !type:StoreWhitelistCondition
whitelist:
tags:
- NukeOpsUplink
- type: listing
id: UplinkThermalGoggles
name: uplink-thermal-vision-name
description: uplink-thermal-vision-desc
productEntity: ClothingEyesThermalVisionGogglesSyndie
discountCategory: rareDiscounts
discountDownTo:
Telecrystal: 1
cost:
Telecrystal: 3
categories:
- UplinkWearables
conditions:
- !type:StoreWhitelistCondition
blacklist:
tags:
- NukeOpsUplink
- type: listing
id: UplinkThermalGogglesNukie
name: uplink-thermal-vision-name
description: uplink-thermal-vision-desc
productEntity: ClothingEyesThermalVisionGogglesNukie
discountCategory: rareDiscounts
discountDownTo:
Telecrystal: 1
cost:
Telecrystal: 3
categories:
- UplinkWearables
conditions:
- !type:StoreWhitelistCondition
whitelist:
tags:
- NukeOpsUplink
# Tools
- type: listing

View File

@@ -233,6 +233,7 @@
- type: Clothing
sprite: Clothing/Eyes/Glasses/ninjavisor.rsi
- type: FlashImmunity
- type: NightVision
- type: entity #Fake goggles, the latest in anti-valid hunting technology
parent: ClothingEyesBase

View File

@@ -0,0 +1,114 @@
# Night Vision Goggles
- type: entity
parent: ClothingEyesBase
id: ClothingEyesNightVisionGoggles
name: night vision goggles
description: An advanced heads-up display which provides id data and vision in complete darkness.
components:
- type: Sprite
sprite: Clothing/Eyes/Goggles/nightvision.rsi
- type: Clothing
sprite: Clothing/Eyes/Goggles/nightvision.rsi
- type: NightVision
- type: IdentityBlocker
coverage: EYES
- type: entity
parent: [ClothingEyesNightVisionGoggles, ShowSecurityIcons]
id: ClothingEyesNightVisionSecurityGoggles
name: night vision security goggles
components:
- type: Sprite
sprite: Clothing/Eyes/Goggles/security_nightvision.rsi
- type: Clothing
sprite: Clothing/Eyes/Goggles/security_nightvision.rsi
- type: entity
parent: [ClothingEyesNightVisionGoggles, ClothingEyesHudMedical]
id: ClothingEyesNightVisionMedicalGoggles
name: night vision medical goggles
components:
- type: Sprite
sprite: Clothing/Eyes/Goggles/medical_nightvision.rsi
- type: Clothing
sprite: Clothing/Eyes/Goggles/medical_nightvision.rsi
- type: entity
parent: [ClothingEyesNightVisionGoggles, ClothingEyesHudDiagnostic]
id: ClothingEyesNightVisionDiagnosticGoggles
name: night vision diagnostic goggles
components:
- type: Sprite
sprite: Clothing/Eyes/Goggles/diagnostic_nightvision.rsi
- type: Clothing
sprite: Clothing/Eyes/Goggles/diagnostic_nightvision.rsi
- type: entity
parent: ClothingEyesNightVisionGoggles
id: ClothingEyesNightVisionGogglesSyndie
suffix: "Chameleon"
components:
- type: ChameleonClothing
slot: [eyes]
default: ClothingEyesNightVisionGoggles
- type: UserInterface
interfaces:
enum.ChameleonUiKey.Key:
type: ChameleonBoundUserInterface
- type: entity
parent: [ClothingEyesNightVisionGogglesSyndie, ShowSecurityIcons]
id: ClothingEyesNightVisionGogglesNukie
suffix: "Chameleon, NukeOps"
components:
- type: ShowSyndicateIcons
# Thermal Vision Goggles
- type: entity
parent: ClothingEyesBase
id: ClothingEyesThermalVisionGoggles
name: thermal vision goggles
description: Thermals in the shape of glasses.
components:
- type: Sprite
sprite: Clothing/Eyes/Goggles/thermal.rsi
- type: Clothing
sprite: Clothing/Eyes/Goggles/thermal.rsi
- type: ThermalVision
pulseTime: 2
toggleAction: PulseThermalVision
- type: IdentityBlocker
coverage: EYES
- type: entity
parent: ClothingEyesThermalVisionGoggles
id: ClothingEyesThermalVisionMonocle
name: thermonocle
description: Never before has seeing through walls felt so gentlepersonly.
components:
- type: Sprite
sprite: Clothing/Eyes/Goggles/monocle_thermal.rsi
- type: Clothing
sprite: Clothing/Eyes/Goggles/monocle_thermal.rsi
- type: entity
parent: ClothingEyesThermalVisionGoggles
id: ClothingEyesThermalVisionGogglesSyndie
suffix: "Chameleon"
components:
- type: ChameleonClothing
slot: [eyes]
default: ClothingEyesThermalVisionGoggles
- type: UserInterface
interfaces:
enum.ChameleonUiKey.Key:
type: ChameleonBoundUserInterface
- type: entity
parent: [ClothingEyesThermalVisionGogglesSyndie, ShowSecurityIcons]
id: ClothingEyesThermalVisionGogglesNukie
suffix: "Chameleon, NukeOps"
components:
- type: ShowSyndicateIcons

View File

@@ -799,6 +799,9 @@
- type: FlashSoundSuppression
maxRange: 0
# WD EDIT END
- type: ThermalVision
color: "#98EEFB"
lightRadius: 15
#MISC. HARDSUITS
#Clown Hardsuit

View File

@@ -126,6 +126,7 @@
cell_slot:
name: power-cell-slot-component-slot-name-default
- type: Body
thermalVisibility: false
- type: StatusEffects
allowed:
- Stun

View File

@@ -70,6 +70,12 @@
barkType: mouse
minTime: 10 # Mice like to squeak, I think. You can always put your pet mouse to sleep if it gets annoying
maxTime: 160
- type: NightVision
isActive: true
toggleAction: null
color: "#808080"
activateSound: null
deactivateSound: null
- type: entity
name: bee
@@ -1793,6 +1799,12 @@
Taco: RatTaco
Burger: RatBurger
Skewer: RatSkewer
- type: NightVision
isActive: true
toggleAction: null
color: "#808080"
activateSound: null
deactivateSound: null
# WD EDIT START
- type: Destructible
thresholds:

View File

@@ -88,6 +88,12 @@
interfaces:
enum.SurgeryUIKey.Key:
type: SurgeryBui
- type: NightVision
isActive: true
toggleAction: null
color: "#808080"
activateSound: null
deactivateSound: null
- type: entity
parent: BaseMobCarp

View File

@@ -132,6 +132,12 @@
interfaces:
enum.SurgeryUIKey.Key:
type: SurgeryBui
- type: NightVision
isActive: true
toggleAction: null
color: "#808080"
activateSound: null
deactivateSound: null
- type: entity
id: MobRatKingBuff
@@ -317,6 +323,12 @@
- type: FireVisuals
sprite: Mobs/Effects/onfire.rsi
normalState: Mouse_burning
- type: NightVision
isActive: true
toggleAction: null
color: "#808080"
activateSound: null
deactivateSound: null
- type: weightedRandomEntity
id: RatKingLoot

View File

@@ -140,6 +140,12 @@
- Xeno
understands:
- Xeno
- type: ThermalVision
isActive: true
lightRadius: 15
color: "#808080"
activateSound: null
deactivateSound: null
- type: entity
name: Praetorian

View File

@@ -38,6 +38,7 @@
bodyType: Kinematic
- type: Body
prototype: Aghost
thermalVisibility: false
- type: Access
groups:
- AllAccess

View File

@@ -140,6 +140,11 @@
tags:
- CannotSuicide
- DoorBumpOpener
- type: NightVision
isActive: true
color: "#808080"
activateSound: null
deactivateSound: null
- type: entity
parent: BaseMobDragon

View File

@@ -233,6 +233,7 @@
- type: Body
prototype: IPC
requiredLegs: 2
thermalVisibility: false
- type: Ensnareable
sprite: Objects/Misc/ensnare.rsi
- type: Speech

View File

@@ -362,6 +362,9 @@
- AnimalTranslator
- MofficTranslatorImplanter
- MofficTranslator
- ClothingEyesNightVisionGoggles
- ClothingEyesNightVisionDiagnosticGoggles
- ClothingEyesThermalVisionGoggles
- RCDAmmo #DeltaV
- RCD #EE
# Shitmed Change
@@ -911,6 +914,7 @@
- WeaponDisablerSMG
- WeaponLaserCannon
- WeaponLaserCarbine
- ClothingEyesNightVisionSecurityGoggles
- ClothingHeadHelmetInsulated # Nyanotrasen - Insulative headgear
- ClothingHeadCage # Nyanotrasen - Insulative headgear
- ShockCollar # Nyanotrasen - Shock Collar
@@ -1059,6 +1063,7 @@
- ClothingEyesHudMedical # Nyano
- ChemicalPayload # Nyano
- SyringeCryostasis
- ClothingEyesNightVisionMedicalGoggles
# Shitmed Change
- EnergyScalpel
- EnergyCautery

View File

@@ -106,6 +106,10 @@
- proto: WeaponTeslaGun
prob: 0.1
cost: 2
- proto: ClothingEyesNightVisionGoggles
cost: 8
- proto: ClothingEyesGlassesThermal
cost: 8
# Mob loot table

View File

@@ -234,3 +234,33 @@
Steel: 500
Glass: 400
Gold: 100
- type: latheRecipe
id: ClothingEyesNightVisionGoggles
result: ClothingEyesNightVisionGoggles
completetime: 2
materials:
Steel: 200
Glass: 100
Silver: 100
Gold: 100
- type: latheRecipe
id: ClothingEyesNightVisionDiagnosticGoggles
result: ClothingEyesNightVisionDiagnosticGoggles
completetime: 2
materials:
Steel: 200
Glass: 100
Silver: 100
Gold: 100
- type: latheRecipe
id: ClothingEyesThermalVisionGoggles
result: ClothingEyesThermalVisionGoggles
completetime: 2
materials:
Steel: 200
Glass: 100
Silver: 100
Gold: 100

View File

@@ -250,4 +250,15 @@
completetime: 2
materials:
Steel: 100
Plastic: 100
Plastic: 100
- type: latheRecipe
id: ClothingEyesNightVisionMedicalGoggles
result: ClothingEyesNightVisionMedicalGoggles
completetime: 7
materials:
Steel: 300
Glass: 300
Silver: 100
Gold: 100
Plasma: 200

View File

@@ -995,4 +995,14 @@
completetime: 2
materials:
Plastic: 15
Uranium: 10
Uranium: 10
- type: latheRecipe
id: ClothingEyesNightVisionSecurityGoggles
result: ClothingEyesNightVisionSecurityGoggles
completetime: 5
materials:
Steel: 500
Glass: 300
Silver: 100
Gold: 100

View File

@@ -167,6 +167,33 @@
- MedicalScannerMachineCircuitboard
- MetempsychoticMachineCircuitboard
- type: technology
id: NightVisionTech
name: research-technology-night-vision
icon:
sprite: Clothing/Eyes/Goggles/nightvision.rsi
state: icon
discipline: Experimental
tier: 2
cost: 10000
recipeUnlocks:
- ClothingEyesNightVisionGoggles
- ClothingEyesNightVisionSecurityGoggles
- ClothingEyesNightVisionMedicalGoggles
- ClothingEyesNightVisionDiagnosticGoggles
- type: technology
id: ThermalVisionTech
name: research-technology-thermal-vision
icon:
sprite: Clothing/Eyes/Goggles/thermal.rsi
state: icon
discipline: Experimental
tier: 2
cost: 10000
recipeUnlocks:
- ClothingEyesThermalVisionGoggles
# Tier 3
- type: technology

View File

@@ -127,4 +127,9 @@
- type: shader
id: Ethereal
kind: source
path: "/Textures/Shaders/ethereal.swsl"
path: "/Textures/Shaders/ethereal.swsl"
- type: shader
id: NightVision
kind: source
path: "/Textures/Shaders/nightvision.swsl"

View File

@@ -113,6 +113,12 @@
sprite: Clothing/Eyes/Misc/blindfold.rsi
- type: FlashImmunity
- type: EyeProtection
- type: NightVision
isActive: true
toggleAction: null
activateSound: null
deactivateSound: null
color: White
- type: ShowHealthBars
damageContainers:
- Biological
@@ -120,5 +126,3 @@
- type: ShowHealthIcons
damageContainers:
- Biological
# TODO: ADD NIGHT VISION

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

View File

@@ -0,0 +1,48 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from https://github.com/BlueMoon-Labs/MOLOT-BlueMoon-Station/blob/master/icons/obj/clothing/glasses.dmi",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "equipped-EYES",
"directions": 4,
"delays": [
[
0.1,
0.1
],
[
0.1,
0.1
],
[
0.1,
0.1
],
[
0.1,
0.1
]
]
},
{
"name": "equipped-EYES-off",
"directions": 4
},
{
"name": "icon"
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

View File

@@ -0,0 +1,48 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from https://github.com/BlueMoon-Labs/MOLOT-BlueMoon-Station/blob/master/icons/obj/clothing/glasses.dmi",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "equipped-EYES",
"directions": 4,
"delays": [
[
0.1,
0.1
],
[
0.1,
0.1
],
[
0.1,
0.1
],
[
0.1,
0.1
]
]
},
{
"name": "equipped-EYES-off",
"directions": 4
},
{
"name": "icon"
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 B

View File

@@ -0,0 +1,22 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from https://github.com/BlueMoon-Labs/MOLOT-BlueMoon-Station/blob/master/icons/obj/clothing/glasses.dmi",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "equipped-EYES",
"directions": 4
},
{
"name": "equipped-EYES-off",
"directions": 4
},
{
"name": "icon"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

View File

@@ -0,0 +1,48 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "equipped-EYES",
"directions": 4,
"delays": [
[
0.1,
0.1
],
[
0.1,
0.1
],
[
0.1,
0.1
],
[
0.1,
0.1
]
]
},
{
"name": "equipped-EYES-off",
"directions": 4
},
{
"name": "icon"
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

View File

@@ -0,0 +1,48 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from https://github.com/BlueMoon-Labs/MOLOT-BlueMoon-Station/blob/master/icons/obj/clothing/glasses.dmi",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "equipped-EYES",
"directions": 4,
"delays": [
[
0.1,
0.1
],
[
0.1,
0.1
],
[
0.1,
0.1
],
[
0.1,
0.1
]
]
},
{
"name": "equipped-EYES-off",
"directions": 4
},
{
"name": "icon"
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

View File

@@ -0,0 +1,26 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "equipped-EYES",
"directions": 4
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
}
]
}

View File

@@ -0,0 +1,38 @@
light_mode unshaded;
uniform sampler2D SCREEN_TEXTURE;
uniform highp vec3 tint; // Colour of the tint
uniform highp float luminance_threshold; // number between 0 and 1
uniform highp float noise_amount; // number between 0 and 1
lowp float rand (lowp vec2 n) {
return 0.5 + 0.5 * fract (sin (dot (n.xy, vec2 (12.9898, 78.233)))* 43758.5453);
}
void fragment() {
highp vec4 color = zTextureSpec(SCREEN_TEXTURE, FRAGCOORD.xy * SCREEN_PIXEL_SIZE);
// convert color to grayscale using luminance
highp float grey = dot(color.rgb, vec3(0.298, 0.5882, 0.1137));
// calculate local threshold
highp float threshold = grey * luminance_threshold;
// amplify low luminance parts
if (grey < threshold) {
grey += (threshold - grey) * 0.5;
if (grey > 1.0) {
grey = 1.0;
}
}
// apply night vision color tint
color.rgb = mix(color.rgb, tint, grey);
// add some noise for realism
lowp float noise = rand(FRAGCOORD.xy + TIME) * noise_amount / 10.0;
color.rgb += noise;
COLOR = color;
}