Улучшение ночного зрения (#2478)

This commit is contained in:
iertis 2025-07-18 03:35:06 +05:00 committed by GitHub
parent 35a01072e8
commit 305735276a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 479 additions and 728 deletions

View file

@ -1,61 +0,0 @@
using System.Numerics;
using Content.Shared._Sunrise.Eye.NightVision.Components;
using Content.Shared.Inventory;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
namespace Content.Client._Sunrise.Eye.NightVision
{
public sealed class NightVisionDeviceOverlay : Overlay
{
[Dependency] private readonly ILightManager _lightManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
public override bool RequestScreenTexture => true;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public ShaderInstance? Shader;
public Color? DisplayColor;
public bool Enabled;
public NightVisionDeviceOverlay()
{
IoCManager.InjectDependencies(this);
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
var playerEntity = _playerManager.LocalSession?.AttachedEntity;
if (playerEntity == null)
return false;
if (!_entityManager.TryGetComponent(playerEntity, out EyeComponent? eyeComp))
return false;
if (args.Viewport.Eye != eyeComp.Eye)
return false;
if (!Enabled)
return false;
// Явный бред
_lightManager.DrawLighting = !Enabled;
return true;
}
protected override void Draw(in OverlayDrawArgs args)
{
if (ScreenTexture == null || Shader == null || DisplayColor == null)
return;
Shader.SetParameter("SCREEN_TEXTURE", ScreenTexture);
var worldHandle = args.WorldHandle;
var viewport = args.WorldBounds;
worldHandle.UseShader(Shader);
worldHandle.DrawRect(viewport, DisplayColor.Value);
worldHandle.UseShader(null);
}
}
}

View file

@ -1,66 +0,0 @@
using Content.Client.Overlays;
using Content.Shared._Sunrise.Eye.NightVision.Components;
using Content.Shared._Sunrise.Eye.NightVision.Systems;
using Content.Shared.Inventory.Events;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Prototypes;
namespace Content.Client._Sunrise.Eye.NightVision;
public sealed class NightVisionDeviceOverlaySystem : EquipmentHudSystem<NightVisionDeviceComponent>
{
[Dependency] private readonly IOverlayManager _overlayMan = default!;
[Dependency] private readonly ILightManager _lightManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
private NightVisionDeviceOverlay _overlay = default!;
public override void Initialize()
{
base.Initialize();
_overlay = new();
SubscribeLocalEvent<NightVisionDeviceComponent, NightVisionDeviceToggledEvent>(OnNightVisionToggled);
}
private void OnNightVisionToggled(EntityUid uid, NightVisionDeviceComponent component, NightVisionDeviceToggledEvent args)
{
var playerEntity = _playerManager.LocalSession?.AttachedEntity;
if (playerEntity == null)
return;
if (playerEntity == args.Equipped)
{
_overlay.Enabled = component.Activated;
// Явный бред
_lightManager.DrawLighting = !component.Activated;
}
}
protected override void UpdateInternal(RefreshEquipmentHudEvent<NightVisionDeviceComponent> component)
{
base.UpdateInternal(component);
foreach (var comp in component.Components)
{
if (_prototypeManager.TryIndex<ShaderPrototype>(comp.DisplayShader, out var shaderPrototype))
_overlay.Shader = shaderPrototype.InstanceUnique();
_overlay.DisplayColor = comp.DisplayColor;
_overlay.Enabled = comp.Activated;
}
if (!_overlayMan.HasOverlay<NightVisionOverlay>())
{
_overlayMan.AddOverlay(_overlay);
}
}
protected override void DeactivateInternal()
{
base.DeactivateInternal();
_overlayMan.RemoveOverlay(_overlay);
// Явный бред
_lightManager.DrawLighting = true;
}
}

View file

@ -1,24 +0,0 @@
using Content.Shared._Sunrise.Eye.NightVision.Components;
using Content.Shared._Sunrise.Eye.NightVision.Systems;
using Robust.Client.GameObjects;
namespace Content.Client._Sunrise.Eye.NightVision;
public sealed class NightVisionDeviceVisualsSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<NightVisionDeviceComponent, AfterNvdUpdateVisualsEvent>(OnAfterNVGUpdateVisualsEvent);
}
private void OnAfterNVGUpdateVisualsEvent(EntityUid uid, NightVisionDeviceComponent component, AfterNvdUpdateVisualsEvent args)
{
if (TryComp<SpriteComponent>(uid, out var sprite))
{
if (sprite.LayerMapTryGet(NVDVisuals.Light, out var layer))
sprite.LayerSetVisible(layer, component.Activated);
}
}
}

View file

@ -1,94 +0,0 @@
using Content.Shared._Sunrise.Eye.NightVision.Components; //creater - vladospupuos
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
namespace Content.Client._Sunrise.Eye.NightVision
{
public sealed class NightVisionOverlay : Overlay
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly ILightManager _lightManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override bool RequestScreenTexture => true;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
private readonly ShaderInstance? _greyscaleShader;
public Color DisplayColor = Color.Green;
private NightVisionComponent _nightVisionComponent = default!;
public NightVisionOverlay()
{
IoCManager.InjectDependencies(this);
if (!_prototypeManager.TryIndex<ShaderPrototype>("GreyscaleFullscreen", out var shaderPrototype))
{
Logger.Error("GreyscaleFullscreen shader not found.");
return;
}
_greyscaleShader = shaderPrototype.InstanceUnique();
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
var playerEntity = _playerManager.LocalSession?.AttachedEntity;
if (playerEntity == null)
return false;
if (!_entityManager.TryGetComponent(playerEntity, out EyeComponent? eyeComp))
return false;
if (args.Viewport.Eye != eyeComp.Eye)
return false;
if (!_entityManager.TryGetComponent<NightVisionComponent>(playerEntity.Value, out var nightvisionComp))
return false;
_nightVisionComponent = nightvisionComp;
DisplayColor = _nightVisionComponent.Color;
var nightvision = _nightVisionComponent.IsNightVision;
if (!nightvision && _nightVisionComponent.DrawShadows) // Disable our Night Vision
{
_lightManager.DrawLighting = true;
_nightVisionComponent.DrawShadows = false;
_nightVisionComponent.GraceFrame = true;
return true;
}
return nightvision;
}
protected override void Draw(in OverlayDrawArgs args)
{
if (ScreenTexture == null)
return;
if (!_nightVisionComponent.GraceFrame)
{
_nightVisionComponent.DrawShadows = true; // Enable our Night Vision
_lightManager.DrawLighting = false;
}
else
{
_nightVisionComponent.GraceFrame = false;
}
if (_nightVisionComponent.IsNightVision)
{
_greyscaleShader?.SetParameter("SCREEN_TEXTURE", ScreenTexture);
var worldHandle = args.WorldHandle;
var viewport = args.WorldBounds;
worldHandle.UseShader(_greyscaleShader);
worldHandle.DrawRect(viewport, DisplayColor);
worldHandle.UseShader(null);
}
}
}
}

View file

@ -1,74 +0,0 @@
using Content.Shared._Sunrise.Eye.NightVision.Components;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Player;
using Content.Shared.Mobs;
namespace Content.Client._Sunrise.Eye.NightVision;
public sealed class NightVisionSystem : EntitySystem
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IOverlayManager _overlayMan = default!;
[Dependency] private readonly ILightManager _lightManager = default!;
private NightVisionOverlay _overlay = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<NightVisionComponent, ComponentInit>(OnNightVisionInit);
SubscribeLocalEvent<NightVisionComponent, ComponentShutdown>(OnNightVisionShutdown);
SubscribeLocalEvent<NightVisionComponent, PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<NightVisionComponent, PlayerDetachedEvent>(OnPlayerDetached);
SubscribeLocalEvent<NightVisionComponent, MobStateChangedEvent>(OnMobStateChanged);
_overlay = new();
}
private void OnMobStateChanged(EntityUid uid, NightVisionComponent component, MobStateChangedEvent args)
{
if (args.NewMobState == MobState.Dead)
{
ResetLighting();
}
}
private void ResetLighting()
{
if (_overlayMan.HasOverlay<NightVisionOverlay>())
_overlayMan.RemoveOverlay(_overlay);
_lightManager.DrawLighting = true;
}
private void OnPlayerAttached(EntityUid uid, NightVisionComponent component, PlayerAttachedEvent args)
{
if (_overlay == default!)
return;
_overlayMan.AddOverlay(_overlay);
}
private void OnPlayerDetached(EntityUid uid, NightVisionComponent component, PlayerDetachedEvent args)
{
if (_overlay == default!)
return;
ResetLighting();
}
private void OnNightVisionInit(EntityUid uid, NightVisionComponent component, ComponentInit args)
{
if (_player.LocalSession?.AttachedEntity != uid)
return;
_overlayMan.AddOverlay(_overlay);
}
private void OnNightVisionShutdown(EntityUid uid, NightVisionComponent component, ComponentShutdown args)
{
if (_player.LocalSession?.AttachedEntity != uid)
return;
if (_overlay == default!)
return;
ResetLighting();
}
}

View file

@ -0,0 +1,59 @@
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
namespace Content.Client._Sunrise.Overlays;
/*
Time to mini rant here. this NEEDs to be a abstract because if its not, then
the overlay system will think its trying to apply multiple of the same overlay
Hence, itll remove all of them until theres only one, even if all the instances
youve added are different based on their fields. So to get around this,
we define all the actual BEHAVIOUR here, but then just make it appear as a new
type by inheriting from this and implementing nothing. Thanks Robust Toolbox team.
*/
public abstract class BaseVisionOverlay : Overlay
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
public override bool RequestScreenTexture => true;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
private protected readonly ShaderInstance Shader;
public BaseVisionOverlay(ShaderPrototype shader)
{
IoCManager.InjectDependencies(this);
Shader = shader.InstanceUnique();
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
if (!_entityManager.TryGetComponent(_playerManager.LocalSession?.AttachedEntity, out EyeComponent? eyeComp))
return false;
if (args.Viewport.Eye != eyeComp.Eye)
return false;
var playerEntity = _playerManager.LocalSession?.AttachedEntity;
if (playerEntity == null)
return false;
return true;
}
protected override void Draw(in OverlayDrawArgs args)
{
if (ScreenTexture == null)
return;
var worldHandle = args.WorldHandle;
var viewport = args.WorldBounds;
Shader.SetParameter("SCREEN_TEXTURE", ScreenTexture);
worldHandle.UseShader(Shader);
worldHandle.DrawRect(viewport, Color.White);
worldHandle.UseShader(null);
}
}

View file

@ -0,0 +1,8 @@
using Robust.Client.Graphics;
namespace Content.Client._Sunrise.Overlays;
public sealed class NightVisionOverlay : BaseVisionOverlay
{
public NightVisionOverlay(ShaderPrototype shader) : base(shader) { ZIndex = (int?)OverlayZIndexes.NightVision; }
}

View file

@ -0,0 +1,7 @@
namespace Content.Client._Sunrise.Overlays;
//order doesnt matter here, we just need them to be unique
public enum OverlayZIndexes
{
NightVision
}

View file

@ -0,0 +1,84 @@
using Content.Shared._Sunrise.NightVision.Components;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.GameStates;
using Content.Shared._Sunrise.NightVision;
namespace Content.Client._Sunrise.Overlays;
public sealed class NightVisionSystem : EntitySystem
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IOverlayManager _overlayMan = default!;
[Dependency] private readonly TransformSystem _xformSys = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private const string NightVisionShaderPrototype = "ModernNightVisionShader";
private NightVisionOverlay _overlay = default!;
[ViewVariables]
private EntityUid? _effect = null;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<NightVisionComponent, ComponentShutdown>(OnVisionShutdown);
SubscribeLocalEvent<NightVisionComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<NightVisionComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
SubscribeLocalEvent<NightVisionComponent, AfterAutoHandleStateEvent>(OnHandleVisionState);
_overlay = new(_prototypeManager.Index<ShaderPrototype>(NightVisionShaderPrototype));
}
private void OnHandleVisionState(Entity<NightVisionComponent> ent, ref AfterAutoHandleStateEvent args)
{
AttemptAddVision(ent.Owner, ent.Comp);
}
private void OnPlayerAttached(Entity<NightVisionComponent> ent, ref LocalPlayerAttachedEvent args)
{
AttemptAddVision(ent.Owner, ent.Comp);
}
private void OnPlayerDetached(Entity<NightVisionComponent> ent, ref LocalPlayerDetachedEvent args)
{
AttemptRemoveVision(ent.Owner, true);
}
private void OnVisionShutdown(Entity<NightVisionComponent> ent, ref ComponentShutdown args)
{
AttemptRemoveVision(ent.Owner);
}
private void AttemptAddVision(EntityUid uid, NightVisionComponent comp)
{
if (_player.LocalSession?.AttachedEntity != uid)
return;
//only add if effect isnt already used
if (_effect != null)
return;
_overlayMan.AddOverlay(_overlay);
_effect = SpawnAttachedTo(comp.Effect, Transform(uid).Coordinates);
_xformSys.SetParent(_effect.Value, uid);
}
/// <summary>
/// Attempt to remove the overlay from the local player.
/// </summary>
/// <param name="uid"></param>
/// <param name="force">Use if you need to forcefully remove the overlay no matter what. Only should be used with events that ONLY the local player can fire, like attach/detach</param>
private void AttemptRemoveVision(EntityUid uid, bool force = false)
{
//ENSURE this is the local player
if (_player.LocalSession?.AttachedEntity != uid && !force)
return;
_overlayMan.RemoveOverlay(_overlay);
Del(_effect);
_effect = null;
}
}

View file

@ -0,0 +1,54 @@
using Content.Shared._Sunrise.NightVision;
using Content.Shared._Sunrise.NightVision.Components;
using Content.Shared._Sunrise.NightVision.Events;
using Content.Shared.Actions;
using Robust.Shared.GameStates;
namespace Content.Server._Sunrise.NightVision;
public sealed class ToggleableNightVisionSystem : EntitySystem
{
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ToggleableNightVisionComponent, ComponentInit>(OnVisionInit);
SubscribeLocalEvent<ToggleableNightVisionComponent, ComponentShutdown>(OnVisionShutdown);
SubscribeLocalEvent<ToggleableNightVisionComponent, ToggleNightVisionEvent>(OnToggleNightVision);
}
private void OnVisionInit(Entity<ToggleableNightVisionComponent> ent, ref ComponentInit args)
{
_actionsSystem.AddAction(ent.Owner, ref ent.Comp.ActionEntity, ent.Comp.Action);
}
private void OnVisionShutdown(Entity<ToggleableNightVisionComponent> ent, ref ComponentShutdown args)
{
_actionsSystem.RemoveAction(ent.Comp.ActionEntity);
RemComp<NightVisionComponent>(ent);
}
private void OnToggleNightVision(Entity<ToggleableNightVisionComponent> ent, ref ToggleNightVisionEvent args)
{
if (args.Handled)
return;
ent.Comp.Active = !ent.Comp.Active;
if (ent.Comp.Active)
ToggleOn(ent, ent.Comp);
else
RemComp<NightVisionComponent>(ent);
args.Handled = true;
}
private void ToggleOn(EntityUid uid, ToggleableNightVisionComponent comp)
{
EnsureComp<NightVisionComponent>(uid, out var vision);
vision.Effect = comp.Effect;
}
}

View file

@ -28,7 +28,6 @@ using Content.Shared.Verbs;
using Content.Shared.Weapons.Ranged.Events;
using Content.Shared.Wieldable;
using Content.Shared.Zombies;
using Content.Shared._Sunrise.Eye.NightVision.Components;
using Content.Shared.Weapons.Ranged.Events; // Sunrise-Edit
namespace Content.Shared.Inventory;
@ -90,7 +89,6 @@ public partial class InventorySystem
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowCriminalRecordIconsComponent>>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<BlackAndWhiteOverlayComponent>>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<NoirOverlayComponent>>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<NightVisionDeviceComponent>>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, GetVerbsEvent<EquipmentVerb>>(OnGetEquipmentVerbs);
SubscribeLocalEvent<InventoryComponent, GetVerbsEvent<InnateVerb>>(OnGetInnateVerbs);

View file

@ -19,6 +19,15 @@ public sealed partial class ItemToggleComponent : Component
[DataField, AutoNetworkedField]
public bool Activated = false;
// Sunrise-start
/// <summary>
/// Можно ли нам активировать если предмет в руках
/// </summary>
[DataField, AutoNetworkedField]
public bool CanActivateInhand = true;
// Sunrise-end
/// <summary>
/// Can the entity be activated in the world.
/// </summary>

View file

@ -111,6 +111,9 @@ public sealed class ItemToggleSystem : EntitySystem
var user = args.User;
// Sunrise-Start
if (!ent.Comp.CanActivateInhand) // Верб позволяет активировать в руке ПНВ и термалы, а нам это не нужно
return;
if (TryComp<HandsComponent>(args.User, out var handsComp))
{
if (!_handsSystem.TryGetActiveItem((args.User, handsComp), out var itemInHand))

View file

@ -1,33 +0,0 @@
using Content.Shared.Actions;
using Content.Shared._Sunrise.Eye.NightVision.Systems;
using Robust.Shared.GameStates;
namespace Content.Shared._Sunrise.Eye.NightVision.Components;
[RegisterComponent]
[NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(NightVisionSystem), typeof(NightVisionDeviceSystem))]
public sealed partial class NightVisionComponent : Component
{
[ViewVariables(VVAccess.ReadWrite), DataField("isOn"), AutoNetworkedField]
public bool IsNightVision;
[DataField("color", required: true)]
public Color Color;
[DataField]
public bool IsToggle;
[DataField] public EntityUid? ActionContainer;
[Access(Other = AccessPermissions.ReadWriteExecute)]
public bool DrawShadows = false;
[Access(Other = AccessPermissions.ReadWriteExecute)]
public bool GraceFrame = false;
}
public sealed partial class NightVisionToggleEvent : InstantActionEvent
{
}

View file

@ -1,48 +0,0 @@
using Content.Shared.Inventory;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared._Sunrise.Eye.NightVision.Components;
[RegisterComponent, NetworkedComponent]
[AutoGenerateComponentState(fieldDeltas: true)]
public sealed partial class NightVisionDeviceComponent : Component
{
[DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string ToggleAction = "NVDToggleAction";
[DataField("toggleActionEntity")]
public EntityUid? ToggleActionEntity;
[DataField("requiredSlot"), AutoNetworkedField]
public SlotFlags RequiredFlags = SlotFlags.EYES;
[DataField("isPowered"), AutoNetworkedField]
public bool IsPowered;
[DataField]
[AutoNetworkedField]
public bool Activated;
[DataField(required: true)]
[AutoNetworkedField]
public Color? DisplayColor;
[DataField(required: true)]
[AutoNetworkedField]
public string DisplayShader;
public SoundSpecifier TurnOnSound = new SoundPathSpecifier("/Audio/_Sunrise/Items/Goggles/activate.ogg");
public SoundSpecifier TurnOffSound = new SoundPathSpecifier("/Audio/_Sunrise/Items/Goggles/deactivate.ogg");
}
[Serializable, NetSerializable]
public enum NVDVisuals : byte
{
Light
}

View file

@ -1,175 +0,0 @@
using Content.Shared._Sunrise.Eye.NightVision.Components;
using Content.Shared.PowerCell;
using Content.Shared.Inventory;
using Content.Shared.Actions;
using Content.Shared.Toggleable;
using Content.Shared.PowerCell.Components;
using Content.Shared.Popups;
using Content.Shared.Containers.ItemSlots;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Network;
using JetBrains.Annotations;
namespace Content.Shared._Sunrise.Eye.NightVision.Systems;
public sealed class NightVisionDeviceSystem : EntitySystem
{
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedPowerCellSystem _cell = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlots = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<NightVisionDeviceComponent, InventoryRelayedEvent<CanVisionAttemptEvent>>(OnNVDTrySee);
SubscribeLocalEvent<NightVisionDeviceComponent, NightVisionDeviceUpdateVisualsEvent>(OnNightVisionDeviceUpdateVisuals);
SubscribeLocalEvent<NightVisionDeviceComponent, PowerCellChangedEvent>(OnPowerCellChanged);
SubscribeLocalEvent<NightVisionDeviceComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
SubscribeLocalEvent<NightVisionDeviceComponent, GetItemActionsEvent>(OnGetActions);
SubscribeLocalEvent<NightVisionDeviceComponent, ToggleActionEvent>(OnToggleAction);
SubscribeLocalEvent<NightVisionDeviceComponent, ComponentShutdown>(OnShutdown);
}
private bool HasPowerAndBattery(EntityUid uid)
{
if (!TryComp<PowerCellSlotComponent>(uid, out var slot))
return false;
if (!_itemSlots.TryGetSlot(uid, slot.CellSlotId, out var itemSlot))
return false;
return itemSlot.Item != null && _cell.HasDrawCharge(uid);
}
private void OnNVDTrySee(EntityUid uid, NightVisionDeviceComponent component, InventoryRelayedEvent<CanVisionAttemptEvent> args)
{
args.Args.Cancel();
}
private void OnNightVisionDeviceUpdateVisuals(EntityUid uid, NightVisionDeviceComponent component, NightVisionDeviceUpdateVisualsEvent args)
{
var updVisEv = new AfterNvdUpdateVisualsEvent();
RaiseLocalEvent(uid, ref updVisEv);
_appearance.SetData(uid, NVDVisuals.Light, component.Activated);
}
private void OnGetActions(EntityUid uid, NightVisionDeviceComponent component, GetItemActionsEvent args)
{
if ((args.SlotFlags & component.RequiredFlags) == component.RequiredFlags)
{
args.AddAction(ref component.ToggleActionEntity, component.ToggleAction);
}
}
private void OnShutdown(EntityUid uid, NightVisionDeviceComponent component, ComponentShutdown args)
{
_actionsSystem.RemoveAction(uid, component.ToggleActionEntity);
}
private void OnPowerCellSlotEmpty(Entity<NightVisionDeviceComponent> ent, ref PowerCellSlotEmptyEvent args)
{
if (ent.Comp.Activated)
ForceDisable(ent);
}
private void OnPowerCellChanged(Entity<NightVisionDeviceComponent> ent, ref PowerCellChangedEvent args)
{
if (args.Ejected || !HasPowerAndBattery(ent.Owner))
{
if (ent.Comp.Activated)
ForceDisable(ent);
}
}
private void OnToggleAction(Entity<NightVisionDeviceComponent> ent, ref ToggleActionEvent args)
{
if (args.Handled)
return;
if (ent.Comp.IsPowered && !HasPowerAndBattery(ent.Owner))
{
_popup.PopupClient(Loc.GetString("base-computer-ui-component-not-powered", ("machine", ent.Owner)), args.Performer, args.Performer);
return;
}
Toggle(ent);
args.Handled = true;
}
private void ForceDisable(Entity<NightVisionDeviceComponent> ent)
{
ent.Comp.Activated = false;
var transform = Transform(ent.Owner);
if (ent.Comp.IsPowered)
{
var draw = Comp<PowerCellDrawComponent>(ent.Owner);
_cell.SetDrawEnabled((ent.Owner, draw), false);
}
_appearance.SetData(ent, ToggleableVisuals.Enabled, false);
var updVisEv = new NightVisionDeviceUpdateVisualsEvent();
RaiseLocalEvent(ent, ref updVisEv);
var equipped = transform.ParentUid;
var changeEv = new NightVisionDeviceToggledEvent(equipped);
RaiseLocalEvent(ent.Owner, ref changeEv);
Dirty(ent);
}
public void Toggle(Entity<NightVisionDeviceComponent> ent)
{
ent.Comp.Activated = !ent.Comp.Activated;
var transform = Transform(ent.Owner);
DirtyField(ent.Owner, ent.Comp, nameof(NightVisionDeviceComponent.Activated));
if (_net.IsServer)
{
var sound = ent.Comp.Activated ? ent.Comp.TurnOnSound : ent.Comp.TurnOffSound;
_audioSystem.PlayPvs(sound, ent.Owner);
}
if (ent.Comp.IsPowered)
{
var draw = Comp<PowerCellDrawComponent>(ent.Owner);
_cell.SetDrawEnabled((ent.Owner, draw), ent.Comp.Activated);
}
_appearance.SetData(ent, ToggleableVisuals.Enabled, ent.Comp.Activated);
var updVisEv = new NightVisionDeviceUpdateVisualsEvent();
RaiseLocalEvent(ent, ref updVisEv);
var equipped = transform.ParentUid;
var changeEv = new NightVisionDeviceToggledEvent(equipped);
RaiseLocalEvent(ent.Owner, ref changeEv);
Dirty(ent);
}
}
[ByRefEvent]
public sealed class NightVisionDeviceToggledEvent : EntityEventArgs
{
public EntityUid Equipped;
public NightVisionDeviceToggledEvent(EntityUid equipped)
{
Equipped = equipped;
}
};
[PublicAPI, ByRefEvent]
public sealed class AfterNvdUpdateVisualsEvent : EntityEventArgs
{
}

View file

@ -1,77 +0,0 @@
using Content.Shared._Sunrise.Eye.NightVision.Components;
using Content.Shared.Inventory;
using Content.Shared.Actions;
using JetBrains.Annotations;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
namespace Content.Shared._Sunrise.Eye.NightVision.Systems;
public sealed class NightVisionSystem : EntitySystem
{
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly INetManager _net = default!;
public override void Initialize()
{
base.Initialize();
if(_net.IsServer)
SubscribeLocalEvent<NightVisionComponent, ComponentStartup>(OnComponentStartup);
SubscribeLocalEvent<NightVisionComponent, NightVisionToggleEvent>(OnActionToggle);
}
[ValidatePrototypeId<EntityPrototype>]
private const string SwitchNightVisionAction = "SwitchNightVision";
private void OnComponentStartup(EntityUid uid, NightVisionComponent component, ComponentStartup args)
{
if (component.IsToggle)
_actionsSystem.AddAction(uid, ref component.ActionContainer, SwitchNightVisionAction);
}
private void OnActionToggle(EntityUid uid, NightVisionComponent component, NightVisionToggleEvent args)
{
component.IsNightVision = !component.IsNightVision;
var changeEv = new NightVisionToggledEvent(component.IsNightVision);
RaiseLocalEvent(uid, ref changeEv);
Dirty(uid, component);
}
[PublicAPI]
public void UpdateIsNightVision(EntityUid uid, NightVisionComponent? component = null)
{
if (!Resolve(uid, ref component, false))
return;
var old = component.IsNightVision;
var ev = new CanVisionAttemptEvent();
RaiseLocalEvent(uid, ev);
component.IsNightVision = ev.CanEnableNightVision;
if (old == component.IsNightVision)
return;
var changeEv = new NightVisionToggledEvent(component.IsNightVision);
RaiseLocalEvent(uid, ref changeEv);
Dirty(uid, component);
}
}
[ByRefEvent]
public record struct NightVisionToggledEvent(bool Enabled);
[PublicAPI, ByRefEvent]
public sealed class NightVisionDeviceUpdateVisualsEvent : EntityEventArgs
{
}
public sealed class CanVisionAttemptEvent : CancellableEntityEventArgs, IInventoryRelayEvent
{
public bool CanEnableNightVision => Cancelled;
public SlotFlags TargetSlots => SlotFlags.EYES | SlotFlags.MASK | SlotFlags.HEAD;
}

View file

@ -0,0 +1,14 @@
using Content.Shared.Eye.Blinding.Systems;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._Sunrise.NightVision.Components;
[RegisterComponent]
[NetworkedComponent]
[AutoGenerateComponentState(true)]
public sealed partial class NightVisionComponent : Component
{
[DataField, AutoNetworkedField]
public EntProtoId Effect = "EffectNightVision";
}

View file

@ -0,0 +1,21 @@
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._Sunrise.NightVision.Components;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class ToggleableNightVisionComponent : Component
{
[DataField]
public EntProtoId Action = "ToggleableNightVision";
[DataField, AutoNetworkedField]
public EntProtoId Effect = "EffectNightVision";
[ViewVariables]
public EntityUid? ActionEntity;
[ViewVariables]
public bool Active;
}

View file

@ -0,0 +1,7 @@
using Content.Shared.Actions;
namespace Content.Shared._Sunrise.NightVision.Events;
public sealed partial class ToggleNightVisionEvent : InstantActionEvent
{
}

View file

@ -263,7 +263,7 @@
coverage: EYES
- type: entity
parent: [ClothingEyesBase, BaseMajorContraband, PowerCellSlotSmallItem] # Sunrise-Edit
parent: [ClothingEyesBase, BaseMajorContraband, PowerCellSlotSmallItem, BaseNightVisionDevice] # Sunrise-Edit
id: ClothingEyesVisorNinja
name: ninja visor
description: An advanced visor protecting a ninja's eyes from flashing lights.
@ -274,10 +274,27 @@
sprite: Clothing/Eyes/Glasses/ninjavisor.rsi
- type: FlashImmunity
# Sunrise-Start
- type: NightVisionDevice
isPowered: true
displayColor: "#44b855"
displayShader: NVDDisplay
- type: ItemToggle
predictable: false
onUse: false
canActivateInhand: false
soundActivate:
path: /Audio/_Sunrise/Items/Goggles/activate.ogg
soundDeactivate:
path: /Audio/_Sunrise/Items/Goggles/deactivate.ogg
soundFailToActivate:
path: /Audio/Machines/button.ogg
- type: PowerCellSlot
cellSlotId: cell_slot
- type: ContainerContainer
containers:
cell_slot: !type:ContainerSlot
- type: ItemSlots
slots:
cell_slot:
name: power-cell-slot-component-slot-name-default
startingItem: PowerCellSmall
- type: ToggleCellDraw
- type: PowerCellDraw
drawRate: 0
useRate: 20

View file

@ -79,9 +79,7 @@
tags:
- VimPilot
# Sunrise-Start
- type: NightVision
isToggle: true
color: "#808080"
- type: ToggleableNightVision
- type: AttackOnInteractionFail
attackMemoryLength: 10
- type: NPCRetaliation
@ -1956,8 +1954,6 @@
recursive: false
# Sunrise-Start
- type: NightVision
isToggle: true
color: "#808080"
- type: HolidayVisuals
holidays:
festive:
@ -2542,8 +2538,6 @@
- type: NPCRetaliation
- type: FactionException
- type: NightVision #Sunrise - Night vision
isToggle: true
color: "#808080"
# Sunrise-End
# Code unique spider prototypes or combine them all into one spider and get a
@ -3257,8 +3251,6 @@
baseSprintSpeed: 4
# Sunrise-start
- type: NightVision
isToggle: true
color: "#808080"
- type: Carriable
- type: CanEscapeInventory
- type: AttackOnInteractionFail
@ -3324,7 +3316,7 @@
factions:
- Syndicate
- type: Access
tags:
tags:
- NuclearOperative
- SyndicateAgent
- type: MeleeWeapon

View file

@ -90,9 +90,7 @@
- type: CollectiveMind
minds:
- Carp
- type: NightVision
isToggle: true
color: "#808080"
- type: ToggleableNightVision
# Sunrise-End
- type: entity

View file

@ -173,9 +173,7 @@
- type: CollectiveMind
minds:
- Carp
- type: NightVision
isToggle: true
color: "#808080"
- type: ToggleableNightVision
- type: Reflect
reflectProb: 0.25
spread: 45

View file

@ -13,7 +13,7 @@
- type: entity
parent: BaseAction
id: SwitchNightVision
id: ToggleableNightVision
name: Switches Night Vision
description: Switches Night Vision
categories: [ HideSpawnMenu ]
@ -22,4 +22,4 @@
useDelay: 1
icon: { sprite: _Sunrise/Clothing/Eyes/Glasses/nvd.rsi, state: icon }
- type: InstantAction
event: !type:NightVisionToggleEvent
event: !type:ToggleNightVisionEvent

View file

@ -74,15 +74,41 @@
- WhitelistChameleon
- type: entity
parent: ClothingEyesGlassesCheapSunglasses
parent: [BaseNightVisionDevice, ClothingEyesGlassesCheapSunglasses, PowerCellSlotSmallItem]
id: ClothingEyesGlassesNVG
name: sun glasses
description: A pair of black sunglasses.
components:
- type: ShowSyndicateIcons
- type: NightVisionDevice
displayColor: "#ff6e6e"
displayShader: NVDDisplay
- type: ItemToggle
predictable: false
onUse: false
canActivateInhand: false
soundActivate:
path: /Audio/_Sunrise/Items/Goggles/activate.ogg
soundDeactivate:
path: /Audio/_Sunrise/Items/Goggles/deactivate.ogg
soundFailToActivate:
path: /Audio/Machines/button.ogg
- type: PowerCellSlot
cellSlotId: cell_slot
- type: ContainerContainer
containers:
cell_slot: !type:ContainerSlot
- type: ItemSlots
slots:
cell_slot:
name: power-cell-slot-component-slot-name-default
startingItem: PowerCellSmall
- type: ToggleCellDraw
- type: PowerCellDraw
drawRate: 0
useRate: 20
- type: ComponentToggler
parent: true
components:
- type: NightVision
effect: EffectNightVisionSyndie
- type: entity
parent: [ClothingEyesGlassesThermal, ShowSecurityIcons, BaseSyndicateContraband]

View file

@ -1,37 +1,77 @@
- type: entity
name: NVD
abstract: true
id: BaseNightVisionDevice
parent: [BaseItem]
components:
- type: Item
- type: Sprite
- type: Clothing
slots: [ Eyes ]
- type: Appearance
- type: UseDelay
delay: 5.0
- type: ToggleClothing
action: NVDToggleAction
mustEquip: true
disableOnUnequip: true
- type: ComponentToggler
parent: true
components:
- type: NightVision
- type: entity
name: NVD
id: ClothingEyesNVD
parent: [BaseItem, PowerCellSlotSmallItem]
parent: [BaseNightVisionDevice, PowerCellSlotSmallItem]
description: Night vision device. Provides an image of the terrain in low-light conditions.
components:
- type: Item
- type: Sprite
sprite: _Sunrise/Clothing/Eyes/Glasses/nvd.rsi
layers:
- state: icon
- state: icon-unshaded
shader: unshaded
- state: light-overlay
- state: icon-flash
visible: false
shader: unshaded
map: [ "enum.NVDVisuals.Light" ]
map: [ "light" ]
- type: Clothing
sprite: _Sunrise/Clothing/Eyes/Glasses/nvd.rsi
quickEquip: true
clothingVisuals:
eyes:
- state: off-equipped-EYES
- state: equipped-EYES-unshaded
shader: unshaded
slots: [ Eyes ]
- type: Appearance
- type: NightVisionDevice
isPowered: true
displayColor: "#82eb6c"
displayShader: NVDDisplay
- type: PowerCellDraw
drawRate: 0
useRate: 20
- type: ItemToggle
predictable: false # issues between ToggleCellDraw and ItemToggleActiveSound
onUse: false
canActivateInhand: false
soundActivate:
path: /Audio/_Sunrise/Items/Goggles/activate.ogg
soundDeactivate:
path: /Audio/_Sunrise/Items/Goggles/deactivate.ogg
soundFailToActivate:
path: /Audio/Machines/button.ogg
- type: ToggleableVisuals
spriteLayer: light
clothingVisuals:
eyes:
- state: on-equipped-EYES
shader: unshaded
- type: PowerCellSlot
cellSlotId: cell_slot
- type: ContainerContainer
containers:
cell_slot: !type:ContainerSlot
- type: ItemSlots
slots:
cell_slot:
name: power-cell-slot-component-slot-name-default
startingItem: PowerCellSmall
- type: ToggleCellDraw
- type: HideLayerClothing
layers:
Hair: HEAD
- type: entity
name: NVD Security
@ -47,14 +87,10 @@
- state: icon
- state: icon-unshaded
shader: unshaded
- state: light-overlay
- state: icon-flash
visible: false
shader: unshaded
map: [ "enum.NVDVisuals.Light" ]
- type: NightVisionDevice
isPowered: true
displayColor: "#82eb6c"
displayShader: NVDDisplay
map: [ "light" ]
- type: entity
name: Med-Security NVD
@ -70,14 +106,10 @@
- state: icon
- state: icon-unshaded
shader: unshaded
- state: light-overlay
- state: icon-flash
visible: false
shader: unshaded
map: [ "enum.NVDVisuals.Light" ]
- type: NightVisionDevice
isPowered: true
displayColor: "#82eb6c"
displayShader: NVDDisplay
map: [ "light" ]
- type: entity
parent: [ClothingEyesNVD, ShowSecurityIcons]
@ -90,15 +122,16 @@
- state: icon
- state: icon-unshaded
shader: unshaded
- state: light-overlay
- state: icon-flash
visible: false
shader: unshaded
map: [ "enum.NVDVisuals.Light" ]
map: [ "light" ]
- type: ShowSyndicateIcons
- type: NightVisionDevice
isPowered: true
displayColor: "#ff6e6e"
displayShader: NVDDisplay
- type: ComponentToggler
parent: true
components:
- type: NightVision
effect: EffectNightVisionSyndie
- type: entity
parent: ClothingEyesNVD
@ -111,14 +144,15 @@
- state: icon
- state: icon-unshaded
shader: unshaded
- state: light-overlay
- state: icon-flash
visible: false
shader: unshaded
map: [ "enum.NVDVisuals.Light" ]
- type: NightVisionDevice
isPowered: true
displayColor: "#f2fa5c"
displayShader: NVDDisplay
map: [ "light" ]
- type: PowerCellDraw
drawRate: 0
useRate: 50
- type: ComponentToggler
parent: true
components:
- type: NightVision
effect: EffectNightVisionHandcraft

View file

@ -0,0 +1,43 @@
- type: entity
id: EffectNightVision
categories: [ HideSpawnMenu ]
name: night vision
components:
- type: PointLight
color: "#12ac27"
energy: 0.8
radius: 20
softness: 20
- type: entity
id: EffectNightVisioSpecies
categories: [ HideSpawnMenu ]
name: night vision
components:
- type: PointLight
color: "#D6E4FF"
energy: 0.1
radius: 10
softness: 20
- type: entity
id: EffectNightVisionHandcraft
categories: [ HideSpawnMenu ]
name: night vision
components:
- type: PointLight
color: "#0ee92b"
energy: 0.1
radius: 20
softness: 20
- type: entity
id: EffectNightVisionSyndie
categories: [ HideSpawnMenu ]
name: night vision
components:
- type: PointLight
color: "#d61b1b"
energy: 0.8
radius: 20
softness: 20

View file

@ -521,13 +521,8 @@
damageSound: /Audio/Effects/hit_kick.ogg
- type: ScaleSprite
scale: 0.72, 0.72
# - type: DamagedByFlashing
# flashDamage:
# types:
# Shock: 5.0
# - type: NightVision
# isToggle: true
# color: "#808080"
- type: ToggleableNightVision
effect: EffectNightVisioSpecies
- type: entity
save: false

View file

@ -64,6 +64,8 @@
speciesId: human
- type: FootprintEmitter
- type: Carriable
- type: ToggleableNightVision
effect: EffectNightVisioSpecies
- type: entity
save: false

View file

@ -89,6 +89,8 @@
Blunt: 2
Slash: 3
- type: Carriable
- type: ToggleableNightVision
effect: EffectNightVisioSpecies
- type: entity

View file

@ -25,3 +25,12 @@
path: "/Textures/_Sunrise/Shaders/brightness.swsl"
params:
brightness: 1.5
- type: shader
id: ModernNightVisionShader
kind: source
path: "/Textures/_Sunrise/Shaders/night.swsl"
params:
NightVisionBoost: 2.0
NightVisionThreshold: 0.3
BlueTintIntensity: 0.6

View file

@ -0,0 +1,23 @@
uniform sampler2D SCREEN_TEXTURE;
uniform highp float NightVisionBoost;
uniform highp float NightVisionThreshold;
uniform highp float BlueTintIntensity;
const highp vec3 blueTintColor = vec3(0.35, 0.7, 1.0);
void fragment()
{
COLOR = zTextureSpec(SCREEN_TEXTURE, Pos);
highp vec3 finalColor = COLOR.rgb;
highp float brightness = dot(finalColor, vec3(0.299, 0.587, 0.114));
highp float factor = smoothstep(0.0, NightVisionThreshold, brightness);
finalColor = mix(finalColor * NightVisionBoost, finalColor, factor);
finalColor = mix(finalColor, finalColor * blueTintColor, (1.0 - factor) * BlueTintIntensity);
COLOR.rgb = finalColor;
}