diff --git a/Content.Client/_Sunrise/Eye/DarkenedVisionSystem.cs b/Content.Client/_Sunrise/Eye/DarkenedVisionSystem.cs new file mode 100644 index 0000000000..34999c6b01 --- /dev/null +++ b/Content.Client/_Sunrise/Eye/DarkenedVisionSystem.cs @@ -0,0 +1,63 @@ +using Robust.Client.Graphics; +using Robust.Shared.Player; +using Robust.Client.Player; +using Content.Client.Overlays; +using Content.Shared.Eye; +using Content.Client.Eye; +using Content.Client.Sunrise.Overlays; +using Content.Client.Sunrise.Eye; +using Content.Shared.Sunrise.Eye; + +namespace Content.Client.Sunrise.Eye; + +public sealed class DarkenedVisionSystem : SharedDarkenedVisionSystem +{ + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IOverlayManager _overlayMan = default!; + + + private DarkenedVisionOverlay _overlay = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnShutdown); + + SubscribeLocalEvent(OnAttached); + SubscribeLocalEvent(OnDetached); + + _overlay = new(); + } + + private void OnAttached(Entity ent, ref LocalPlayerAttachedEvent args) + { + _overlayMan.AddOverlay(_overlay); + _overlay.DarkenedVision = ent.Comp; + } + + private void OnDetached(Entity ent, ref LocalPlayerDetachedEvent args) + { + _overlayMan.RemoveOverlay(_overlay); + _overlay.DarkenedVision = null; + } + + private void OnStartup(Entity ent, ref ComponentStartup args) + { + if (_player.LocalEntity == ent.Owner) + { + _overlayMan.AddOverlay(_overlay); + _overlay.DarkenedVision = ent.Comp; + } + } + + private void OnShutdown(Entity ent, ref ComponentShutdown args) + { + if (_player.LocalEntity == ent.Owner) + { + _overlayMan.RemoveOverlay(_overlay); + _overlay.DarkenedVision = null; + } + } +} \ No newline at end of file diff --git a/Content.Client/_Sunrise/Overlays/DarkenedVisionOverlay.cs b/Content.Client/_Sunrise/Overlays/DarkenedVisionOverlay.cs new file mode 100644 index 0000000000..bb243123cc --- /dev/null +++ b/Content.Client/_Sunrise/Overlays/DarkenedVisionOverlay.cs @@ -0,0 +1,75 @@ +using Content.Client.Movement.Systems; +using Robust.Client.Graphics; +using Robust.Client.Player; +using Robust.Shared.Enums; +using Robust.Shared.Prototypes; +using Content.Shared.Eye.Blinding; +using Content.Shared.Eye.Blinding.Components; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using Content.Shared.Eye; +using Content.Client.Overlays; +using Content.Shared.Sunrise.Eye; +using Content.Client.Sunrise.Overlays; + +namespace Content.Client.Sunrise.Overlays; + +public sealed class DarkenedVisionOverlay : Overlay +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IEntityManager _entityManager = default!; + + public override bool RequestScreenTexture => true; + public override OverlaySpace Space => OverlaySpace.WorldSpace; + private readonly ShaderInstance _circleMaskShader; + + public DarkenedVisionComponent? DarkenedVision; + + + public DarkenedVisionOverlay() + { + IoCManager.InjectDependencies(this); + _circleMaskShader = _prototypeManager.Index("CircleMask").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 DarkenedVision != null && DarkenedVision.Strength < DarkenedVision.BlindTreshold && DarkenedVision.Strength > 0; + } + + protected override void Draw(in OverlayDrawArgs args) + { + if (ScreenTexture == null) + return; + + var playerEntity = _playerManager.LocalSession?.AttachedEntity; + + if (playerEntity == null) + return; + + if (_entityManager.TryGetComponent(playerEntity, out var content)) + { + _circleMaskShader?.SetParameter("Zoom", content.Zoom.X); + } + + _circleMaskShader?.SetParameter("CirclePow", 1f); + _circleMaskShader?.SetParameter("CircleRadius", (10 - DarkenedVision!.Strength) * 32f); + + var worldHandle = args.WorldHandle; + var viewport = args.WorldBounds; + worldHandle.UseShader(_circleMaskShader); + worldHandle.DrawRect(viewport, Color.White); + worldHandle.UseShader(null); + } +} \ No newline at end of file diff --git a/Content.Server/_Sunrise/Eye/DarkenedVisionSystem.cs b/Content.Server/_Sunrise/Eye/DarkenedVisionSystem.cs new file mode 100644 index 0000000000..f980435548 --- /dev/null +++ b/Content.Server/_Sunrise/Eye/DarkenedVisionSystem.cs @@ -0,0 +1,15 @@ +using Content.Shared.Clothing; +using Content.Shared.Clothing.Components; +using Content.Shared.Eye.Blinding.Components; +using Content.Shared.Eye.Blinding.Systems; +using Content.Shared.Inventory; +using Content.Shared.Tag; + +using Content.Shared.Eye; +using Content.Shared.Sunrise.Eye; +using Content.Shared.Sunrise.Clothing.EntitySystems; +using Content.Shared.Sunrise.Clothing.Components; + +namespace Content.Server.Sunrise.Eye; + +public sealed class DarkenedVisionSystem : SharedDarkenedVisionSystem {} \ No newline at end of file diff --git a/Content.Shared/_Sunrise/Clothing/Components/WeldingMaskComponent.cs b/Content.Shared/_Sunrise/Clothing/Components/WeldingMaskComponent.cs new file mode 100644 index 0000000000..3e8b1f0df4 --- /dev/null +++ b/Content.Shared/_Sunrise/Clothing/Components/WeldingMaskComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameStates; +using Content.Shared.Clothing.EntitySystems; +using Content.Shared.Clothing.Components; +using Content.Shared.Sunrise.Clothing.EntitySystems; +using Content.Shared.Sunrise.Clothing.Components; +using Content.Shared.Clothing; + +namespace Content.Shared.Sunrise.Clothing.Components; + +[RegisterComponent, NetworkedComponent, Access(typeof(WeldingMaskSystem))] +public sealed partial class WeldingMaskComponent : Component +{ +} \ No newline at end of file diff --git a/Content.Shared/_Sunrise/Clothing/EntitySystems/WeldingMaskSystem.cs b/Content.Shared/_Sunrise/Clothing/EntitySystems/WeldingMaskSystem.cs new file mode 100644 index 0000000000..20fe86bde9 --- /dev/null +++ b/Content.Shared/_Sunrise/Clothing/EntitySystems/WeldingMaskSystem.cs @@ -0,0 +1,36 @@ +using Content.Shared.Clothing.Components; +using Content.Shared.Eye; +using Content.Shared.Item; +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Clothing.EntitySystems; +using Content.Shared.Sunrise.Eye; +using Content.Shared.Sunrise.Clothing.EntitySystems; +using Content.Shared.Sunrise.Clothing.Components; +using Content.Shared.Clothing; + +namespace Content.Shared.Sunrise.Clothing.EntitySystems; + +public sealed class WeldingMaskSystem : EntitySystem +{ + [Dependency] private readonly ClothingSystem _clothing = default!; + [Dependency] private readonly SharedItemSystem _item = default!; + [Dependency] private readonly SharedDarkenedVisionSystem _darkenedVision = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnToggle); + } + + private void OnToggle(Entity ent, ref ItemToggledEvent args) + { + var prefix = args.Activated ? null : "up"; + _clothing.SetEquippedPrefix(ent, prefix); + _item.SetHeldPrefix(ent, prefix); + + if (args.User is not { } user) return; + + _darkenedVision.UpdateVisionDarkening(user); + } +} \ No newline at end of file diff --git a/Content.Shared/_Sunrise/Eye/DarkenedVisionComponent.cs b/Content.Shared/_Sunrise/Eye/DarkenedVisionComponent.cs new file mode 100644 index 0000000000..9d49ebfd83 --- /dev/null +++ b/Content.Shared/_Sunrise/Eye/DarkenedVisionComponent.cs @@ -0,0 +1,27 @@ +using Robust.Shared.GameStates; +using Content.Shared.Sunrise.Eye; +using Content.Shared.Sunrise.Clothing.EntitySystems; +using Content.Shared.Sunrise.Clothing.Components; +using Content.Shared.Clothing; + +namespace Content.Shared.Sunrise.Eye; + +/// +/// Adds overlay for client that darkness vision +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedDarkenedVisionSystem))] +public sealed partial class DarkenedVisionComponent : Component +{ + /// + /// How much is vision darkened (in tiles from screen edge) + /// + [DataField, AutoNetworkedField] + public float Strength = 0; + + /// + /// If strength is higher than this value users goes blind + /// + [DataField] + public float BlindTreshold = 8; +} \ No newline at end of file diff --git a/Content.Shared/_Sunrise/Eye/SharedDarkenedVisionSystem.cs b/Content.Shared/_Sunrise/Eye/SharedDarkenedVisionSystem.cs new file mode 100644 index 0000000000..0b85ffb34c --- /dev/null +++ b/Content.Shared/_Sunrise/Eye/SharedDarkenedVisionSystem.cs @@ -0,0 +1,54 @@ +using Content.Shared.Clothing; +using Content.Shared.Clothing.Components; +using Content.Shared.Eye.Blinding.Components; +using Content.Shared.Eye.Blinding.Systems; +using Content.Shared.Inventory; +using Content.Shared.Tag; +using Content.Shared.Eye; +using Content.Shared.Sunrise.Eye; +using Content.Shared.Sunrise.Clothing.Components; + +namespace Content.Shared.Sunrise.Eye; + +public abstract class SharedDarkenedVisionSystem : EntitySystem +{ + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly BlindableSystem _blinding = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnTrySee); + } + + public void UpdateVisionDarkening(Entity ent) + { + if (!Resolve(ent, ref ent.Comp, false)) + return; + + var ev = new GetVisionDarkeningEvent(); + RaiseLocalEvent(ent, ev); + if (TryComp(ent, out var inventory)) + _inventory.RelayEvent((ent.Owner, inventory), ref ev); + + ent.Comp.Strength = ev.Strength; + + Dirty(ent); + _blinding.UpdateIsBlind(ent.Owner); + } + + private void OnTrySee(Entity ent, ref CanSeeAttemptEvent args) + { + if (ent.Comp.Strength >= ent.Comp.BlindTreshold) args.Cancel(); + } +} + +/// +/// Event to get total vision darkening +/// +public sealed class GetVisionDarkeningEvent : EntityEventArgs, IInventoryRelayEvent +{ + public SlotFlags TargetSlots => SlotFlags.EYES | SlotFlags.MASK | SlotFlags.HEAD; + public float Strength = 0f; +} \ No newline at end of file diff --git a/Content.Shared/_Sunrise/Eye/VisionDarkenerComponent.cs b/Content.Shared/_Sunrise/Eye/VisionDarkenerComponent.cs new file mode 100644 index 0000000000..02c9ca8a21 --- /dev/null +++ b/Content.Shared/_Sunrise/Eye/VisionDarkenerComponent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Sunrise.Eye; + +/// +/// Piece of gear that darkens the user vision +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(VisionDarkenerSystem))] +public sealed partial class VisionDarkenerComponent : Component +{ + [DataField, AutoNetworkedField] + public float Strength = 0; +} \ No newline at end of file diff --git a/Content.Shared/_Sunrise/Eye/VisionDarkenerSystem.cs b/Content.Shared/_Sunrise/Eye/VisionDarkenerSystem.cs new file mode 100644 index 0000000000..8b9f51ca44 --- /dev/null +++ b/Content.Shared/_Sunrise/Eye/VisionDarkenerSystem.cs @@ -0,0 +1,46 @@ +using Content.Shared.Clothing; +using Content.Shared.Clothing.Components; +using Content.Shared.Eye.Blinding.Components; +using Content.Shared.Eye.Blinding.Systems; +using Content.Shared.Inventory; +using Content.Shared.Tag; +using Content.Shared.Eye; +using Content.Shared.Sunrise.Eye; +using Content.Shared.Sunrise.Clothing.Components; + +namespace Content.Shared.Sunrise.Eye; + +public sealed class VisionDarkenerSystem : EntitySystem +{ + [Dependency] private readonly SharedDarkenedVisionSystem _darkenedVision = default!; + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGetVisionDarkening); + SubscribeLocalEvent>(OnGetVisionDarkening); + + SubscribeLocalEvent(OnGotEquipped); + SubscribeLocalEvent(OnGotUnquipped); + } + + private void OnGetVisionDarkening(Entity ent, ref GetVisionDarkeningEvent args) + { + args.Strength += ent.Comp.Strength; + } + + private void OnGetVisionDarkening(Entity ent, ref InventoryRelayedEvent args) + { + args.Args.Strength += ent.Comp.Strength; + } + + private void OnGotEquipped(Entity ent, ref ClothingGotEquippedEvent args) + { + _darkenedVision.UpdateVisionDarkening(args.Wearer); + } + + private void OnGotUnquipped(Entity ent, ref ClothingGotUnequippedEvent args) + { + _darkenedVision.UpdateVisionDarkening(args.Wearer); + } +} \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Clothing/Head/welding.yml b/Resources/Prototypes/Entities/Clothing/Head/welding.yml index c0ae440a56..0c04852a05 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/welding.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/welding.yml @@ -1,6 +1,6 @@ - type: entity parent: ClothingHeadBase - id: WeldingMaskBase + id: WeldingMaskBase # Left for the guys made by the greatest prototypers of Sunrise. name: welding mask abstract: true components: @@ -25,15 +25,56 @@ - Snout - type: entity - parent: WeldingMaskBase - id: ClothingHeadHatWelding + parent: [ClothingHeadBase, BaseToggleClothing] # Sunrise-Edit + id: ClothingHeadHatWelding # Sunrise-Edit name: welding mask + # Sunrise-Start + # abstract: true description: A head-mounted face cover designed to protect the wearer completely from space-arc eye. components: - type: Sprite sprite: Clothing/Head/Welding/welding.rsi + layers: + - state: icon + map: [ "enum.ToggleVisuals.Layer" ] - type: Clothing sprite: Clothing/Head/Welding/welding.rsi + # Sunrise End + - type: Item + storedRotation: 0 + - type: ItemToggle # Welding mask starts in an activated state + activated: true + - type: ToggleClothing + action: ActionToggleWeldingMask + - type: HideLayerClothing + slots: + - Snout + hideOnToggle: true + - type: Appearance + - type: WeldingMask + - type: GenericVisualizer + visuals: + enum.ToggleVisuals.Toggled: + enum.ToggleVisuals.Layer: + True: {state: icon} + False: {state: icon-up} + # Sunrise-End + - type: PhysicalComposition + materialComposition: + Steel: 200 + Glass: 100 + - type: StaticPrice + price: 50 + # Sunrise-Start + - type: ComponentToggler + components: + - type: IngestionBlocker + - type: FlashImmunity + - type: IdentityBlocker + - type: EyeProtection + - type: VisionDarkener + strength: 6 + # Sunrise-End - type: Tag tags: - HamsterWearable @@ -41,34 +82,79 @@ - WeldingMask - type: entity - parent: WeldingMaskBase + parent: ClothingHeadHatWelding # Sunrise-Edit id: ClothingHeadHatWeldingMaskFlame name: flame welding mask description: A painted welding helmet, this one has flames on it. components: - type: Sprite sprite: Clothing/Head/Welding/flame_welding_mask.rsi + # Sunrise-Start + layers: + - state: icon + map: [ "enum.ToggleVisuals.Layer" ] + # Sunrise-End - type: Clothing sprite: Clothing/Head/Welding/flame_welding_mask.rsi + # Sunrise-Start + - type: Tag + tags: + - WhitelistChameleon + - WeldingMask + # Sunrise-End - type: entity - parent: WeldingMaskBase + parent: ClothingHeadHatWelding # Sunrise-Edit id: ClothingHeadHatWeldingMaskFlameBlue name: blue-flame welding mask description: A painted welding helmet, this one has blue flames on it. components: - type: Sprite sprite: Clothing/Head/Welding/blue_flame_welding_mask.rsi + # Sunrise-Start + layers: + - state: icon + map: [ "enum.ToggleVisuals.Layer" ] + # Sunrise-End - type: Clothing sprite: Clothing/Head/Welding/blue_flame_welding_mask.rsi + # Sunrise-Start + - type: Tag + tags: + - WhitelistChameleon + - WeldingMask + # Sunrise-End - type: entity - parent: WeldingMaskBase + parent: ClothingHeadHatWelding # Sunrise-Edit id: ClothingHeadHatWeldingMaskPainted name: painted welding mask description: A welding helmet, painted in crimson. components: - type: Sprite sprite: Clothing/Head/Welding/paintedwelding.rsi + # Sunrise-Start + layers: + - state: icon + map: [ "enum.ToggleVisuals.Layer" ] + # Sunrise-End - type: Clothing sprite: Clothing/Head/Welding/paintedwelding.rsi + # Sunrise-Start + - type: Tag + tags: + - WhitelistChameleon + - WeldingMask + # Sunrise-End + + # Sunrise-Start +- type: entity + id: ActionToggleWeldingMask + name: Adjust Welding Mask + description: Covers or reveals your face. + components: + - type: InstantAction + useDelay: 1 + itemIconStyle: BigItem + event: !type:ToggleActionEvent + # Sunrise-End \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index 63b4476297..93083c57ad 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -267,6 +267,7 @@ abstract: true components: # Sunrise-Start + - type: DarkenedVision - type: CanEscapeInventory - type: Mood - type: CritHeartbeat diff --git a/Resources/Prototypes/_Sunrise/Entities/Clothing/Eyes/glasses.yml b/Resources/Prototypes/_Sunrise/Entities/Clothing/Eyes/glasses.yml index 671b321c48..e7725132ac 100644 --- a/Resources/Prototypes/_Sunrise/Entities/Clothing/Eyes/glasses.yml +++ b/Resources/Prototypes/_Sunrise/Entities/Clothing/Eyes/glasses.yml @@ -56,12 +56,14 @@ sprite: _Sunrise/Clothing/Eyes/Glasses/weldglasses.rsi - type: Clothing sprite: _Sunrise/Clothing/Eyes/Glasses/weldglasses.rsi - #- type: FlashImmunity + - type: IdentityBlocker + coverage: EYES + - type: FlashImmunity - type: EyeProtection - - type: Tag - tags: - - HamsterWearable - - WhitelistChameleon + - type: VisionDarkener + strength: 3 + - type: StaticPrice + price: 8 - type: entity parent: ClothingEyesBase