using Robust.Shared.Utility;
using System.Linq;
using System.Threading;
+using Content.Shared.Eye.Blinding.Components;
using static Content.Shared.Interaction.SharedInteractionSystem;
using static Robust.Client.UserInterface.Controls.BoxContainer;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using Content.Shared.Eye.Blinding;
+using Content.Shared.Eye.Blinding.Components;
namespace Content.Client.Eye.Blinding
{
_blindableComponent = blindComp;
- var blind = _blindableComponent.Sources > 0;
+ var blind = _blindableComponent.IsBlind;
if (!blind && _blindableComponent.LightSetup) // Turn FOV back on if we can see again
{
using System.Linq;
using Content.Shared.Administration;
using Content.Shared.Administration.Events;
+using Content.Shared.Eye.Blinding.Components;
using Content.Shared.GameTicking;
using Robust.Shared.GameObjects;
using Robust.Shared.Network;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using Content.Shared.Eye.Blinding;
+using Content.Shared.Eye.Blinding.Components;
namespace Content.Client.Eye.Blinding
{
public sealed class BlurryVisionOverlay : Overlay
{
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
- public override bool RequestScreenTexture => true;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
- private readonly ShaderInstance _dim;
- private BlurryVisionComponent _blurryVisionComponent = default!;
+ private float _magnitude;
public BlurryVisionOverlay()
{
IoCManager.InjectDependencies(this);
- _dim = _prototypeManager.Index<ShaderPrototype>("Dim").InstanceUnique();
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
if (!_entityManager.TryGetComponent<BlurryVisionComponent>(playerEntity, out var blurComp))
return false;
- if (!blurComp.Active)
+ if (blurComp.Magnitude <= 0)
return false;
if (_entityManager.TryGetComponent<BlindableComponent>(playerEntity, out var blindComp)
- && blindComp.Sources > 0)
+ && blindComp.IsBlind)
return false;
- _blurryVisionComponent = blurComp;
+ _magnitude = blurComp.Magnitude;
return true;
}
protected override void Draw(in OverlayDrawArgs args)
{
- if (ScreenTexture == null)
- return;
-
- var opacity = -(_blurryVisionComponent.Magnitude / 15) + 0.9f;
-
- _dim.SetParameter("DAMAGE_AMOUNT", opacity);
-
+ // TODO make this better.
+ // This is a really shitty effect.
+ // Maybe gradually shrink the view-size?
+ // Make the effect only apply to the edge of the viewport?
+ // Actually make it blurry??
+ var opacity = 0.5f * _magnitude / BlurryVisionComponent.MaxMagnitude;
var worldHandle = args.WorldHandle;
var viewport = args.WorldBounds;
-
- worldHandle.UseShader(_dim);
worldHandle.SetTransform(Matrix3.Identity);
- worldHandle.DrawRect(viewport, Color.Black);
- worldHandle.UseShader(null);
+ worldHandle.DrawRect(viewport, Color.White.WithAlpha(opacity));
}
}
}
using Content.Shared.Eye.Blinding;
+using Content.Shared.Eye.Blinding.Components;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Player;
SubscribeLocalEvent<BlurryVisionComponent, PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<BlurryVisionComponent, PlayerDetachedEvent>(OnPlayerDetached);
- SubscribeLocalEvent<BlurryVisionComponent, ComponentHandleState>(OnHandleState);
-
_overlay = new();
}
_overlayMan.RemoveOverlay(_overlay);
}
}
-
- private void OnHandleState(EntityUid uid, BlurryVisionComponent component, ref ComponentHandleState args)
- {
- if (args.Current is not BlurryVisionComponentState state)
- return;
-
- component.Magnitude = state.Magnitude;
- }
}
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Eye.Blinding;
+using Content.Shared.Eye.Blinding.Systems;
using JetBrains.Annotations;
namespace Content.Server.Chemistry.ReagentEffects
{
/// <summary>
- /// Heal eye damage (or deal)
+ /// Heal or apply eye damage
/// </summary>
[UsedImplicitly]
public sealed class ChemHealEyeDamage : ReagentEffect
{
/// <summary>
- /// How much eye damage to remove.
+ /// How much eye damage to add.
/// </summary>
[DataField("amount")]
public int Amount = -1;
public override void Effect(ReagentEffectArgs args)
{
- if (args.Scale != 1f)
+ if (args.Scale != 1f) // huh?
return;
- args.EntityManager.EntitySysManager.GetEntitySystem<SharedBlindingSystem>().AdjustEyeDamage(args.SolutionEntity, Amount);
+ args.EntityManager.EntitySysManager.GetEntitySystem<BlindableSystem>().AdjustEyeDamage(args.SolutionEntity, Amount);
}
}
}
using Content.Shared.Eye.Blinding;
using Content.Server.UserInterface;
using Content.Server.Popups;
+using Content.Shared.Eye.Blinding.Components;
+using Content.Shared.Eye.Blinding.Systems;
using Robust.Shared.Player;
using Robust.Server.GameObjects;
if (args.Cancelled)
return;
- if (TryComp<BlindableComponent>(args.User, out var blindable) && blindable.Sources > 0)
+ if (TryComp<BlindableComponent>(args.User, out var blindable) && blindable.IsBlind)
{
_popupSystem.PopupCursor(Loc.GetString("blindness-fail-attempt"), args.User, Shared.Popups.PopupType.MediumCaution);
args.Cancel();
}
}
- private void OnBlindnessChanged(EntityUid uid, BlindableComponent component, BlindnessChangedEvent args)
+ private void OnBlindnessChanged(EntityUid uid, BlindableComponent component, ref BlindnessChangedEvent args)
{
if (!args.Blind)
return;
-using Content.Shared.Eye.Blinding.EyeProtection; // why aren't tools predicted 🙂
using Content.Shared.Eye.Blinding;
using Content.Shared.StatusEffect;
-using Content.Shared.Clothing.Components;
using Content.Shared.Inventory;
-using Content.Shared.Inventory.Events;
using Content.Server.Tools;
+using Content.Shared.Eye.Blinding.Components;
+using Content.Shared.Eye.Blinding.Systems;
using Content.Shared.Tools.Components;
namespace Content.Server.Eye.Blinding.EyeProtection
public sealed class EyeProtectionSystem : EntitySystem
{
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
- [Dependency] private readonly SharedBlindingSystem _blindingSystem = default!;
+ [Dependency] private readonly BlindableSystem _blindingSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RequiresEyeProtectionComponent, ToolUseAttemptEvent>(OnUseAttempt);
SubscribeLocalEvent<RequiresEyeProtectionComponent, WelderToggledEvent>(OnWelderToggled);
- SubscribeLocalEvent<EyeProtectionComponent, GotEquippedEvent>(OnEquipped);
- SubscribeLocalEvent<EyeProtectionComponent, GotUnequippedEvent>(OnUnequipped);
+ SubscribeLocalEvent<EyeProtectionComponent, GetEyeProtectionEvent>(OnGetProtection);
+ SubscribeLocalEvent<EyeProtectionComponent, InventoryRelayedEvent<GetEyeProtectionEvent>>(OnGetRelayedProtection);
}
- private void OnUseAttempt(EntityUid uid, RequiresEyeProtectionComponent component, ToolUseAttemptEvent args)
+ private void OnGetRelayedProtection(EntityUid uid, EyeProtectionComponent component,
+ InventoryRelayedEvent<GetEyeProtectionEvent> args)
{
- if (!component.Toggled)
- return;
-
- if (!HasComp<StatusEffectsComponent>(args.User) || !TryComp<BlindableComponent>(args.User, out var blindable))
- return;
-
- if (blindable.Sources > 0)
- return;
-
- var statusTime = (float) component.StatusEffectTime.TotalSeconds - blindable.BlindResistance;
-
- if (statusTime <= 0)
- return;
-
- var statusTimeSpan = TimeSpan.FromSeconds(statusTime * (blindable.EyeDamage + 1));
- // Add permanent eye damage if they had zero protection, also scale their temporary blindness by how much they already accumulated.
- if (_statusEffectsSystem.TryAddStatusEffect(args.User, SharedBlindingSystem.BlindingStatusEffect, statusTimeSpan, false, "TemporaryBlindness") && blindable.BlindResistance <= 0)
- _blindingSystem.AdjustEyeDamage(args.User, 1, blindable);
+ OnGetProtection(uid, component, args.Args);
}
- private void OnWelderToggled(EntityUid uid, RequiresEyeProtectionComponent component, WelderToggledEvent args)
+
+ private void OnGetProtection(EntityUid uid, EyeProtectionComponent component, GetEyeProtectionEvent args)
{
- component.Toggled = args.WelderOn;
+ args.Protection += component.ProtectionTime;
}
- private void OnEquipped(EntityUid uid, EyeProtectionComponent component, GotEquippedEvent args)
+ private void OnUseAttempt(EntityUid uid, RequiresEyeProtectionComponent component, ToolUseAttemptEvent args)
{
- if (!TryComp<ClothingComponent>(uid, out var clothing) || clothing.Slots == SlotFlags.PREVENTEQUIP)
+ if (!component.Toggled)
return;
- if (!clothing.Slots.HasFlag(args.SlotFlags))
+ if (!TryComp<BlindableComponent>(args.User, out var blindable) || blindable.IsBlind)
return;
- component.IsActive = true;
- if (!TryComp<BlindableComponent>(args.Equipee, out var blindComp))
+ var ev = new GetEyeProtectionEvent();
+ RaiseLocalEvent(args.User, ev);
+
+ var time = (float) (component.StatusEffectTime- ev.Protection).TotalSeconds;
+ if (time <= 0)
return;
- blindComp.BlindResistance += (float) component.ProtectionTime.TotalSeconds;
+ // Add permanent eye damage if they had zero protection, also somewhat scale their temporary blindness by
+ // how much damage they already accumulated.
+ _blindingSystem.AdjustEyeDamage(args.User, 1, blindable);
+ var statusTimeSpan = TimeSpan.FromSeconds(time * MathF.Sqrt(blindable.EyeDamage));
+ _statusEffectsSystem.TryAddStatusEffect(args.User, TemporaryBlindnessSystem.BlindingStatusEffect,
+ statusTimeSpan, false, TemporaryBlindnessSystem.BlindingStatusEffect);
}
-
- private void OnUnequipped(EntityUid uid, EyeProtectionComponent component, GotUnequippedEvent args)
+ private void OnWelderToggled(EntityUid uid, RequiresEyeProtectionComponent component, WelderToggledEvent args)
{
- if (!component.IsActive)
- return;
- component.IsActive = false;
- if (!TryComp<BlindableComponent>(args.Equipee, out var blindComp))
- return;
-
- blindComp.BlindResistance -= (float) component.ProtectionTime.TotalSeconds;
+ component.Toggled = args.WelderOn;
}
}
}
using Content.Server.Stunnable;
using Content.Shared.Examine;
using Content.Shared.Eye.Blinding;
+using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Flash;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
-using Content.Shared.Eye.Blinding;
using Content.Shared.Speech;
using Content.Shared.Actions;
using Content.Shared.Bed.Sleep;
+using Content.Shared.Eye.Blinding.Systems;
namespace Content.Server.Bed.Sleep
{
public abstract class SharedSleepingSystem : EntitySystem
{
- [Dependency] private readonly SharedBlindingSystem _blindingSystem = default!;
+ [Dependency] private readonly BlindableSystem _blindableSystem = default!;
public override void Initialize()
{
base.Initialize();
- SubscribeLocalEvent<SleepingComponent, ComponentInit>(OnInit);
+ SubscribeLocalEvent<SleepingComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<SleepingComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<SleepingComponent, SpeakAttemptEvent>(OnSpeakAttempt);
+ SubscribeLocalEvent<SleepingComponent, CanSeeAttemptEvent>(OnSeeAttempt);
SubscribeLocalEvent<SleepingComponent, EntityUnpausedEvent>(OnSleepUnpaused);
}
Dirty(component);
}
- private void OnInit(EntityUid uid, SleepingComponent component, ComponentInit args)
+ private void OnStartup(EntityUid uid, SleepingComponent component, ComponentStartup args)
{
var ev = new SleepStateChangedEvent(true);
RaiseLocalEvent(uid, ev);
- _blindingSystem.AdjustBlindSources(uid, 1);
+ _blindableSystem.UpdateIsBlind(uid);
}
private void OnShutdown(EntityUid uid, SleepingComponent component, ComponentShutdown args)
{
var ev = new SleepStateChangedEvent(false);
RaiseLocalEvent(uid, ev);
- _blindingSystem.AdjustBlindSources(uid, -1);
+ _blindableSystem.UpdateIsBlind(uid);
}
private void OnSpeakAttempt(EntityUid uid, SleepingComponent component, SpeakAttemptEvent args)
{
args.Cancel();
}
+
+ private void OnSeeAttempt(EntityUid uid, SleepingComponent component, CanSeeAttemptEvent args)
+ {
+ if (component.LifeStage <= ComponentLifeStage.Running)
+ args.Cancel();
+ }
}
}
using Content.Shared.DragDrop;
using Content.Shared.Interaction;
using Content.Shared.Eye.Blinding;
+using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using JetBrains.Annotations;
{
if (MobStateSystem.IsDead(examiner, mobState))
return DeadExamineRange;
- else if (MobStateSystem.IsCritical(examiner, mobState) || (TryComp<BlindableComponent>(examiner, out var blind) && blind.Sources > 0))
+
+ if (MobStateSystem.IsCritical(examiner, mobState) || TryComp<BlindableComponent>(examiner, out var blind) && blind.IsBlind)
return CritExamineRange;
- else if (TryComp<BlurryVisionComponent>(examiner, out var blurry) && blurry.Magnitude != 0)
- {
- float range = ExamineRange - (2 * (8 - blurry.Magnitude));
- return Math.Clamp(range, 2, 16);
- }
+ if (TryComp<BlurryVisionComponent>(examiner, out var blurry))
+ return Math.Clamp(ExamineRange - blurry.Magnitude, 2, ExamineRange);
}
return ExamineRange;
}
+++ /dev/null
-using Robust.Shared.GameStates;
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Eye.Blinding
-{
- [RegisterComponent]
- [NetworkedComponent]
- public sealed class BlindableComponent : Component
- {
- /// <description>
- /// How many sources of blindness are affecting us?
- /// </description>
- [DataField("sources")]
- public int Sources = 0;
-
- /// <summary>
- /// How many seconds will be subtracted from each attempt to add blindness to us?
- /// </summary>
- [DataField("blindResistance")]
- public float BlindResistance = 0;
-
- /// <summary>
- /// Replace with actual eye damage after bobby I guess
- /// </summary>
- [ViewVariables]
- public int EyeDamage = 0;
-
- /// <summary>
- /// Whether eye damage has accumulated enough to blind them.
- /// </summary>
- [ViewVariables]
- public bool EyeTooDamaged = false;
-
- /// <description>
- /// Used to ensure that this doesn't break with sandbox or admin tools.
- /// This is not "enabled/disabled".
- /// </description>
- public bool LightSetup = false;
-
- /// <description>
- /// Gives an extra frame of blindness to reenable light manager during
- /// </description>
- public bool GraceFrame = false;
- }
-
- [Serializable, NetSerializable]
- public sealed class BlindableComponentState : ComponentState
- {
- public readonly int Sources;
-
- public BlindableComponentState(int sources)
- {
- Sources = sources;
- }
- }
-}
+++ /dev/null
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Eye.Blinding
-{
- [RegisterComponent]
- [NetworkedComponent]
- public sealed class BlindfoldComponent : Component
- {
- [ViewVariables]
- public bool IsActive = false;
- }
-}
+++ /dev/null
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Eye.Blinding
-{
- [RegisterComponent]
- [NetworkedComponent]
- public sealed class BlurryVisionComponent : Component
- {
- [DataField("mangitude")]
- public float Magnitude = 1f;
-
- public bool Active => Magnitude < 10f;
- }
-}
--- /dev/null
+using Content.Shared.Eye.Blinding.Systems;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Eye.Blinding.Components;
+
+[RegisterComponent]
+[NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(BlindableSystem))]
+public sealed partial class BlindableComponent : Component
+{
+ /// <summary>
+ /// How many seconds will be subtracted from each attempt to add blindness to us?
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite), DataField("isBlind"), AutoNetworkedField]
+ public bool IsBlind;
+
+ /// <summary>
+ /// Eye damage due to things like staring directly at welders. Causes blurry vision or outright
+ /// blindness if greater than or equal to <see cref="MaxDamage"/>.
+ /// </summary>
+ /// <remarks>
+ /// Should eventually be replaced with a proper eye health system when we have bobby.
+ /// </remarks>
+ [ViewVariables(VVAccess.ReadWrite), DataField("EyeDamage"), AutoNetworkedField]
+ public int EyeDamage = 0;
+
+ public const int MaxDamage = 8;
+
+ /// <description>
+ /// Used to ensure that this doesn't break with sandbox or admin tools.
+ /// This is not "enabled/disabled".
+ /// </description>
+ [Access(Other = AccessPermissions.ReadWriteExecute)]
+ public bool LightSetup = false;
+
+ /// <description>
+ /// Gives an extra frame of blindness to reenable light manager during
+ /// </description>
+ [Access(Other = AccessPermissions.ReadWriteExecute)]
+ public bool GraceFrame = false;
+}
--- /dev/null
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Eye.Blinding.Components;
+
+/// <summary>
+/// Blinds a person when an item with this component is equipped to the eye, head, or mask slot.
+/// </summary>
+[RegisterComponent]
+[NetworkedComponent]
+public sealed class BlindfoldComponent : Component
+{
+}
--- /dev/null
+using Content.Shared.Eye.Blinding.Systems;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Eye.Blinding.Components;
+
+/// <summary>
+/// This component adds a white overlay to the viewport. It does not actually cause blurring.
+/// </summary>
+[RegisterComponent]
+[NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(BlurryVisionSystem))]
+public sealed partial class BlurryVisionComponent : Component
+{
+ /// <summary>
+ /// Amount of "blurring". Also modifies examine ranges.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite), DataField("magnitude"), AutoNetworkedField]
+ public float Magnitude;
+
+ public const float MaxMagnitude = 10;
+}
--- /dev/null
+namespace Content.Shared.Eye.Blinding.Components;
+
+/// <summary>
+/// For welding masks, sunglasses, etc.
+/// </summary>
+[RegisterComponent]
+public sealed class EyeProtectionComponent : Component
+{
+ /// <summary>
+ /// How many seconds to subtract from the status effect. If it's greater than the source
+ /// of blindness, do not blind.
+ /// </summary>
+ [DataField("protectionTime")]
+ public readonly TimeSpan ProtectionTime = TimeSpan.FromSeconds(10);
+}
--- /dev/null
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Eye.Blinding.Components;
+
+/// <summary>
+/// For tools like welders that will damage your eyes when you use them.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class RequiresEyeProtectionComponent : Component
+{
+ /// <summary>
+ /// How long to apply temporary blindness to the user.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite), DataField("statusEffectTime"), AutoNetworkedField]
+ public TimeSpan StatusEffectTime = TimeSpan.FromSeconds(10);
+
+ /// <summary>
+ /// You probably want to turn this on in yaml if it's something always on and not a welder.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite), DataField("toggled"), AutoNetworkedField]
+ public bool Toggled;
+}
--- /dev/null
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Eye.Blinding.Components;
+
+/// <summary>
+/// Component used for the blind status effect.
+/// </summary>
+[NetworkedComponent, RegisterComponent]
+public sealed class TemporaryBlindnessComponent : Component
+{
+}
--- /dev/null
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Eye.Blinding.Components;
+
+/// <summary>
+/// This component allows equipment to offset blurry vision.
+/// </summary>
+[RegisterComponent]
+[NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class VisionCorrectionComponent : Component
+{
+ [ViewVariables(VVAccess.ReadWrite), DataField("visionBonus"), AutoNetworkedField]
+ public float VisionBonus = 3f;
+}
+++ /dev/null
-namespace Content.Shared.Eye.Blinding.EyeProtection
-{
- /// <summary>
- /// For welding masks, sunglasses, etc.
- /// </summary>
- [RegisterComponent]
- public sealed class EyeProtectionComponent : Component
- {
- /// <summary>
- /// How many seconds to subtract from the status effect. If it's greater than the source
- /// of blindness, do not blind.
- /// </summary>
- [DataField("protectionTime")]
- public TimeSpan ProtectionTime = TimeSpan.FromSeconds(10);
-
- public bool IsActive = false;
- }
-}
+++ /dev/null
-namespace Content.Shared.Eye.Blinding.EyeProtection
-{
- /// <summary>
- /// For tools like welders that will damage your eyes when you use them.
- /// </summary>
- [RegisterComponent]
- public sealed class RequiresEyeProtectionComponent : Component
- {
- /// <summary>
- /// How long to apply temporary blindness to the user.
- /// </summary>
- [DataField("statusEffectTime")]
- public TimeSpan StatusEffectTime = TimeSpan.FromSeconds(10);
-
- /// <summary>
- /// You probably want to turn this on in yaml if it's something always on and not a welder.
- /// </summary>
- [DataField("toggled")]
- public bool Toggled = false;
- }
-}
+++ /dev/null
-using Content.Shared.Clothing.Components;
-using Content.Shared.Inventory.Events;
-using Content.Shared.Inventory;
-using Content.Shared.Rejuvenate;
-using Robust.Shared.GameStates;
-using Robust.Shared.Serialization;
-using JetBrains.Annotations;
-
-namespace Content.Shared.Eye.Blinding
-{
- public sealed class SharedBlindingSystem : EntitySystem
- {
- public const string BlindingStatusEffect = "TemporaryBlindness";
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent<BlindableComponent, ComponentGetState>(OnGetBlindableState);
- SubscribeLocalEvent<BlindableComponent, ComponentHandleState>(OnHandleBlindableState);
-
- SubscribeLocalEvent<BlindfoldComponent, GotEquippedEvent>(OnEquipped);
- SubscribeLocalEvent<BlindfoldComponent, GotUnequippedEvent>(OnUnequipped);
-
- SubscribeLocalEvent<VisionCorrectionComponent, GotEquippedEvent>(OnGlassesEquipped);
- SubscribeLocalEvent<VisionCorrectionComponent, GotUnequippedEvent>(OnGlassesUnequipped);
-
- SubscribeLocalEvent<BlurryVisionComponent, ComponentGetState>(OnGetState);
-
- SubscribeLocalEvent<TemporaryBlindnessComponent, ComponentInit>(OnInit);
- SubscribeLocalEvent<TemporaryBlindnessComponent, ComponentShutdown>(OnShutdown);
-
- SubscribeLocalEvent<BlindableComponent, RejuvenateEvent>(OnRejuvenate);
- }
-
- private void OnGetBlindableState(EntityUid uid, BlindableComponent component, ref ComponentGetState args)
- {
- args.State = new BlindableComponentState(component.Sources);
- }
-
- private void OnHandleBlindableState(EntityUid uid, BlindableComponent component, ref ComponentHandleState args)
- {
- if (args.Current is not BlindableComponentState cast)
- return;
-
- component.Sources = cast.Sources;
- }
-
- private void OnEquipped(EntityUid uid, BlindfoldComponent component, GotEquippedEvent args)
- {
- if (!TryComp<ClothingComponent>(uid, out var clothing) || clothing.Slots == SlotFlags.PREVENTEQUIP) // we live in a society
- return;
- // Is the clothing in its actual slot?
- if (!clothing.Slots.HasFlag(args.SlotFlags))
- return;
-
- component.IsActive = true;
- if (!TryComp<BlindableComponent>(args.Equipee, out var blindComp))
- return;
- AdjustBlindSources(args.Equipee, 1, blindComp);
- }
-
- private void OnUnequipped(EntityUid uid, BlindfoldComponent component, GotUnequippedEvent args)
- {
- if (!component.IsActive)
- return;
- component.IsActive = false;
- if (!TryComp<BlindableComponent>(args.Equipee, out var blindComp))
- return;
- AdjustBlindSources(args.Equipee, -1, blindComp);
- }
-
- private void OnGlassesEquipped(EntityUid uid, VisionCorrectionComponent component, GotEquippedEvent args)
- {
- if (!TryComp<ClothingComponent>(uid, out var clothing) || clothing.Slots == SlotFlags.PREVENTEQUIP) // we live in a society
- return;
- // Is the clothing in its actual slot?
- if (!clothing.Slots.HasFlag(args.SlotFlags))
- return;
-
- if (!TryComp<BlurryVisionComponent>(args.Equipee, out var blur))
- return;
-
- component.IsActive = true;
- blur.Magnitude += component.VisionBonus;
- Dirty(blur);
- }
-
- private void OnGlassesUnequipped(EntityUid uid, VisionCorrectionComponent component, GotUnequippedEvent args)
- {
- if (!component.IsActive || !TryComp<BlurryVisionComponent>(args.Equipee, out var blur))
- return;
- component.IsActive = false;
- blur.Magnitude -= component.VisionBonus;
- Dirty(blur);
- }
-
- private void OnGetState(EntityUid uid, BlurryVisionComponent component, ref ComponentGetState args)
- {
- args.State = new BlurryVisionComponentState(component.Magnitude);
- }
-
- private void OnInit(EntityUid uid, TemporaryBlindnessComponent component, ComponentInit args)
- {
- AdjustBlindSources(uid, 1);
- }
-
- private void OnShutdown(EntityUid uid, TemporaryBlindnessComponent component, ComponentShutdown args)
- {
- AdjustBlindSources(uid, -1);
- }
-
- private void OnRejuvenate(EntityUid uid, BlindableComponent component, RejuvenateEvent args)
- {
- AdjustEyeDamage(uid, -component.EyeDamage, component);
- }
-
- [PublicAPI]
- public void AdjustBlindSources(EntityUid uid, int amount, BlindableComponent? blindable = null)
- {
- if (!Resolve(uid, ref blindable, false))
- return;
-
- var oldSources = blindable.Sources;
-
- blindable.Sources += amount;
- blindable.Sources = Math.Max(blindable.Sources, 0);
-
- if (oldSources == 0 && blindable.Sources > 0)
- {
- var ev = new BlindnessChangedEvent(true);
- RaiseLocalEvent(uid, ev, false);
- }
- else if (blindable.Sources == 0 && oldSources > 0)
- {
- var ev = new BlindnessChangedEvent(false);
- RaiseLocalEvent(uid, ev, false);
- }
-
- Dirty(blindable);
- }
-
- public void AdjustEyeDamage(EntityUid uid, int amount, BlindableComponent? blindable = null)
- {
- if (!Resolve(uid, ref blindable, false))
- return;
-
- blindable.EyeDamage += amount;
-
- if (blindable.EyeDamage > 0)
- {
- var blurry = EnsureComp<BlurryVisionComponent>(uid);
- blurry.Magnitude = (9 - blindable.EyeDamage);
- Dirty(blurry);
- }
- else
- {
- RemComp<BlurryVisionComponent>(uid);
- }
-
- if (!blindable.EyeTooDamaged && blindable.EyeDamage >= 8)
- {
- blindable.EyeTooDamaged = true;
- AdjustBlindSources(uid, 1, blindable);
- }
- if (blindable.EyeTooDamaged && blindable.EyeDamage < 8)
- {
- blindable.EyeTooDamaged = false;
- AdjustBlindSources(uid, -1, blindable);
- }
-
- blindable.EyeDamage = Math.Clamp(blindable.EyeDamage, 0, 8);
- }
- }
-
- // I have no idea why blurry vision needs this but blindness doesn't
- [Serializable, NetSerializable]
- public sealed class BlurryVisionComponentState : ComponentState
- {
- public float Magnitude;
- public BlurryVisionComponentState(float magnitude)
- {
- Magnitude = magnitude;
- }
- }
-
- /// <summary>
- /// You became blind or lost blindess, not just changed # of sources.
- /// </summary>
- public sealed class BlindnessChangedEvent : EntityEventArgs
- {
- public bool Blind;
-
- public BlindnessChangedEvent(bool blind)
- {
- Blind = blind;
- }
- }
-}
--- /dev/null
+using Content.Shared.Eye.Blinding.Components;
+using Content.Shared.Inventory;
+using Content.Shared.Rejuvenate;
+using JetBrains.Annotations;
+
+namespace Content.Shared.Eye.Blinding.Systems;
+
+public sealed class BlindableSystem : EntitySystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent<BlindableComponent, RejuvenateEvent>(OnRejuvenate);
+ }
+
+ private void OnRejuvenate(EntityUid uid, BlindableComponent component, RejuvenateEvent args)
+ {
+ AdjustEyeDamage(uid, -component.EyeDamage, component);
+ }
+
+ [PublicAPI]
+ public void UpdateIsBlind(EntityUid uid, BlindableComponent? blindable = null)
+ {
+ if (!Resolve(uid, ref blindable, false))
+ return;
+
+ var old = blindable.IsBlind;
+
+ // Don't bother raising an event if the eye is too damaged.
+ if (blindable.EyeDamage >= BlindableComponent.MaxDamage)
+ {
+ blindable.IsBlind = true;
+ }
+ else
+ {
+ var ev = new CanSeeAttemptEvent();
+ RaiseLocalEvent(uid, ev);
+ blindable.IsBlind = ev.Blind;
+ }
+
+ if (old == blindable.IsBlind)
+ return;
+
+ var changeEv = new BlindnessChangedEvent(blindable.IsBlind);
+ RaiseLocalEvent(uid, ref changeEv);
+ Dirty(blindable);
+ }
+
+ public void AdjustEyeDamage(EntityUid uid, int amount, BlindableComponent? blindable = null)
+ {
+ if (!Resolve(uid, ref blindable, false) || amount == 0)
+ return;
+
+ blindable.EyeDamage += amount;
+ blindable.EyeDamage = Math.Clamp(blindable.EyeDamage, 0, BlindableComponent.MaxDamage);
+ Dirty(blindable);
+ UpdateIsBlind(uid, blindable);
+
+ var ev = new EyeDamageChangedEvent(blindable.EyeDamage);
+ RaiseLocalEvent(uid, ref ev);
+ }
+}
+
+/// <summary>
+/// This event is raised when an entity's blindness changes
+/// </summary>
+[ByRefEvent]
+public record struct BlindnessChangedEvent(bool Blind);
+
+/// <summary>
+/// This event is raised when an entity's eye damage changes
+/// </summary>
+[ByRefEvent]
+public record struct EyeDamageChangedEvent(int Damage);
+
+/// <summary>
+/// Raised directed at an entity to see whether the entity is currently blind or not.
+/// </summary>
+public sealed class CanSeeAttemptEvent : CancellableEntityEventArgs, IInventoryRelayEvent
+{
+ public bool Blind => Cancelled;
+ public SlotFlags TargetSlots => SlotFlags.EYES | SlotFlags.MASK | SlotFlags.HEAD;
+}
+
+public sealed class GetEyeProtectionEvent : EntityEventArgs, IInventoryRelayEvent
+{
+ /// <summary>
+ /// Time to subtract from any temporary blindness sources.
+ /// </summary>
+ public TimeSpan Protection;
+
+ public SlotFlags TargetSlots => SlotFlags.EYES | SlotFlags.MASK | SlotFlags.HEAD;
+}
--- /dev/null
+using Content.Shared.Eye.Blinding.Components;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Inventory;
+
+namespace Content.Shared.Eye.Blinding.Systems;
+
+public sealed class BlindfoldSystem : EntitySystem
+{
+ [Dependency] private readonly BlindableSystem _blindableSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<BlindfoldComponent, GotEquippedEvent>(OnEquipped);
+ SubscribeLocalEvent<BlindfoldComponent, GotUnequippedEvent>(OnUnequipped);
+ SubscribeLocalEvent<BlindfoldComponent, InventoryRelayedEvent<CanSeeAttemptEvent>>(OnBlindfoldTrySee);
+ }
+
+ private void OnBlindfoldTrySee(EntityUid uid, BlindfoldComponent component, InventoryRelayedEvent<CanSeeAttemptEvent> args)
+ {
+ args.Args.Cancel();
+ }
+
+ private void OnEquipped(EntityUid uid, BlindfoldComponent component, GotEquippedEvent args)
+ {
+ _blindableSystem.UpdateIsBlind(args.Equipee);
+ }
+
+ private void OnUnequipped(EntityUid uid, BlindfoldComponent component, GotUnequippedEvent args)
+ {
+ _blindableSystem.UpdateIsBlind(args.Equipee);
+ }
+}
--- /dev/null
+using Content.Shared.Eye.Blinding.Components;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Inventory;
+
+namespace Content.Shared.Eye.Blinding.Systems;
+
+public sealed class BlurryVisionSystem : EntitySystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<BlindableComponent, EyeDamageChangedEvent>(OnDamageChanged);
+ SubscribeLocalEvent<VisionCorrectionComponent, GotEquippedEvent>(OnGlassesEquipped);
+ SubscribeLocalEvent<VisionCorrectionComponent, GotUnequippedEvent>(OnGlassesUnequipped);
+ SubscribeLocalEvent<VisionCorrectionComponent, InventoryRelayedEvent<GetBlurEvent>>(OnGetBlur);
+ }
+
+ private void OnGetBlur(EntityUid uid, VisionCorrectionComponent component, InventoryRelayedEvent<GetBlurEvent> args)
+ {
+ args.Args.Blur += component.VisionBonus;
+ }
+
+ private void OnDamageChanged(EntityUid uid, BlindableComponent component, ref EyeDamageChangedEvent args)
+ {
+ UpdateBlurMagnitude(uid, component);
+ }
+
+ private void UpdateBlurMagnitude(EntityUid uid, BlindableComponent? component = null)
+ {
+ if (!Resolve(uid, ref component, false))
+ return;
+
+ var ev = new GetBlurEvent(component.EyeDamage);
+ RaiseLocalEvent(uid, ev);
+
+ var blur = Math.Clamp(0, ev.Blur, BlurryVisionComponent.MaxMagnitude);
+ if (blur <= 0)
+ {
+ RemCompDeferred<BlurryVisionComponent>(uid);
+ return;
+ }
+
+ var blurry = EnsureComp<BlurryVisionComponent>(uid);
+ blurry.Magnitude = blur;
+ Dirty(blurry);
+ }
+
+ private void OnGlassesEquipped(EntityUid uid, VisionCorrectionComponent component, GotEquippedEvent args)
+ {
+ UpdateBlurMagnitude(uid);
+ }
+
+ private void OnGlassesUnequipped(EntityUid uid, VisionCorrectionComponent component, GotUnequippedEvent args)
+ {
+ UpdateBlurMagnitude(uid);
+ }
+}
+
+public sealed class GetBlurEvent : EntityEventArgs, IInventoryRelayEvent
+{
+ public readonly float BaseBlur;
+ public float Blur;
+
+ public GetBlurEvent(float blur)
+ {
+ Blur = blur;
+ BaseBlur = blur;
+ }
+
+ public SlotFlags TargetSlots => SlotFlags.HEAD | SlotFlags.MASK | SlotFlags.EYES;
+}
--- /dev/null
+using Content.Shared.Eye.Blinding.Components;
+
+namespace Content.Shared.Eye.Blinding.Systems;
+
+public sealed class TemporaryBlindnessSystem : EntitySystem
+{
+ public const string BlindingStatusEffect = "TemporaryBlindness";
+
+ [Dependency] private readonly BlindableSystem _blindableSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<TemporaryBlindnessComponent, ComponentStartup>(OnStartup);
+ SubscribeLocalEvent<TemporaryBlindnessComponent, ComponentShutdown>(OnShutdown);
+ SubscribeLocalEvent<TemporaryBlindnessComponent, CanSeeAttemptEvent>(OnBlindTrySee);
+ }
+
+ private void OnStartup(EntityUid uid, TemporaryBlindnessComponent component, ComponentStartup args)
+ {
+ _blindableSystem.UpdateIsBlind(uid);
+ }
+
+ private void OnShutdown(EntityUid uid, TemporaryBlindnessComponent component, ComponentShutdown args)
+ {
+ _blindableSystem.UpdateIsBlind(uid);
+ }
+
+ private void OnBlindTrySee(EntityUid uid, TemporaryBlindnessComponent component, CanSeeAttemptEvent args)
+ {
+ if (component.LifeStage <= ComponentLifeStage.Running)
+ args.Cancel();
+ }
+}
+++ /dev/null
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Eye.Blinding
-{
- /// <summary>
- /// Blind status effect.
- /// </summary>
- [NetworkedComponent, RegisterComponent]
- public sealed class TemporaryBlindnessComponent : Component
- {}
-}
+++ /dev/null
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Eye.Blinding
-{
- [RegisterComponent]
- [NetworkedComponent]
- public sealed class VisionCorrectionComponent : Component
- {
- [ViewVariables]
- public bool IsActive = false;
-
- [DataField("visionBonus")]
- public float VisionBonus = 3f;
- }
-}
using Content.Shared.Damage;
using Content.Shared.Electrocution;
using Content.Shared.Explosion;
+using Content.Shared.Eye.Blinding.Systems;
using Content.Shared.IdentityManagement.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Radio;
SubscribeLocalEvent<InventoryComponent, ModifyChangedTemperatureEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, GetDefaultRadioChannelEvent>(RelayInventoryEvent);
+ // Eye/vision events
+ SubscribeLocalEvent<InventoryComponent, CanSeeAttemptEvent>(RelayInventoryEvent);
+ SubscribeLocalEvent<InventoryComponent, GetEyeProtectionEvent>(RelayInventoryEvent);
+ SubscribeLocalEvent<InventoryComponent, GetBlurEvent>(RelayInventoryEvent);
+
SubscribeLocalEvent<InventoryComponent, GetVerbsEvent<EquipmentVerb>>(OnGetStrippingVerbs);
}
using Content.Shared.Examine;
using Content.Shared.Eye.Blinding;
+using Content.Shared.Eye.Blinding.Systems;
using Content.Shared.IdentityManagement;
using Robust.Shared.Network;
public sealed class PermanentBlindnessSystem : EntitySystem
{
[Dependency] private readonly INetManager _net = default!;
- [Dependency] private readonly SharedBlindingSystem _blinding = default!;
+ [Dependency] private readonly BlindableSystem _blinding = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<PermanentBlindnessComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<PermanentBlindnessComponent, ComponentShutdown>(OnShutdown);
+ SubscribeLocalEvent<PermanentBlindnessComponent, CanSeeAttemptEvent>(OnTrySee);
SubscribeLocalEvent<PermanentBlindnessComponent, ExaminedEvent>(OnExamined);
}
private void OnShutdown(EntityUid uid, PermanentBlindnessComponent component, ComponentShutdown args)
{
- _blinding.AdjustBlindSources(uid, -1);
+ _blinding.UpdateIsBlind(uid);
}
private void OnStartup(EntityUid uid, PermanentBlindnessComponent component, ComponentStartup args)
{
- _blinding.AdjustBlindSources(uid, 1);
+ _blinding.UpdateIsBlind(uid);
+ }
+
+ private void OnTrySee(EntityUid uid, PermanentBlindnessComponent component, CanSeeAttemptEvent args)
+ {
+ if (component.LifeStage <= ComponentLifeStage.Running)
+ args.Cancel();
}
}
kind: source
path: "/Textures/Shaders/blurryy.swsl"
-- type: shader
- id: Dim
- kind: source
- path: "/Textures/Shaders/dim.swsl"
-
# cloaking distortion effect
- type: shader
id: Stealth
+++ /dev/null
-uniform highp float DAMAGE_AMOUNT;
-
-void fragment() {
-
- // Higher exponent -> stronger blinding effect
-
- // Gradually mixes between the texture mix and a full-white texture, causing the "blinding" effect
- highp vec4 mixed = mix(vec4(1.0, 1.0, 1.0, 1.0), vec4(1.0, 1.0, 1.0, 1.0), DAMAGE_AMOUNT);
-
- COLOR = vec4(mixed.rgb, DAMAGE_AMOUNT);
-}
-