diff --git a/Content.Client/Examine/ExamineSystem.cs b/Content.Client/Examine/ExamineSystem.cs index 9b3e209c3b..a33671c3db 100644 --- a/Content.Client/Examine/ExamineSystem.cs +++ b/Content.Client/Examine/ExamineSystem.cs @@ -15,6 +15,7 @@ using Robust.Shared.Map; 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; diff --git a/Content.Client/Eye/Blinding/BlindOverlay.cs b/Content.Client/Eye/Blinding/BlindOverlay.cs index fc7367d715..1ade23c14d 100644 --- a/Content.Client/Eye/Blinding/BlindOverlay.cs +++ b/Content.Client/Eye/Blinding/BlindOverlay.cs @@ -4,6 +4,7 @@ using Robust.Client.Player; using Robust.Shared.Enums; using Robust.Shared.Prototypes; using Content.Shared.Eye.Blinding; +using Content.Shared.Eye.Blinding.Components; namespace Content.Client.Eye.Blinding { @@ -46,7 +47,7 @@ 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 { diff --git a/Content.Client/Eye/Blinding/BlindingSystem.cs b/Content.Client/Eye/Blinding/BlindingSystem.cs index 5c7176dc77..f0b760d838 100644 --- a/Content.Client/Eye/Blinding/BlindingSystem.cs +++ b/Content.Client/Eye/Blinding/BlindingSystem.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; 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; diff --git a/Content.Client/Eye/Blinding/BlurryVisionOverlay.cs b/Content.Client/Eye/Blinding/BlurryVisionOverlay.cs index 376b415fab..9ab90d16c9 100644 --- a/Content.Client/Eye/Blinding/BlurryVisionOverlay.cs +++ b/Content.Client/Eye/Blinding/BlurryVisionOverlay.cs @@ -4,24 +4,21 @@ using Robust.Client.Player; 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("Dim").InstanceUnique(); } protected override bool BeforeDraw(in OverlayDrawArgs args) @@ -40,33 +37,29 @@ namespace Content.Client.Eye.Blinding if (!_entityManager.TryGetComponent(playerEntity, out var blurComp)) return false; - if (!blurComp.Active) + if (blurComp.Magnitude <= 0) return false; if (_entityManager.TryGetComponent(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)); } } } diff --git a/Content.Client/Eye/Blinding/BlurryVisionSystem.cs b/Content.Client/Eye/Blinding/BlurryVisionSystem.cs index 3e8afd9e4d..1bac2a97bf 100644 --- a/Content.Client/Eye/Blinding/BlurryVisionSystem.cs +++ b/Content.Client/Eye/Blinding/BlurryVisionSystem.cs @@ -1,4 +1,5 @@ using Content.Shared.Eye.Blinding; +using Content.Shared.Eye.Blinding.Components; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Player; @@ -22,8 +23,6 @@ public sealed class BlurryVisionSystem : EntitySystem SubscribeLocalEvent(OnPlayerAttached); SubscribeLocalEvent(OnPlayerDetached); - SubscribeLocalEvent(OnHandleState); - _overlay = new(); } @@ -50,12 +49,4 @@ public sealed class BlurryVisionSystem : EntitySystem _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; - } } diff --git a/Content.Server/Chemistry/ReagentEffects/ChemHealEyeDamage.cs b/Content.Server/Chemistry/ReagentEffects/ChemHealEyeDamage.cs index fb6d648402..0826e99110 100644 --- a/Content.Server/Chemistry/ReagentEffects/ChemHealEyeDamage.cs +++ b/Content.Server/Chemistry/ReagentEffects/ChemHealEyeDamage.cs @@ -1,27 +1,28 @@ using Content.Shared.Chemistry.Reagent; using Content.Shared.Eye.Blinding; +using Content.Shared.Eye.Blinding.Systems; using JetBrains.Annotations; namespace Content.Server.Chemistry.ReagentEffects { /// - /// Heal eye damage (or deal) + /// Heal or apply eye damage /// [UsedImplicitly] public sealed class ChemHealEyeDamage : ReagentEffect { /// - /// How much eye damage to remove. + /// How much eye damage to add. /// [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().AdjustEyeDamage(args.SolutionEntity, Amount); + args.EntityManager.EntitySysManager.GetEntitySystem().AdjustEyeDamage(args.SolutionEntity, Amount); } } } diff --git a/Content.Server/Eye/Blinding/ActivatableUIRequiresVisionSystem.cs b/Content.Server/Eye/Blinding/ActivatableUIRequiresVisionSystem.cs index 4b5d3d3eef..cbecba701c 100644 --- a/Content.Server/Eye/Blinding/ActivatableUIRequiresVisionSystem.cs +++ b/Content.Server/Eye/Blinding/ActivatableUIRequiresVisionSystem.cs @@ -1,6 +1,8 @@ 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; @@ -23,14 +25,14 @@ public sealed class ActivatableUIRequiresVisionSystem : EntitySystem if (args.Cancelled) return; - if (TryComp(args.User, out var blindable) && blindable.Sources > 0) + if (TryComp(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; diff --git a/Content.Server/Eye/Blinding/EyeProtection/EyeProtectionSystem.cs b/Content.Server/Eye/Blinding/EyeProtection/EyeProtectionSystem.cs index 4872cd359a..d31f9eeed6 100644 --- a/Content.Server/Eye/Blinding/EyeProtection/EyeProtectionSystem.cs +++ b/Content.Server/Eye/Blinding/EyeProtection/EyeProtectionSystem.cs @@ -1,10 +1,9 @@ -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 @@ -12,15 +11,26 @@ 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(OnUseAttempt); SubscribeLocalEvent(OnWelderToggled); - SubscribeLocalEvent(OnEquipped); - SubscribeLocalEvent(OnUnequipped); + SubscribeLocalEvent(OnGetProtection); + SubscribeLocalEvent>(OnGetRelayedProtection); + } + + private void OnGetRelayedProtection(EntityUid uid, EyeProtectionComponent component, + InventoryRelayedEvent args) + { + OnGetProtection(uid, component, args.Args); + } + + private void OnGetProtection(EntityUid uid, EyeProtectionComponent component, GetEyeProtectionEvent args) + { + args.Protection += component.ProtectionTime; } private void OnUseAttempt(EntityUid uid, RequiresEyeProtectionComponent component, ToolUseAttemptEvent args) @@ -28,51 +38,26 @@ namespace Content.Server.Eye.Blinding.EyeProtection if (!component.Toggled) return; - if (!HasComp(args.User) || !TryComp(args.User, out var blindable)) + if (!TryComp(args.User, out var blindable) || blindable.IsBlind) return; - if (blindable.Sources > 0) + var ev = new GetEyeProtectionEvent(); + RaiseLocalEvent(args.User, ev); + + var time = (float) (component.StatusEffectTime- ev.Protection).TotalSeconds; + if (time <= 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); + // 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 OnWelderToggled(EntityUid uid, RequiresEyeProtectionComponent component, WelderToggledEvent args) { component.Toggled = args.WelderOn; } - - private void OnEquipped(EntityUid uid, EyeProtectionComponent component, GotEquippedEvent args) - { - if (!TryComp(uid, out var clothing) || clothing.Slots == SlotFlags.PREVENTEQUIP) - return; - - if (!clothing.Slots.HasFlag(args.SlotFlags)) - return; - - component.IsActive = true; - if (!TryComp(args.Equipee, out var blindComp)) - return; - - blindComp.BlindResistance += (float) component.ProtectionTime.TotalSeconds; - } - - private void OnUnequipped(EntityUid uid, EyeProtectionComponent component, GotUnequippedEvent args) - { - if (!component.IsActive) - return; - component.IsActive = false; - if (!TryComp(args.Equipee, out var blindComp)) - return; - - blindComp.BlindResistance -= (float) component.ProtectionTime.TotalSeconds; - } } } diff --git a/Content.Server/Flash/FlashSystem.cs b/Content.Server/Flash/FlashSystem.cs index b10864b829..016ea7e781 100644 --- a/Content.Server/Flash/FlashSystem.cs +++ b/Content.Server/Flash/FlashSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Light.EntitySystems; 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; diff --git a/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs b/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs index 2435711c71..458a69b621 100644 --- a/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs +++ b/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs @@ -1,20 +1,21 @@ -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(OnInit); + SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnSpeakAttempt); + SubscribeLocalEvent(OnSeeAttempt); SubscribeLocalEvent(OnSleepUnpaused); } @@ -24,24 +25,30 @@ namespace Content.Server.Bed.Sleep 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(); + } } } diff --git a/Content.Shared/Examine/ExamineSystemShared.cs b/Content.Shared/Examine/ExamineSystemShared.cs index 1eed657d0f..77ab083a2f 100644 --- a/Content.Shared/Examine/ExamineSystemShared.cs +++ b/Content.Shared/Examine/ExamineSystemShared.cs @@ -2,6 +2,7 @@ using System.Linq; 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; @@ -122,14 +123,12 @@ namespace Content.Shared.Examine { if (MobStateSystem.IsDead(examiner, mobState)) return DeadExamineRange; - else if (MobStateSystem.IsCritical(examiner, mobState) || (TryComp(examiner, out var blind) && blind.Sources > 0)) + + if (MobStateSystem.IsCritical(examiner, mobState) || TryComp(examiner, out var blind) && blind.IsBlind) return CritExamineRange; - else if (TryComp(examiner, out var blurry) && blurry.Magnitude != 0) - { - float range = ExamineRange - (2 * (8 - blurry.Magnitude)); - return Math.Clamp(range, 2, 16); - } + if (TryComp(examiner, out var blurry)) + return Math.Clamp(ExamineRange - blurry.Magnitude, 2, ExamineRange); } return ExamineRange; } diff --git a/Content.Shared/Eye/Blinding/BlindableComponent.cs b/Content.Shared/Eye/Blinding/BlindableComponent.cs deleted file mode 100644 index 8cb34bc4b3..0000000000 --- a/Content.Shared/Eye/Blinding/BlindableComponent.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Robust.Shared.GameStates; -using Robust.Shared.Serialization; - -namespace Content.Shared.Eye.Blinding -{ - [RegisterComponent] - [NetworkedComponent] - public sealed class BlindableComponent : Component - { - /// - /// How many sources of blindness are affecting us? - /// - [DataField("sources")] - public int Sources = 0; - - /// - /// How many seconds will be subtracted from each attempt to add blindness to us? - /// - [DataField("blindResistance")] - public float BlindResistance = 0; - - /// - /// Replace with actual eye damage after bobby I guess - /// - [ViewVariables] - public int EyeDamage = 0; - - /// - /// Whether eye damage has accumulated enough to blind them. - /// - [ViewVariables] - public bool EyeTooDamaged = false; - - /// - /// Used to ensure that this doesn't break with sandbox or admin tools. - /// This is not "enabled/disabled". - /// - public bool LightSetup = false; - - /// - /// Gives an extra frame of blindness to reenable light manager during - /// - public bool GraceFrame = false; - } - - [Serializable, NetSerializable] - public sealed class BlindableComponentState : ComponentState - { - public readonly int Sources; - - public BlindableComponentState(int sources) - { - Sources = sources; - } - } -} diff --git a/Content.Shared/Eye/Blinding/BlindfoldComponent.cs b/Content.Shared/Eye/Blinding/BlindfoldComponent.cs deleted file mode 100644 index d1bdce423a..0000000000 --- a/Content.Shared/Eye/Blinding/BlindfoldComponent.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Eye.Blinding -{ - [RegisterComponent] - [NetworkedComponent] - public sealed class BlindfoldComponent : Component - { - [ViewVariables] - public bool IsActive = false; - } -} diff --git a/Content.Shared/Eye/Blinding/BlurryVisionComponent.cs b/Content.Shared/Eye/Blinding/BlurryVisionComponent.cs deleted file mode 100644 index bbf518b2b4..0000000000 --- a/Content.Shared/Eye/Blinding/BlurryVisionComponent.cs +++ /dev/null @@ -1,14 +0,0 @@ -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; - } -} diff --git a/Content.Shared/Eye/Blinding/Components/BlindableComponent.cs b/Content.Shared/Eye/Blinding/Components/BlindableComponent.cs new file mode 100644 index 0000000000..32bd4c1c79 --- /dev/null +++ b/Content.Shared/Eye/Blinding/Components/BlindableComponent.cs @@ -0,0 +1,41 @@ +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 +{ + /// + /// How many seconds will be subtracted from each attempt to add blindness to us? + /// + [ViewVariables(VVAccess.ReadWrite), DataField("isBlind"), AutoNetworkedField] + public bool IsBlind; + + /// + /// Eye damage due to things like staring directly at welders. Causes blurry vision or outright + /// blindness if greater than or equal to . + /// + /// + /// Should eventually be replaced with a proper eye health system when we have bobby. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("EyeDamage"), AutoNetworkedField] + public int EyeDamage = 0; + + public const int MaxDamage = 8; + + /// + /// Used to ensure that this doesn't break with sandbox or admin tools. + /// This is not "enabled/disabled". + /// + [Access(Other = AccessPermissions.ReadWriteExecute)] + public bool LightSetup = false; + + /// + /// Gives an extra frame of blindness to reenable light manager during + /// + [Access(Other = AccessPermissions.ReadWriteExecute)] + public bool GraceFrame = false; +} diff --git a/Content.Shared/Eye/Blinding/Components/BlindfoldComponent.cs b/Content.Shared/Eye/Blinding/Components/BlindfoldComponent.cs new file mode 100644 index 0000000000..85268d8545 --- /dev/null +++ b/Content.Shared/Eye/Blinding/Components/BlindfoldComponent.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Eye.Blinding.Components; + +/// +/// Blinds a person when an item with this component is equipped to the eye, head, or mask slot. +/// +[RegisterComponent] +[NetworkedComponent] +public sealed class BlindfoldComponent : Component +{ +} diff --git a/Content.Shared/Eye/Blinding/Components/BlurryVisionComponent.cs b/Content.Shared/Eye/Blinding/Components/BlurryVisionComponent.cs new file mode 100644 index 0000000000..8182dfe974 --- /dev/null +++ b/Content.Shared/Eye/Blinding/Components/BlurryVisionComponent.cs @@ -0,0 +1,21 @@ +using Content.Shared.Eye.Blinding.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared.Eye.Blinding.Components; + +/// +/// This component adds a white overlay to the viewport. It does not actually cause blurring. +/// +[RegisterComponent] +[NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(BlurryVisionSystem))] +public sealed partial class BlurryVisionComponent : Component +{ + /// + /// Amount of "blurring". Also modifies examine ranges. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("magnitude"), AutoNetworkedField] + public float Magnitude; + + public const float MaxMagnitude = 10; +} diff --git a/Content.Shared/Eye/Blinding/Components/EyeProtectionComponent.cs b/Content.Shared/Eye/Blinding/Components/EyeProtectionComponent.cs new file mode 100644 index 0000000000..eaf3bdb924 --- /dev/null +++ b/Content.Shared/Eye/Blinding/Components/EyeProtectionComponent.cs @@ -0,0 +1,15 @@ +namespace Content.Shared.Eye.Blinding.Components; + +/// +/// For welding masks, sunglasses, etc. +/// +[RegisterComponent] +public sealed class EyeProtectionComponent : Component +{ + /// + /// How many seconds to subtract from the status effect. If it's greater than the source + /// of blindness, do not blind. + /// + [DataField("protectionTime")] + public readonly TimeSpan ProtectionTime = TimeSpan.FromSeconds(10); +} diff --git a/Content.Shared/Eye/Blinding/Components/RequiresEyeProtectionComponent.cs b/Content.Shared/Eye/Blinding/Components/RequiresEyeProtectionComponent.cs new file mode 100644 index 0000000000..f12d174d9c --- /dev/null +++ b/Content.Shared/Eye/Blinding/Components/RequiresEyeProtectionComponent.cs @@ -0,0 +1,22 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Eye.Blinding.Components; + +/// +/// For tools like welders that will damage your eyes when you use them. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class RequiresEyeProtectionComponent : Component +{ + /// + /// How long to apply temporary blindness to the user. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("statusEffectTime"), AutoNetworkedField] + public TimeSpan StatusEffectTime = TimeSpan.FromSeconds(10); + + /// + /// You probably want to turn this on in yaml if it's something always on and not a welder. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("toggled"), AutoNetworkedField] + public bool Toggled; +} diff --git a/Content.Shared/Eye/Blinding/Components/TemporaryBlindnessComponent.cs b/Content.Shared/Eye/Blinding/Components/TemporaryBlindnessComponent.cs new file mode 100644 index 0000000000..41049e3671 --- /dev/null +++ b/Content.Shared/Eye/Blinding/Components/TemporaryBlindnessComponent.cs @@ -0,0 +1,11 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Eye.Blinding.Components; + +/// +/// Component used for the blind status effect. +/// +[NetworkedComponent, RegisterComponent] +public sealed class TemporaryBlindnessComponent : Component +{ +} diff --git a/Content.Shared/Eye/Blinding/Components/VisionCorrectionComponent.cs b/Content.Shared/Eye/Blinding/Components/VisionCorrectionComponent.cs new file mode 100644 index 0000000000..54f0833c37 --- /dev/null +++ b/Content.Shared/Eye/Blinding/Components/VisionCorrectionComponent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Eye.Blinding.Components; + +/// +/// This component allows equipment to offset blurry vision. +/// +[RegisterComponent] +[NetworkedComponent, AutoGenerateComponentState] +public sealed partial class VisionCorrectionComponent : Component +{ + [ViewVariables(VVAccess.ReadWrite), DataField("visionBonus"), AutoNetworkedField] + public float VisionBonus = 3f; +} diff --git a/Content.Shared/Eye/Blinding/EyeProtection/EyeProtectionComponent.cs b/Content.Shared/Eye/Blinding/EyeProtection/EyeProtectionComponent.cs deleted file mode 100644 index 0eb360b41b..0000000000 --- a/Content.Shared/Eye/Blinding/EyeProtection/EyeProtectionComponent.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Content.Shared.Eye.Blinding.EyeProtection -{ - /// - /// For welding masks, sunglasses, etc. - /// - [RegisterComponent] - public sealed class EyeProtectionComponent : Component - { - /// - /// How many seconds to subtract from the status effect. If it's greater than the source - /// of blindness, do not blind. - /// - [DataField("protectionTime")] - public TimeSpan ProtectionTime = TimeSpan.FromSeconds(10); - - public bool IsActive = false; - } -} diff --git a/Content.Shared/Eye/Blinding/EyeProtection/RequiresEyeProtectionComponent.cs b/Content.Shared/Eye/Blinding/EyeProtection/RequiresEyeProtectionComponent.cs deleted file mode 100644 index 7ec3eefab3..0000000000 --- a/Content.Shared/Eye/Blinding/EyeProtection/RequiresEyeProtectionComponent.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Content.Shared.Eye.Blinding.EyeProtection -{ - /// - /// For tools like welders that will damage your eyes when you use them. - /// - [RegisterComponent] - public sealed class RequiresEyeProtectionComponent : Component - { - /// - /// How long to apply temporary blindness to the user. - /// - [DataField("statusEffectTime")] - public TimeSpan StatusEffectTime = TimeSpan.FromSeconds(10); - - /// - /// You probably want to turn this on in yaml if it's something always on and not a welder. - /// - [DataField("toggled")] - public bool Toggled = false; - } -} diff --git a/Content.Shared/Eye/Blinding/SharedBlindingSystem.cs b/Content.Shared/Eye/Blinding/SharedBlindingSystem.cs deleted file mode 100644 index aa5e2b8cc8..0000000000 --- a/Content.Shared/Eye/Blinding/SharedBlindingSystem.cs +++ /dev/null @@ -1,198 +0,0 @@ -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(OnGetBlindableState); - SubscribeLocalEvent(OnHandleBlindableState); - - SubscribeLocalEvent(OnEquipped); - SubscribeLocalEvent(OnUnequipped); - - SubscribeLocalEvent(OnGlassesEquipped); - SubscribeLocalEvent(OnGlassesUnequipped); - - SubscribeLocalEvent(OnGetState); - - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - - SubscribeLocalEvent(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(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(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(args.Equipee, out var blindComp)) - return; - AdjustBlindSources(args.Equipee, -1, blindComp); - } - - private void OnGlassesEquipped(EntityUid uid, VisionCorrectionComponent component, GotEquippedEvent args) - { - if (!TryComp(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(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(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(uid); - blurry.Magnitude = (9 - blindable.EyeDamage); - Dirty(blurry); - } - else - { - RemComp(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; - } - } - - /// - /// You became blind or lost blindess, not just changed # of sources. - /// - public sealed class BlindnessChangedEvent : EntityEventArgs - { - public bool Blind; - - public BlindnessChangedEvent(bool blind) - { - Blind = blind; - } - } -} diff --git a/Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs b/Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs new file mode 100644 index 0000000000..65ecc1deec --- /dev/null +++ b/Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs @@ -0,0 +1,93 @@ +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(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); + } +} + +/// +/// This event is raised when an entity's blindness changes +/// +[ByRefEvent] +public record struct BlindnessChangedEvent(bool Blind); + +/// +/// This event is raised when an entity's eye damage changes +/// +[ByRefEvent] +public record struct EyeDamageChangedEvent(int Damage); + +/// +/// Raised directed at an entity to see whether the entity is currently blind or not. +/// +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 +{ + /// + /// Time to subtract from any temporary blindness sources. + /// + public TimeSpan Protection; + + public SlotFlags TargetSlots => SlotFlags.EYES | SlotFlags.MASK | SlotFlags.HEAD; +} diff --git a/Content.Shared/Eye/Blinding/Systems/BlindfoldSystem.cs b/Content.Shared/Eye/Blinding/Systems/BlindfoldSystem.cs new file mode 100644 index 0000000000..b875c13f64 --- /dev/null +++ b/Content.Shared/Eye/Blinding/Systems/BlindfoldSystem.cs @@ -0,0 +1,34 @@ +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(OnEquipped); + SubscribeLocalEvent(OnUnequipped); + SubscribeLocalEvent>(OnBlindfoldTrySee); + } + + private void OnBlindfoldTrySee(EntityUid uid, BlindfoldComponent component, InventoryRelayedEvent 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); + } +} diff --git a/Content.Shared/Eye/Blinding/Systems/BlurryVisionSystem.cs b/Content.Shared/Eye/Blinding/Systems/BlurryVisionSystem.cs new file mode 100644 index 0000000000..4c9fb02afa --- /dev/null +++ b/Content.Shared/Eye/Blinding/Systems/BlurryVisionSystem.cs @@ -0,0 +1,72 @@ +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(OnDamageChanged); + SubscribeLocalEvent(OnGlassesEquipped); + SubscribeLocalEvent(OnGlassesUnequipped); + SubscribeLocalEvent>(OnGetBlur); + } + + private void OnGetBlur(EntityUid uid, VisionCorrectionComponent component, InventoryRelayedEvent 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(uid); + return; + } + + var blurry = EnsureComp(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; +} diff --git a/Content.Shared/Eye/Blinding/Systems/TemporaryBlindnessSystem.cs b/Content.Shared/Eye/Blinding/Systems/TemporaryBlindnessSystem.cs new file mode 100644 index 0000000000..431c40d316 --- /dev/null +++ b/Content.Shared/Eye/Blinding/Systems/TemporaryBlindnessSystem.cs @@ -0,0 +1,35 @@ +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(OnStartup); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(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(); + } +} diff --git a/Content.Shared/Eye/Blinding/TemporaryBlindnessComponent.cs b/Content.Shared/Eye/Blinding/TemporaryBlindnessComponent.cs deleted file mode 100644 index 0c6ccc3980..0000000000 --- a/Content.Shared/Eye/Blinding/TemporaryBlindnessComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Eye.Blinding -{ - /// - /// Blind status effect. - /// - [NetworkedComponent, RegisterComponent] - public sealed class TemporaryBlindnessComponent : Component - {} -} diff --git a/Content.Shared/Eye/Blinding/VisionCorrectionComponent.cs b/Content.Shared/Eye/Blinding/VisionCorrectionComponent.cs deleted file mode 100644 index dbc9750c59..0000000000 --- a/Content.Shared/Eye/Blinding/VisionCorrectionComponent.cs +++ /dev/null @@ -1,15 +0,0 @@ -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; - } -} diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index a46399797c..3a79258971 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -1,6 +1,7 @@ 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; @@ -26,6 +27,11 @@ public partial class InventorySystem SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); + // Eye/vision events + SubscribeLocalEvent(RelayInventoryEvent); + SubscribeLocalEvent(RelayInventoryEvent); + SubscribeLocalEvent(RelayInventoryEvent); + SubscribeLocalEvent>(OnGetStrippingVerbs); } diff --git a/Content.Shared/Traits/Assorted/PermanentBlindnessSystem.cs b/Content.Shared/Traits/Assorted/PermanentBlindnessSystem.cs index 49b82a0966..a9bdb28168 100644 --- a/Content.Shared/Traits/Assorted/PermanentBlindnessSystem.cs +++ b/Content.Shared/Traits/Assorted/PermanentBlindnessSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Examine; using Content.Shared.Eye.Blinding; +using Content.Shared.Eye.Blinding.Systems; using Content.Shared.IdentityManagement; using Robust.Shared.Network; @@ -11,13 +12,14 @@ namespace Content.Shared.Traits.Assorted; public sealed class PermanentBlindnessSystem : EntitySystem { [Dependency] private readonly INetManager _net = default!; - [Dependency] private readonly SharedBlindingSystem _blinding = default!; + [Dependency] private readonly BlindableSystem _blinding = default!; /// public override void Initialize() { SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnTrySee); SubscribeLocalEvent(OnExamined); } @@ -31,11 +33,17 @@ public sealed class PermanentBlindnessSystem : EntitySystem 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(); } } diff --git a/Resources/Prototypes/Shaders/shaders.yml b/Resources/Prototypes/Shaders/shaders.yml index 74d715ee25..462c629222 100644 --- a/Resources/Prototypes/Shaders/shaders.yml +++ b/Resources/Prototypes/Shaders/shaders.yml @@ -66,11 +66,6 @@ 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 diff --git a/Resources/Textures/Shaders/dim.swsl b/Resources/Textures/Shaders/dim.swsl deleted file mode 100644 index 19ae636193..0000000000 --- a/Resources/Textures/Shaders/dim.swsl +++ /dev/null @@ -1,12 +0,0 @@ -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); -} -