From 2e649fd74b60bf8b9e0b330b3cafaebd6798cc4d Mon Sep 17 00:00:00 2001 From: Spatison <137375981+Spatison@users.noreply.github.com> Date: Fri, 10 Jan 2025 07:41:32 +0300 Subject: [PATCH] Night And Thermal Vision (#1462) # Description Port from WWDP. Refactor from [Goob](https://github.com/Goob-Station/Goob-Station/pull/1251) ---

Media

![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)

--- # Changelog :cl: @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) --- Content.Client/Overlays/EquipmentHudSystem.cs | 2 +- .../Switchable/BaseSwitchableOverlay.cs | 48 +++++ .../Overlays/Switchable/NightVisionSystem.cs | 87 +++++++++ .../Switchable/ThermalVisionOverlay.cs | 159 +++++++++++++++++ .../Switchable/ThermalVisionSystem.cs | 95 ++++++++++ Content.Shared/Actions/SharedActionsSystem.cs | 5 +- .../Body/Components/BodyComponent.cs | 3 + .../Inventory/InventorySystem.Relay.cs | 4 + .../Overlays/BaseOverlayComponent.cs | 16 ++ .../Switchable/NightVisionComponent.cs | 14 ++ .../Overlays/Switchable/NightVisionSystem.cs | 3 + .../Switchable/SwitchableOverlayComponent.cs | 48 +++++ .../Switchable/SwitchableOverlaySystem.cs | 166 ++++++++++++++++++ .../Switchable/ThermalVisionComponent.cs | 17 ++ .../Switchable/ThermalVisionSystem.cs | 3 + Resources/Audio/Items/Goggles/activate.ogg | Bin 0 -> 12048 bytes .../Audio/Items/Goggles/attributions.yml | 9 + Resources/Audio/Items/Goggles/deactivate.ogg | Bin 0 -> 12424 bytes .../Locale/en-US/research/technologies.ftl | 2 + .../Locale/en-US/store/uplink-catalog.ftl | 6 + .../Locale/ru-RU/prototypes/actions/types.ftl | 9 + .../entities/clothing/eyes/goggles.ftl | 23 +++ .../objects/tools/empflashlight.ftl | 0 .../Locale/ru-RU/research/techologies.ftl | 2 + .../Locale/ru-RU/store/uplink-catalog.ftl | 9 +- Resources/Prototypes/Actions/types.yml | 39 ++++ .../Prototypes/Catalog/Fills/Lockers/misc.yml | 6 +- .../Prototypes/Catalog/uplink_catalog.yml | 72 ++++++++ .../Entities/Clothing/Eyes/glasses.yml | 1 + .../Entities/Clothing/Eyes/goggles.yml | 114 ++++++++++++ .../Clothing/Head/hardsuit-helmets.yml | 3 + .../Mobs/Cyborgs/base_borg_chassis.yml | 1 + .../Prototypes/Entities/Mobs/NPCs/animals.yml | 12 ++ .../Prototypes/Entities/Mobs/NPCs/carp.yml | 6 + .../Entities/Mobs/NPCs/regalrat.yml | 12 ++ .../Prototypes/Entities/Mobs/NPCs/xeno.yml | 6 + .../Entities/Mobs/Player/admin_ghost.yml | 1 + .../Entities/Mobs/Player/dragon.yml | 5 + .../Entities/Mobs/Player/silicon_base.yml | 1 + .../Entities/Structures/Machines/lathe.yml | 5 + .../Prototypes/Procedural/salvage_loot.yml | 4 + .../Prototypes/Recipes/Lathes/devices.yml | 30 ++++ .../Prototypes/Recipes/Lathes/medical.yml | 13 +- .../Prototypes/Recipes/Lathes/security.yml | 12 +- .../Prototypes/Research/experimental.yml | 27 +++ Resources/Prototypes/Shaders/shaders.yml | 7 +- .../Entities/Clothing/Cult/armor.yml | 8 +- .../equipped-EYES-off.png | Bin 0 -> 452 bytes .../equipped-EYES.png | Bin 0 -> 739 bytes .../diagnostic_nightvision.rsi/icon.png | Bin 0 -> 306 bytes .../inhand-left.png | Bin 0 -> 233 bytes .../inhand-right.png | Bin 0 -> 230 bytes .../diagnostic_nightvision.rsi/meta.json | 48 +++++ .../equipped-EYES-off.png | Bin 0 -> 452 bytes .../medical_nightvision.rsi/equipped-EYES.png | Bin 0 -> 733 bytes .../Goggles/medical_nightvision.rsi/icon.png | Bin 0 -> 306 bytes .../medical_nightvision.rsi/inhand-left.png | Bin 0 -> 233 bytes .../medical_nightvision.rsi/inhand-right.png | Bin 0 -> 230 bytes .../Goggles/medical_nightvision.rsi/meta.json | 48 +++++ .../monocle_thermal.rsi/equipped-EYES-off.png | Bin 0 -> 477 bytes .../monocle_thermal.rsi/equipped-EYES.png | Bin 0 -> 410 bytes .../Eyes/Goggles/monocle_thermal.rsi/icon.png | Bin 0 -> 298 bytes .../Goggles/monocle_thermal.rsi/meta.json | 22 +++ .../nightvision.rsi/equipped-EYES-off.png | Bin 0 -> 452 bytes .../Goggles/nightvision.rsi/equipped-EYES.png | Bin 0 -> 719 bytes .../Eyes/Goggles/nightvision.rsi/icon.png | Bin 0 -> 218 bytes .../Goggles/nightvision.rsi/inhand-left.png | Bin 0 -> 385 bytes .../Goggles/nightvision.rsi/inhand-right.png | Bin 0 -> 410 bytes .../Eyes/Goggles/nightvision.rsi/meta.json | 48 +++++ .../equipped-EYES-off.png | Bin 0 -> 452 bytes .../equipped-EYES.png | Bin 0 -> 704 bytes .../Goggles/security_nightvision.rsi/icon.png | Bin 0 -> 505 bytes .../security_nightvision.rsi/inhand-left.png | Bin 0 -> 233 bytes .../security_nightvision.rsi/inhand-right.png | Bin 0 -> 309 bytes .../security_nightvision.rsi/meta.json | 48 +++++ .../Goggles/thermal.rsi/equipped-EYES.png | Bin 0 -> 524 bytes .../Eyes/Goggles/thermal.rsi/icon.png | Bin 0 -> 510 bytes .../Eyes/Goggles/thermal.rsi/inhand-left.png | Bin 0 -> 327 bytes .../Eyes/Goggles/thermal.rsi/inhand-right.png | Bin 0 -> 325 bytes .../Eyes/Goggles/thermal.rsi/meta.json | 26 +++ Resources/Textures/Shaders/nightvision.swsl | 38 ++++ 81 files changed, 1384 insertions(+), 9 deletions(-) create mode 100644 Content.Client/Overlays/Switchable/BaseSwitchableOverlay.cs create mode 100644 Content.Client/Overlays/Switchable/NightVisionSystem.cs create mode 100644 Content.Client/Overlays/Switchable/ThermalVisionOverlay.cs create mode 100644 Content.Client/Overlays/Switchable/ThermalVisionSystem.cs create mode 100644 Content.Shared/Overlays/BaseOverlayComponent.cs create mode 100644 Content.Shared/Overlays/Switchable/NightVisionComponent.cs create mode 100644 Content.Shared/Overlays/Switchable/NightVisionSystem.cs create mode 100644 Content.Shared/Overlays/Switchable/SwitchableOverlayComponent.cs create mode 100644 Content.Shared/Overlays/Switchable/SwitchableOverlaySystem.cs create mode 100644 Content.Shared/Overlays/Switchable/ThermalVisionComponent.cs create mode 100644 Content.Shared/Overlays/Switchable/ThermalVisionSystem.cs create mode 100644 Resources/Audio/Items/Goggles/activate.ogg create mode 100644 Resources/Audio/Items/Goggles/attributions.yml create mode 100644 Resources/Audio/Items/Goggles/deactivate.ogg create mode 100644 Resources/Locale/ru-RU/prototypes/entities/clothing/eyes/goggles.ftl rename Resources/Locale/ru-RU/{entities => prototypes}/objects/tools/empflashlight.ftl (100%) create mode 100644 Resources/Locale/ru-RU/research/techologies.ftl create mode 100644 Resources/Prototypes/Entities/Clothing/Eyes/goggles.yml create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES-off.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/equipped-EYES-off.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/equipped-EYES.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES-off.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES-off.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES-off.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/equipped-EYES.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/meta.json create mode 100644 Resources/Textures/Shaders/nightvision.swsl diff --git a/Content.Client/Overlays/EquipmentHudSystem.cs b/Content.Client/Overlays/EquipmentHudSystem.cs index c7578b6793..3672892ae3 100644 --- a/Content.Client/Overlays/EquipmentHudSystem.cs +++ b/Content.Client/Overlays/EquipmentHudSystem.cs @@ -102,7 +102,7 @@ public abstract class EquipmentHudSystem : EntitySystem where T : IComponent args.Components.Add(component); } - private void RefreshOverlay(EntityUid uid) + protected void RefreshOverlay(EntityUid uid) { if (uid != _player.LocalSession?.AttachedEntity) return; diff --git a/Content.Client/Overlays/Switchable/BaseSwitchableOverlay.cs b/Content.Client/Overlays/Switchable/BaseSwitchableOverlay.cs new file mode 100644 index 0000000000..5977355968 --- /dev/null +++ b/Content.Client/Overlays/Switchable/BaseSwitchableOverlay.cs @@ -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 : 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("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); + } +} diff --git a/Content.Client/Overlays/Switchable/NightVisionSystem.cs b/Content.Client/Overlays/Switchable/NightVisionSystem.cs new file mode 100644 index 0000000000..c85b3758d8 --- /dev/null +++ b/Content.Client/Overlays/Switchable/NightVisionSystem.cs @@ -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 +{ + [Dependency] private readonly IOverlayManager _overlayMan = default!; + [Dependency] private readonly ILightManager _lightManager = default!; + + private BaseSwitchableOverlay _overlay = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnToggle); + + _overlay = new BaseSwitchableOverlay(); + } + + private void OnToggle(Entity ent, ref SwitchableOverlayToggledEvent args) + { + RefreshOverlay(args.User); + } + + protected override void UpdateInternal(RefreshEquipmentHudEvent 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>(): + _overlayMan.AddOverlay(_overlay); + break; + case null: + _overlayMan.RemoveOverlay(_overlay); + break; + } + + if (_overlayMan.TryGetOverlay>(out var overlay)) + overlay.IsActive = nvComp == null; + } +} diff --git a/Content.Client/Overlays/Switchable/ThermalVisionOverlay.cs b/Content.Client/Overlays/Switchable/ThermalVisionOverlay.cs new file mode 100644 index 0000000000..eb12b33e3a --- /dev/null +++ b/Content.Client/Overlays/Switchable/ThermalVisionOverlay.cs @@ -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 _entries = []; + + private EntityUid? _lightEntity; + + public float LightRadius; + + public ThermalVisionComponent? Comp; + + public ThermalVisionOverlay() + { + IoCManager.InjectDependencies(this); + + _container = _entity.System(); + _transform = _entity.System(); + _stealth = _entity.System(); + _light = _entity.System(); + + 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(_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(); + 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(owner, out var ownerSprite) + && _entity.TryGetComponent(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 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 Ent, + MapId? Map, + Angle EyeRot); diff --git a/Content.Client/Overlays/Switchable/ThermalVisionSystem.cs b/Content.Client/Overlays/Switchable/ThermalVisionSystem.cs new file mode 100644 index 0000000000..9b6e5eed0f --- /dev/null +++ b/Content.Client/Overlays/Switchable/ThermalVisionSystem.cs @@ -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 +{ + [Dependency] private readonly IOverlayManager _overlayMan = default!; + + private ThermalVisionOverlay _thermalOverlay = default!; + private BaseSwitchableOverlay _overlay = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnToggle); + + _thermalOverlay = new ThermalVisionOverlay(); + _overlay = new BaseSwitchableOverlay(); + } + + private void OnToggle(Entity ent, ref SwitchableOverlayToggledEvent args) + { + RefreshOverlay(args.User); + } + + protected override void UpdateInternal(RefreshEquipmentHudEvent 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(): + _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>(): + _overlayMan.AddOverlay(_overlay); + break; + case null or { DrawOverlay: false }: + _overlayMan.RemoveOverlay(_overlay); + break; + } + + // Night vision overlay is prioritized + _overlay.IsActive = !_overlayMan.HasOverlay>(); + } +} diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 91acf47478..8cede391a8 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -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))); diff --git a/Content.Shared/Body/Components/BodyComponent.cs b/Content.Shared/Body/Components/BodyComponent.cs index 481e22150b..4ddfbdf979 100644 --- a/Content.Shared/Body/Components/BodyComponent.cs +++ b/Content.Shared/Body/Components/BodyComponent.cs @@ -41,4 +41,7 @@ public sealed partial class BodyComponent : Component [ViewVariables] [DataField, AutoNetworkedField] public HashSet LegEntities = new(); + + [DataField, AutoNetworkedField] + public bool ThermalVisibility = true; } diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index a65fe96296..585fa97623 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -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>(RelayInventoryEvent); SubscribeLocalEvent>(RelayInventoryEvent); SubscribeLocalEvent>(RelayInventoryEvent); + SubscribeLocalEvent>(RelayInventoryEvent); + SubscribeLocalEvent>(RelayInventoryEvent); SubscribeLocalEvent>(OnGetEquipmentVerbs); } diff --git a/Content.Shared/Overlays/BaseOverlayComponent.cs b/Content.Shared/Overlays/BaseOverlayComponent.cs new file mode 100644 index 0000000000..fe6a5c763e --- /dev/null +++ b/Content.Shared/Overlays/BaseOverlayComponent.cs @@ -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; +} diff --git a/Content.Shared/Overlays/Switchable/NightVisionComponent.cs b/Content.Shared/Overlays/Switchable/NightVisionComponent.cs new file mode 100644 index 0000000000..cb8866dc85 --- /dev/null +++ b/Content.Shared/Overlays/Switchable/NightVisionComponent.cs @@ -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; diff --git a/Content.Shared/Overlays/Switchable/NightVisionSystem.cs b/Content.Shared/Overlays/Switchable/NightVisionSystem.cs new file mode 100644 index 0000000000..f547b9dc76 --- /dev/null +++ b/Content.Shared/Overlays/Switchable/NightVisionSystem.cs @@ -0,0 +1,3 @@ +namespace Content.Shared.Overlays.Switchable; + +public sealed class NightVisionSystem : SwitchableOverlaySystem; diff --git a/Content.Shared/Overlays/Switchable/SwitchableOverlayComponent.cs b/Content.Shared/Overlays/Switchable/SwitchableOverlayComponent.cs new file mode 100644 index 0000000000..8565defe04 --- /dev/null +++ b/Content.Shared/Overlays/Switchable/SwitchableOverlayComponent.cs @@ -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; + + /// + /// If it is greater than 0, overlay isn't toggled but pulsed instead + /// + [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; +} diff --git a/Content.Shared/Overlays/Switchable/SwitchableOverlaySystem.cs b/Content.Shared/Overlays/Switchable/SwitchableOverlaySystem.cs new file mode 100644 index 0000000000..eb7b230cbc --- /dev/null +++ b/Content.Shared/Overlays/Switchable/SwitchableOverlaySystem.cs @@ -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 : 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(OnToggle); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnGetItemActions); + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(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(); + + 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 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); diff --git a/Content.Shared/Overlays/Switchable/ThermalVisionComponent.cs b/Content.Shared/Overlays/Switchable/ThermalVisionComponent.cs new file mode 100644 index 0000000000..6e3d39f289 --- /dev/null +++ b/Content.Shared/Overlays/Switchable/ThermalVisionComponent.cs @@ -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; diff --git a/Content.Shared/Overlays/Switchable/ThermalVisionSystem.cs b/Content.Shared/Overlays/Switchable/ThermalVisionSystem.cs new file mode 100644 index 0000000000..c19d0d1690 --- /dev/null +++ b/Content.Shared/Overlays/Switchable/ThermalVisionSystem.cs @@ -0,0 +1,3 @@ +namespace Content.Shared.Overlays.Switchable; + +public sealed class SharedThermalVisionSystem : SwitchableOverlaySystem; diff --git a/Resources/Audio/Items/Goggles/activate.ogg b/Resources/Audio/Items/Goggles/activate.ogg new file mode 100644 index 0000000000000000000000000000000000000000..96cdb288fe07d0e1d3c0f38774cb7ffe01624724 GIT binary patch literal 12048 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 literal 0 HcmV?d00001 diff --git a/Resources/Audio/Items/Goggles/attributions.yml b/Resources/Audio/Items/Goggles/attributions.yml new file mode 100644 index 0000000000..7b1121f542 --- /dev/null +++ b/Resources/Audio/Items/Goggles/attributions.yml @@ -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" \ No newline at end of file diff --git a/Resources/Audio/Items/Goggles/deactivate.ogg b/Resources/Audio/Items/Goggles/deactivate.ogg new file mode 100644 index 0000000000000000000000000000000000000000..e1e8f4fd82fcfa0b0c01c28b4ec93f53bbb2c2de GIT binary patch literal 12424 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 Z{kT65Jzel|`M;>-Z+o;iR#cM${||7Ja3cT! literal 0 HcmV?d00001 diff --git a/Resources/Locale/en-US/research/technologies.ftl b/Resources/Locale/en-US/research/technologies.ftl index 26cd4f8315..88cd0be397 100644 --- a/Resources/Locale/en-US/research/technologies.ftl +++ b/Resources/Locale/en-US/research/technologies.ftl @@ -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 diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 7eaacc5026..214a259296 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -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. diff --git a/Resources/Locale/ru-RU/prototypes/actions/types.ftl b/Resources/Locale/ru-RU/prototypes/actions/types.ftl index 4a9364816a..c14186ba7d 100644 --- a/Resources/Locale/ru-RU/prototypes/actions/types.ftl +++ b/Resources/Locale/ru-RU/prototypes/actions/types.ftl @@ -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 = Временно активируйте тепловизионное зрение. diff --git a/Resources/Locale/ru-RU/prototypes/entities/clothing/eyes/goggles.ftl b/Resources/Locale/ru-RU/prototypes/entities/clothing/eyes/goggles.ftl new file mode 100644 index 0000000000..050e373c86 --- /dev/null +++ b/Resources/Locale/ru-RU/prototypes/entities/clothing/eyes/goggles.ftl @@ -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 } diff --git a/Resources/Locale/ru-RU/entities/objects/tools/empflashlight.ftl b/Resources/Locale/ru-RU/prototypes/objects/tools/empflashlight.ftl similarity index 100% rename from Resources/Locale/ru-RU/entities/objects/tools/empflashlight.ftl rename to Resources/Locale/ru-RU/prototypes/objects/tools/empflashlight.ftl diff --git a/Resources/Locale/ru-RU/research/techologies.ftl b/Resources/Locale/ru-RU/research/techologies.ftl new file mode 100644 index 0000000000..54f3899255 --- /dev/null +++ b/Resources/Locale/ru-RU/research/techologies.ftl @@ -0,0 +1,2 @@ +research-technology-night-vision = Ночное видение +research-technology-thermal-vision = Термальное видение \ No newline at end of file diff --git a/Resources/Locale/ru-RU/store/uplink-catalog.ftl b/Resources/Locale/ru-RU/store/uplink-catalog.ftl index 47083e83a4..1f7ec3d6e9 100644 --- a/Resources/Locale/ru-RU/store/uplink-catalog.ftl +++ b/Resources/Locale/ru-RU/store/uplink-catalog.ftl @@ -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 = Чрезвычайно прочный шлем, обычно используемый военизированными формированиями. Он украшен гнусным рисунком в красную и черную полоску. \ No newline at end of file +uplink-helmet-desc = Чрезвычайно прочный шлем, обычно используемый военизированными формированиями. Он украшен гнусным рисунком в красную и черную полоску. diff --git a/Resources/Prototypes/Actions/types.yml b/Resources/Prototypes/Actions/types.yml index 21a2882897..0a57157f1b 100644 --- a/Resources/Prototypes/Actions/types.yml +++ b/Resources/Prototypes/Actions/types.yml @@ -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 + diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml index bf3776e6a9..65916a84b9 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml @@ -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 diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index 7f9088a3d0..cc0e0bb0f3 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -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 diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml index f0fdfa0a07..14474cf2ae 100644 --- a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml +++ b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml @@ -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 diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/goggles.yml b/Resources/Prototypes/Entities/Clothing/Eyes/goggles.yml new file mode 100644 index 0000000000..4348c87227 --- /dev/null +++ b/Resources/Prototypes/Entities/Clothing/Eyes/goggles.yml @@ -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 diff --git a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml index ed184845bf..6f28812549 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml @@ -799,6 +799,9 @@ - type: FlashSoundSuppression maxRange: 0 # WD EDIT END + - type: ThermalVision + color: "#98EEFB" + lightRadius: 15 #MISC. HARDSUITS #Clown Hardsuit diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index b8ff03abca..ada65ad33f 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -126,6 +126,7 @@ cell_slot: name: power-cell-slot-component-slot-name-default - type: Body + thermalVisibility: false - type: StatusEffects allowed: - Stun diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index d981061993..bf921c0885 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -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: diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml index d241173f2e..356615d2e9 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml @@ -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 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml index abe602037f..9e4f72bb0c 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml @@ -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 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml index 7358d07236..db8287e13e 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -140,6 +140,12 @@ - Xeno understands: - Xeno + - type: ThermalVision + isActive: true + lightRadius: 15 + color: "#808080" + activateSound: null + deactivateSound: null - type: entity name: Praetorian diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index fc87201b09..09791e6c7d 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -38,6 +38,7 @@ bodyType: Kinematic - type: Body prototype: Aghost + thermalVisibility: false - type: Access groups: - AllAccess diff --git a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml index 0f24313712..4c03c6c688 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml @@ -140,6 +140,11 @@ tags: - CannotSuicide - DoorBumpOpener + - type: NightVision + isActive: true + color: "#808080" + activateSound: null + deactivateSound: null - type: entity parent: BaseMobDragon diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml index 7c65b89a76..b8de17a55f 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml @@ -233,6 +233,7 @@ - type: Body prototype: IPC requiredLegs: 2 + thermalVisibility: false - type: Ensnareable sprite: Objects/Misc/ensnare.rsi - type: Speech diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 8ffa9e608c..d2924ad074 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -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 diff --git a/Resources/Prototypes/Procedural/salvage_loot.yml b/Resources/Prototypes/Procedural/salvage_loot.yml index 7e7ddf6ff9..1b941cd1a2 100644 --- a/Resources/Prototypes/Procedural/salvage_loot.yml +++ b/Resources/Prototypes/Procedural/salvage_loot.yml @@ -106,6 +106,10 @@ - proto: WeaponTeslaGun prob: 0.1 cost: 2 + - proto: ClothingEyesNightVisionGoggles + cost: 8 + - proto: ClothingEyesGlassesThermal + cost: 8 # Mob loot table diff --git a/Resources/Prototypes/Recipes/Lathes/devices.yml b/Resources/Prototypes/Recipes/Lathes/devices.yml index 2b0d6fa44f..56e1739817 100644 --- a/Resources/Prototypes/Recipes/Lathes/devices.yml +++ b/Resources/Prototypes/Recipes/Lathes/devices.yml @@ -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 diff --git a/Resources/Prototypes/Recipes/Lathes/medical.yml b/Resources/Prototypes/Recipes/Lathes/medical.yml index ba8f596d21..29f3fed247 100644 --- a/Resources/Prototypes/Recipes/Lathes/medical.yml +++ b/Resources/Prototypes/Recipes/Lathes/medical.yml @@ -250,4 +250,15 @@ completetime: 2 materials: Steel: 100 - Plastic: 100 \ No newline at end of file + Plastic: 100 + +- type: latheRecipe + id: ClothingEyesNightVisionMedicalGoggles + result: ClothingEyesNightVisionMedicalGoggles + completetime: 7 + materials: + Steel: 300 + Glass: 300 + Silver: 100 + Gold: 100 + Plasma: 200 diff --git a/Resources/Prototypes/Recipes/Lathes/security.yml b/Resources/Prototypes/Recipes/Lathes/security.yml index 1d9bdcfeb0..ed37e3abeb 100644 --- a/Resources/Prototypes/Recipes/Lathes/security.yml +++ b/Resources/Prototypes/Recipes/Lathes/security.yml @@ -995,4 +995,14 @@ completetime: 2 materials: Plastic: 15 - Uranium: 10 \ No newline at end of file + Uranium: 10 + +- type: latheRecipe + id: ClothingEyesNightVisionSecurityGoggles + result: ClothingEyesNightVisionSecurityGoggles + completetime: 5 + materials: + Steel: 500 + Glass: 300 + Silver: 100 + Gold: 100 diff --git a/Resources/Prototypes/Research/experimental.yml b/Resources/Prototypes/Research/experimental.yml index 423ec0f84d..cf6493847a 100644 --- a/Resources/Prototypes/Research/experimental.yml +++ b/Resources/Prototypes/Research/experimental.yml @@ -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 diff --git a/Resources/Prototypes/Shaders/shaders.yml b/Resources/Prototypes/Shaders/shaders.yml index 108b9f778b..96c51d1a98 100644 --- a/Resources/Prototypes/Shaders/shaders.yml +++ b/Resources/Prototypes/Shaders/shaders.yml @@ -127,4 +127,9 @@ - type: shader id: Ethereal kind: source - path: "/Textures/Shaders/ethereal.swsl" \ No newline at end of file + path: "/Textures/Shaders/ethereal.swsl" + +- type: shader + id: NightVision + kind: source + path: "/Textures/Shaders/nightvision.swsl" diff --git a/Resources/Prototypes/WhiteDream/Entities/Clothing/Cult/armor.yml b/Resources/Prototypes/WhiteDream/Entities/Clothing/Cult/armor.yml index 04279d51d2..bdbb4bac46 100644 --- a/Resources/Prototypes/WhiteDream/Entities/Clothing/Cult/armor.yml +++ b/Resources/Prototypes/WhiteDream/Entities/Clothing/Cult/armor.yml @@ -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 - diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES-off.png b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES-off.png new file mode 100644 index 0000000000000000000000000000000000000000..b63f30fc713a739b08ea1070559e2d3b6abf3f6d GIT binary patch literal 452 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|R53 zr;B4q#hkaZ9K8-ZNVLvZ2+=sg&GN=-_pVLq1q&5k=x)-e5L(!M$7$j6723PGU#)W5 z#>Fc#EA)*#M@!aG&dIJ7@0$-8IDayh|E%7wZ z^T!=~_Ut$ULtZq`>HPbLKH)0w_^@&grxzQ>o$a;58P>sLRUl^pFqX^AoC_sf|- zH%2Ayz4<4?T9&WSOzL5xS@Ej(Dhux2*`~2dY(w&WnfHDSyN;9=|6h33dcn>2wQv7Q z)v`MDu{6kVGFS>R6sa&g@nASHkpbcsG>L}w?5&9rY>pRx?sqz^_N1~;=k%T{AC(Rm zG2dQv;OyJEoDS0?dVgL{4!!Iyw=mT1fxtA;7qilMCvVtaXE^hK>f#Gp@*kwHG1Z^^ z*m`w?mzopr E058M1eEv^N50mYoN%&OG`A^vbtyPxmWzq$#J@{|EG+Kx&SYsS@l)(mYpr|D^}r1WthR3 zz}CqJ^9SI^ZW7tZ~s)iJ6OzpY?r&ODX!rS;WqlUYpY+5+P9 p=YUjt?g>1!-^SjYoDg~=zRyZNy+M4FAu!c4c)I$ztaD0e0swobG`0W$ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8bcf7105c2ba36f66beaadf560b76a9e095f924d GIT binary patch literal 306 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCil21AIbU?VJO24Q(VOltLquw`VCWjnv%JYH?##2vA-%W$|wy#aR;M7yKUz z7=ryPgg}A?9+AZi419+{nDKc2iWHz=x2KC^h=u>#37&k+iaacL-zNV2-+ei4zDJ)Sg7qp~XuIk%3Jr6%9C9(X0nn1$i0KY~V>9=9~Sid|BymH<$dA6Q|OX1S@O)G`n z`d78ixbDkXrrP|x``z2M%J-f0R4;o?D*hsWD6~k)WQhu=vS@I^?xt5)|0Ev}`lWu9 mpGBE_+J{c}7k{*`{9<~wS!TBVg?x}(89ZJ6T-G@yGywp$pK%)i literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..04fff80e4df5f5bfe6b670cf9f68368e945a073a GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`NC)_YxB_Vj z2_;=a8$0KKGkM1Mnl&CTboqZO@&Et-xdI8oKxxjBAirP+ARi3OnPy)B3Z{FyIEF;D zzP)yk?|=dabHI`X|Ne{TZ+A86ev>k7-|S;TdqP_}Ufh?N@s8pCxopE;;RRtO^-kZd z{2adSo4^0Qd9w|pXvFOLue+Qj#rWKnl$5MImaWkFymHZ|6YAYw9~Q1S!l|RxzKdax XEW^!ZZ4OI-Rx)_H`njxgN@xNA_h(vM literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d80344136fca4bab3f4e25ae0dc66b77c694904f GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`NC)_YxB_Vj z2_;=a8$0KKGkM1Mnl&CTboqZO@&Et-xdI8oKxxjBAirP+ARi3OnPy)B3Z{6vIEF;D zzP)D6cR+!M`GSD@fxrHD`gG+r6SJS1Cr`epFc#EA)*#M@!aG&dIJ7@0$-8IDayh|E%7wZ z^T!=~_Ut$ULtZq`>HPbLKH)0w_^@&grxzQ>o$a;58P>sLRUl^pFqX^AoC_sf|- zH%2Ayz4<4?T9&WSOzL5xS@Ej(Dhux2*`~2dY(w&WnfHDSyN;9=|6h33dcn>2wQv7Q z)v`MDu{6kVGFS>R6sa&g@nASHkpbcsG>L}w?5&9rY>pRx?sqz^_N1~;=k%T{AC(Rm zG2dQv;OyJEoDS0?dVgL{4!!Iyw=mT1fxtA;7qilMCvVtaXE^hK>f#Gp@*kwHG1Z^^ z*m`w?mzopr E058M1eEubw%a%8SRV``369S$#*7XdTVcwJeySGVP{uof;WesA9Xp4}yl?V0)a8riu=ZSN^NYA_$* zVK8RMS-@~T|Blw9@9)lqW%T=hc^@59VJ0NybpE`i<$I$>-;{t(|)JLu#4Yk?Wi4-^IPQH)NcE46?QpOx&`Immzifrw`A4D{Q3IHv_|*&@uT(i|2=KG>teny<`1qntKR$e<VqPDJP z-Os{8H|xtU{_OBqx*G6@tLs(G^Q)Hi8B8)^Yz>kOX$&(M6G&!ElTrBh{@>TFqHkh+ zs_ODiGFHj+JK4DI4iV^fIDcN((xG&1gGQXmL;vTeuJ1JX^5@PZ<|EDh;&}_caa{n$ r3g3kztwR5v|J?aglY?{z+-K~|lbbFZd?FT@UKu=H{an^LB{Ts5{kAk( literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..52522c080e85e6a1a893ba5bbd642663d48252d5 GIT binary patch literal 306 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCil21AIbU?VJO24Q(VOltLqu6?*rG<*v6}zd!KWS)hE+0@uAjinAohFZe$c zFa-No2!R9(JR*x382Ao@Fyrz36)8Z$Zci7-5DWje6Fm8t6?s_hzD@l3zx#69e2+kp zr&gs0E@(-&T-CR6dLDjIN@Do~HGzc30e+1#(r?4|v3_|Nc;&oh@@zc^m%^p*n^p?D z^{;B3aov}(OttxW_q(@imG3+0sb2P)RQyH$P-u~o$r2S#Wzpb--A%8q{z*O{^h^CH mKZ`Q=v=5!`FaBs>`Ni~Vv&?M!3;7_oGI+ZBxvXk44ofy`glX(f`NC)_YxB_Vj z2_;=a8$0I!m8JJ&``&Bq-~9de&6{l)MI&a{f8FIQDaPlnq@-l!v22CT=aq{#olx)g`mk`t5l$Vg_FW8n XWEpNQYjaovw35No)z4*}Q$iB}O9Nb- literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..f9c17318b4ee395b57eb4efab8c1756143523406 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`NC)_YxB_Vj z2_;=a8$0I!m8JJ&``Q#W5tJ z_3brlz5@z8%ohaI5B&AN)2A!1nV9|5JbCg(B^Q_f%oRV@G8f0K%`jMhv?RyjtK8Z9 zymw!(c4WQwZ1uMbJfHpqc>nl!m_blbP&O*Jzvt>V53`=z%DK1XeoF7KwR^#IC55(qv;yP#7H2K0>|A0A0-$kw5 z6(>6%PgxXLE_MH0&uDSA^2;w6CjS(dyMFz}EUxa^+^kEtwA@JEP`!Klx|IbtQZ3@* z5@)S%nsj@A$JhBsxO_f`YMd5g_+WDX#%~@If5u~W1}lOWWNV#0zSJ?Bb%S5y!8Ht% z^X0jgJ8*cK|Fu8z>$kn*HU??_17Zn}8E-J<*f2yl^s*;#Z}`ZN!)PYgpnKpL^9I%% z6%0s<3^UFs&){3oxRc@5XO3q~Up{lpKfhiJ$jocIwQIsZsdpPr7yJrv=bXXKz+nHG zqg>iDa?f$jjC1TumaXOP{I*>xVgFoCg9WcYUXhurTPl}Q$oeHe{_y=LTmjX;11wnA iBp0z}JR-(1pLx$LUT)lx{@o84UkskEelF{r5}E*W3du_V literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES.png new file mode 100644 index 0000000000000000000000000000000000000000..dc4d9e775b88d077723af637c09a002db51a84a7 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|TRv zr;B4q#hkad7J4}+%CvoSkGx>ER!MJtRLSXk4T)z~B*Y5rnRRfJd4g4f^o^X;&51@C zg1nC|>ieYq`}g$ee>?Gl2Y>bz^EL04os`ZnNu0~T?&a3<{?ocGpM(Rd8>hbvy#2C% zb8*43r#~3Cu3|iOw!B^Y-ZS3ji-!BxGkAqEPnoQ5{(tL@XTNIZpX3b+Wq$K5tzLfb z^~%diVK3Y_*zg)SuMB87|D0{>AqKl?;cuV4UJxYF@aX0ALk!>Aw0By5;aI+a{R< zX@b=(kqt$^1nZBVW^!2@sKU6&Z@&tsz)sgmEFE(mc`&)8F@U^^iM}(RDDYh>5>Rvo P7}yM+u6{1-oD!M<%d4uQ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e0aaa8ae9aa3c281302ad194b0a2b7b1a30f4d21 GIT binary patch literal 298 zcmV+_0oDGAP)qE$6}EuNQ7bkzjg&GExgwG!?EerK=fwVrJ$Az(jfTU# wGaKHE%iEaGi#ILUo+rw;(+U14XZUA%0jaqLSXS!5lK=n!07*qoM6N<$f=#%29{>OV literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/meta.json new file mode 100644 index 0000000000..ad770fb0f8 --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/meta.json @@ -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" + } + ] +} diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES-off.png b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES-off.png new file mode 100644 index 0000000000000000000000000000000000000000..b63f30fc713a739b08ea1070559e2d3b6abf3f6d GIT binary patch literal 452 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|R53 zr;B4q#hkaZ9K8-ZNVLvZ2+=sg&GN=-_pVLq1q&5k=x)-e5L(!M$7$j6723PGU#)W5 z#>Fc#EA)*#M@!aG&dIJ7@0$-8IDayh|E%7wZ z^T!=~_Ut$ULtZq`>HPbLKH)0w_^@&grxzQ>o$a;58P>sLRUl^pFqX^AoC_sf|- zH%2Ayz4<4?T9&WSOzL5xS@Ej(Dhux2*`~2dY(w&WnfHDSyN;9=|6h33dcn>2wQv7Q z)v`MDu{6kVGFS>R6sa&g@nASHkpbcsG>L}w?5&9rY>pRx?sqz^_N1~;=k%T{AC(Rm zG2dQv;OyJEoDS0?dVgL{4!!Iyw=mT1fxtA;7qilMCvVtaXE^hK>f#Gp@*kwHG1Z^^ z*m`w?mzopr E058M1eE(56?F=2Egz13RyoeI)BL=X2J-)z>E(+4Mi& z``V*l_3ymve|Dv^z9`%Mw14qp2|nd*k7}x7qyA(GUV8htZvRHt70;F*_3u~oEMBed zc=@ya`}foLpF8*H{L!QK+xJGq_L{$b{qgAoh3vYphFATszgRt0$cz7$X)W>QUfjM) zHn+*EeunSf9vxt6U-fzB{M|eH4KM$1*!^2~mj_6geB4wJioOZ>O~p_I#bo`3qqpU10zeil#S_HtVs`1;xNlV{^+F!)a;-)*Vv z9_pv%`j1~e@5I(HFMj*XIL4O_ET3xre)(gS${z7^i_rAFr(ZtZ!tp)7e)nc!-+71a zWm^nyJbiXu*V^INqLwYJBK$dwzVi>;*CsMs?$5i)_+t9slT10*VS)*8>L**f|I28rn!mD1}BQ zi!9KSsclyt1T!`qZMy~(> literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..995b37471b38d3dbcef1e91888c4ed58239b2d29 GIT binary patch literal 385 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEVD#{GaSW+oe0ytS)}a8AV;?79 zTDrUS+Q#IAP0lZsthSWoxw9WU{v!DYhg1B5>~=oQcTG*fg=?A>V|Tglb68?=^-eYO z#s71CKGf`de*CKH@t#%&CJu!LG~z%Yr^~+Gxjp%tUSD5r^X>EFvzGg-|4-4(F^q^Z znq&F=?YHVp)rX(%y813!_Xwq1POdh~sI^pF2$AHG+EHSOm8{6o=W#kb{d*H*u; zS^a$a*}YN)E5Bwqi(qjds&fl0m=50K3wI1TBe%n+zhXZBibaLCi>F;y-d9lL*MH>| rOUn$om3QkIfC6>yPr#m?k;`yJy1Mbjqo4nPLCfIj>gTe~DWM4fU$Cme literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..c3efa67f837fbc9cc5851d2ec09ce705d1bc7652 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU`+IMaSW+oe0ytS)}a82qaV*- zI(paX^^L%Q0*zQNYmK?LH#R>={vl!4^nkmhv>;%Yc+?A(J}Zr~mvWYk+>A$OyLd19 z_g={GyV~0qbm+$>=7ys&Nlh=Q< zJ!dO^ygOUHQG3Fv|Ldli?To*f`uzN=uk8ZK*JtUR*PLIZwZ3S#ynd*fZk_a9R-vmW zsw=~TEKR3G|E%SZ_IKFwA|qhSyU$x~P2=DG{(Shi#ZG}pxv9aGQ=@<0?f6!7|K*OY zQ`GdIZ7#TX)%Rz{DfjNVPVE`;cFGM5OdJaMh-%K9T5Fl-U7qDhM|P%(6`OryKf<{% z{5<>ZPQmAGm2;F^RICj@-Ef>4WXl*eqeQ*!+LOX1|M&}1Z*)FXu}}gTEi?as)UL{^ V#;mG;OXk;vd$@?2>@%NsSE%B literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/meta.json new file mode 100644 index 0000000000..987b20b9af --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/meta.json @@ -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 + } + ] +} diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES-off.png b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES-off.png new file mode 100644 index 0000000000000000000000000000000000000000..b63f30fc713a739b08ea1070559e2d3b6abf3f6d GIT binary patch literal 452 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|R53 zr;B4q#hkaZ9K8-ZNVLvZ2+=sg&GN=-_pVLq1q&5k=x)-e5L(!M$7$j6723PGU#)W5 z#>Fc#EA)*#M@!aG&dIJ7@0$-8IDayh|E%7wZ z^T!=~_Ut$ULtZq`>HPbLKH)0w_^@&grxzQ>o$a;58P>sLRUl^pFqX^AoC_sf|- zH%2Ayz4<4?T9&WSOzL5xS@Ej(Dhux2*`~2dY(w&WnfHDSyN;9=|6h33dcn>2wQv7Q z)v`MDu{6kVGFS>R6sa&g@nASHkpbcsG>L}w?5&9rY>pRx?sqz^_N1~;=k%T{AC(Rm zG2dQv;OyJEoDS0?dVgL{4!!Iyw=mT1fxtA;7qilMCvVtaXE^hK>f#Gp@*kwHG1Z^^ z*m`w?mzopr E058M1eEetl7au2E?VTg* ze>mZH*ynBOiwY7il zzuUfh`upeu-;V$Mee*l}lzn#luGCr0NffO&X8b^?^`Wi1k8LvP6cPi(H{m>BR zVK9aSHSvrMZx~*tf8TFe(;D0D`F{E|*#~b<+br?kvUrvSOV$1@Zy4U5f8~E^vf7`Q zQ9t$gm({Lg$^wbL{BT*=Jm5PQkN?fTmo_nf`RKy=|5IINoj42WE|71vlDK?Fdg*## Pl4S67^>bP0l+XkKzim56 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..80ae69d53bada105ba124e8045888bb0cc92f94b GIT binary patch literal 505 zcmVPx$vq?ljR9J=Wls`x#K@^8SgJ6*&m?l@?Zl_Ff8e zy@BhkEG+E=K@y0akb^`a5scXEs=Myispg7vf*Z5DL5nMHzG|5_^WN|G-p+tRp-}wq zOa%oSjfQWmQmu(`>#k$-YyYPwZ?RbP%`+V&U>v2=x=80V04&q+6*o@Ey9Ja=>jDT( zEcaEaHL>?&J85+P^s2LHk0(Mce_0-(;%JG zSX^9qb?)Ku3F&-mXIH#g!0MOp!gXw#Ohig)xWg6Vy_kHU` zf&|Fq<#vnZ%Nyc}6s}`a&~-M?jw!cWSf)WXlO&r-(yZ5U9ea4*G7XN74gyVv{)ClI zll=Su9fxlngK{KBJdq+lH{j$}qoA*_d3H=+MCpE*9Xba>i-&dqpf92T-1TQTnfja9 zb!;}GUpduc0OaQe^dd3Oxk-vcNCzy_;PBIDXt(G^VqAQjC;dEM>9kp|vzA)|4bR*s vCG-}Idw8TfdEZAFbszK+DijKZ;-B*e%6-anRB9Xj00000NkvXXu0mjf^S9-c literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..02b00b1591eea9b0c895e3a2a40ac644df2afe07 GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`NC)_YxB_Vj z2_;=a8$0KKVsYJBqH=r0gl}8${Qv)7Ep<;nP@1zO$S;@y$Oi*+rrB43g6W6>n>+WF+O)CB_%75Wh-<(uUxe0gnGBvhlMMSaO!Ba?_$^^ W%W!j9o5K>Il?k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`032l#}z+BpZz5|z_6v?&(Xm5@-{BPRU+|Nq+-JV5!=^5?$*DbA80zhIys z7%&9;R|tUw3p^r=85sBugD~Uq{1quc!9GtH$B+p3x6`bJ4k&Q23i^Ker`{*=*xdNh z63>s@0=RB0mld&NVq{@r5#X>F5||mY)-a@ae%B4Fb6>g2rezoG<15a3zWAYuyXUlK z=E(RtTqmNtf>-*eTYvqX|LTC>J8c061qX&k29ODp7u>Zi`SSPfyM@Ua`Q`kw$>p1? oU(9$^Hht<@`ST7RA_Wey_TQ^(H(6x73g|KhPgg&ebxsLQ0D|>x_W%F@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/meta.json new file mode 100644 index 0000000000..2b65c68fe8 --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/meta.json @@ -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 + } + ] +} diff --git a/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/equipped-EYES.png new file mode 100644 index 0000000000000000000000000000000000000000..9bef0a8c05f0ca0e150da1e9b81aebb13b8dadd7 GIT binary patch literal 524 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=1yvytB`&GO$wiq3C7Jno3=9=> zRF7M8H5dr69j}nF@vcztmTUGcLZM@bZhhV_L6f^To+GC)ukuUkvCh+_+)$V*aYbQomVd z3Im?al0?L61)98Q1L z)#F>sdhq7KjZ2(YT=?OrFX4Ca{>(`+c|}Fb9R5u#KDTbBl&-^KhAj-eOf7jQHtF#1 z{C9t4d)|k8(ccUI#O#}Q&3d|_)#1}Q)9TlSZM*xH?^>P|Z}zsRYx2E|CRDSr8yMfN zS#$PGj%DfLb4$XH>{x5(@Z$8}R*(CP;eAXOm`u1FxG!ihz-%B|faSlOKEG4N`?p{J z=Kd?*S5b7>yJPO6-xqG5SG%+QJzJs7|0w@SOK$ya_{4n40aY>v4(UjYnXdl9N2msQpJGL%i!ti=d#Wzp$Py!<<~I) literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3d5f8ef9b65ccfe2ed64fbc2d10c725c5fad51d2 GIT binary patch literal 510 zcmV~YL`2emQAsH<`}Ol@aPemfRt!=?oD8Bs z_Iz7&h65FI{vYZ{A?OH_900N$L{IK-!7zBCTjc*0Kwsw;*8IP9^%BFvci$Q2H-Gux zR9k@85kxs)>deJpi)|epQTQ;>tT~TiYH$(*15|v)ig}D6#~!fX$nb)p0I%5u9RQ2# z_wU}|RFn3~A4t9htI^O2@kk}_Q@h%{p%Af$|gDeMy z0!0o0fy>j@!r35SKRIv_%m&diTpTEBLGldK&Vt#*SWeIZAVX4t@pvN37|h?^vy>s{ zBOhFT3d1J`R|kCt784yX-vVekwj4sX13&;|pfWW3iAriPteAv@xb1_FVDPUrWmc)9n>XTSEY{`*wGJ@BP4Y`g2W{ z@4IJdS4$$NUAC-T6)(0v=IQU!-9o0C?{~LXu3q)Z-*x{E`|_1p%PfD!?BaNAaVzNkM_opU zgBm^@;?~~-AyO{a$J#D)zw?~=-)Xhw?t3cy`g2}!MkH6I6yIl9=FY(2cjgU8R89XJ TA;as_K-xWB{an^LB{Ts51B{c1 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..4ede078291d212cabdf7a4d5731153a85f0963c1 GIT binary patch literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEVC3<1aSW-L^Y)gZUvq-Qv5(vN zVoi8@dM9^JHV&WAyO49uUdR4NymLM>y%4l$l5Q?cQFwc)^;u?9W)7>Pko}kc-ZOqE zoOia1sNe;f2?Rgl(_(WE8?3oLJ^$Ojn>Szm+*gzr=`xV)W7BD z7WgIbUuIddqrert>EU)yL*kx>{X1yh{dh-RsxpJvySiOlcf;n zfXIf6JpYn!l?xpXT`Q~zopr06im!bN~PV literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/meta.json new file mode 100644 index 0000000000..205508acfa --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/meta.json @@ -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 + } + ] +} diff --git a/Resources/Textures/Shaders/nightvision.swsl b/Resources/Textures/Shaders/nightvision.swsl new file mode 100644 index 0000000000..8a3e7706ad --- /dev/null +++ b/Resources/Textures/Shaders/nightvision.swsl @@ -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; +}