From 8408c1128f607aac281323aad5d7926619331a5c Mon Sep 17 00:00:00 2001 From: Alex Evgrashin Date: Sun, 22 Aug 2021 19:32:24 +0300 Subject: [PATCH] Add security barriers (#4458) * Add sprites * Lock system now raises lock toggle events * Add prototype and barrier system * Toggle lock on click * Barrier blocks bullets (like a real wall) * Barrier now destroyable * Fancy visualzer and lighting. Also unlock by default * Deleted comma * Ignored components? * Update Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Update Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Toggle Lock no longer handled * Made it much easier to move through airlocks Co-authored-by: Swept Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- Content.Client/Entry/IgnoredComponents.cs | 1 + .../Security/DeployableBarrierVisualizer.cs | 39 +++++++++++++ Content.Server/Lock/LockComponent.cs | 1 + Content.Server/Lock/LockSystem.cs | 9 +++ Content.Server/Lock/LockToggledEvent.cs | 14 +++++ .../Components/DeployableBarrierComponent.cs | 10 ++++ .../Systems/DeployableBarrierSystem.cs | 43 ++++++++++++++ .../Security/DeployableBarrierVisuals.cs | 23 ++++++++ .../Objects/Specific/Security/barrier.yml | 55 ++++++++++++++++++ .../Security/barrier.rsi/deployed.png | Bin 0 -> 1463 bytes .../Specific/Security/barrier.rsi/idle.png | Bin 0 -> 824 bytes .../Specific/Security/barrier.rsi/meta.json | 26 +++++++++ 12 files changed, 221 insertions(+) create mode 100644 Content.Client/Security/DeployableBarrierVisualizer.cs create mode 100644 Content.Server/Lock/LockToggledEvent.cs create mode 100644 Content.Server/Security/Components/DeployableBarrierComponent.cs create mode 100644 Content.Server/Security/Systems/DeployableBarrierSystem.cs create mode 100644 Content.Shared/Security/DeployableBarrierVisuals.cs create mode 100644 Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml create mode 100644 Resources/Textures/Objects/Specific/Security/barrier.rsi/deployed.png create mode 100644 Resources/Textures/Objects/Specific/Security/barrier.rsi/idle.png create mode 100644 Resources/Textures/Objects/Specific/Security/barrier.rsi/meta.json diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index 860dcc3d8e..6694b7c108 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -57,6 +57,7 @@ namespace Content.Client.Entry "CablePlacer", "Drink", "Food", + "DeployableBarrier", "MagicMirror", "FloorTile", "ShuttleController", diff --git a/Content.Client/Security/DeployableBarrierVisualizer.cs b/Content.Client/Security/DeployableBarrierVisualizer.cs new file mode 100644 index 0000000000..4551440725 --- /dev/null +++ b/Content.Client/Security/DeployableBarrierVisualizer.cs @@ -0,0 +1,39 @@ +using Content.Shared.Security; +using JetBrains.Annotations; +using Robust.Client.GameObjects; + +namespace Content.Client.Security +{ + [UsedImplicitly] + public class DeployableBarrierVisualizer : AppearanceVisualizer + { + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + + if (!component.Owner.TryGetComponent(out SpriteComponent? sprite)) + return; + + if (!component.TryGetData(DeployableBarrierVisuals.State, out DeployableBarrierState state)) + return; + + switch (state) + { + case DeployableBarrierState.Idle: + sprite.LayerSetState(0, "idle"); + ToggleLight(component, false); + break; + case DeployableBarrierState.Deployed: + sprite.LayerSetState(0, "deployed"); + ToggleLight(component, true); + break; + } + } + + private void ToggleLight(AppearanceComponent component, bool enabled) + { + if (component.Owner.TryGetComponent(out PointLightComponent? light)) + light.Enabled = enabled; + } + } +} diff --git a/Content.Server/Lock/LockComponent.cs b/Content.Server/Lock/LockComponent.cs index 8d1d94c7e0..3e9a26716b 100644 --- a/Content.Server/Lock/LockComponent.cs +++ b/Content.Server/Lock/LockComponent.cs @@ -20,6 +20,7 @@ namespace Content.Server.Storage.Components public override string Name => "Lock"; [ViewVariables(VVAccess.ReadWrite)] [DataField("locked")] public bool Locked { get; set; } = true; + [ViewVariables(VVAccess.ReadWrite)] [DataField("lockOnClick")] public bool LockOnClick { get; set; } = false; [ViewVariables(VVAccess.ReadWrite)] [DataField("unlockingSound")] public SoundSpecifier UnlockSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/door_lock_off.ogg"); [ViewVariables(VVAccess.ReadWrite)] [DataField("lockingSound")] public SoundSpecifier LockSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/door_lock_off.ogg"); diff --git a/Content.Server/Lock/LockSystem.cs b/Content.Server/Lock/LockSystem.cs index 063ac1656a..d8b6c65190 100644 --- a/Content.Server/Lock/LockSystem.cs +++ b/Content.Server/Lock/LockSystem.cs @@ -43,6 +43,11 @@ namespace Content.Server.Lock { DoUnlock(lockComp, args); } + else + { + if (lockComp.LockOnClick) + DoLock(lockComp, args); + } } private void OnExamined(EntityUid eUI, LockComponent lockComp, ExaminedEvent args) @@ -73,6 +78,8 @@ namespace Content.Server.Lock appearanceComp.SetData(StorageVisuals.Locked, true); } + RaiseLocalEvent(lockComp.Owner.Uid, new LockToggledEvent(true)); + args.Handled = true; } @@ -95,6 +102,8 @@ namespace Content.Server.Lock appearanceComp.SetData(StorageVisuals.Locked, false); } + RaiseLocalEvent(lockComp.Owner.Uid, new LockToggledEvent(false)); + // To stop EntityStorageComponent from opening right after the container gets unlocked args.Handled = true; } diff --git a/Content.Server/Lock/LockToggledEvent.cs b/Content.Server/Lock/LockToggledEvent.cs new file mode 100644 index 0000000000..04ae77010b --- /dev/null +++ b/Content.Server/Lock/LockToggledEvent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.GameObjects; + +namespace Content.Server.Lock +{ + public class LockToggledEvent : EntityEventArgs + { + public readonly bool Locked; + + public LockToggledEvent(bool locked) + { + Locked = locked; + } + } +} diff --git a/Content.Server/Security/Components/DeployableBarrierComponent.cs b/Content.Server/Security/Components/DeployableBarrierComponent.cs new file mode 100644 index 0000000000..f71d51a093 --- /dev/null +++ b/Content.Server/Security/Components/DeployableBarrierComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameObjects; + +namespace Content.Server.Security +{ + [RegisterComponent] + public class DeployableBarrierComponent : Component + { + public override string Name => "DeployableBarrier"; + } +} diff --git a/Content.Server/Security/Systems/DeployableBarrierSystem.cs b/Content.Server/Security/Systems/DeployableBarrierSystem.cs new file mode 100644 index 0000000000..d819fcb651 --- /dev/null +++ b/Content.Server/Security/Systems/DeployableBarrierSystem.cs @@ -0,0 +1,43 @@ +using Content.Server.Lock; +using Content.Server.Storage.Components; +using Content.Shared.Security; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using System; + +namespace Content.Server.Security.Systems +{ + public class DeployableBarrierSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnLockToggled); + } + + private void OnStartup(EntityUid uid, DeployableBarrierComponent component, ComponentStartup args) + { + if (!component.Owner.TryGetComponent(out LockComponent? lockComponent)) + return; + + ToggleBarrierDeploy(component, lockComponent.Locked); + } + + private void OnLockToggled(EntityUid uid, DeployableBarrierComponent component, LockToggledEvent args) + { + ToggleBarrierDeploy(component, args.Locked); + } + + private void ToggleBarrierDeploy(DeployableBarrierComponent component, bool isDeployed) + { + component.Owner.Transform.Anchored = isDeployed; + + if (!component.Owner.TryGetComponent(out AppearanceComponent? appearanceComponent)) + return; + + var state = isDeployed ? DeployableBarrierState.Deployed : DeployableBarrierState.Idle; + appearanceComponent.SetData(DeployableBarrierVisuals.State, state); + } + } +} diff --git a/Content.Shared/Security/DeployableBarrierVisuals.cs b/Content.Shared/Security/DeployableBarrierVisuals.cs new file mode 100644 index 0000000000..a25a925d35 --- /dev/null +++ b/Content.Shared/Security/DeployableBarrierVisuals.cs @@ -0,0 +1,23 @@ +using Robust.Shared.Serialization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Content.Shared.Security +{ + [Serializable, NetSerializable] + public enum DeployableBarrierVisuals : byte + { + State + } + + + [Serializable, NetSerializable] + public enum DeployableBarrierState : byte + { + Idle, + Deployed + } +} diff --git a/Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml b/Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml new file mode 100644 index 0000000000..6447a30d2f --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml @@ -0,0 +1,55 @@ +- type: entity + name: deployable barrier + id: DeployableBarrier + description: A deployable barrier. Swipe your ID card to lock/unlock it. + parent: BaseStructure + components: + - type: Sprite + sprite: Objects/Specific/Security/barrier.rsi + netsync: false + state: idle + - type: InteractionOutline + - type: Physics + fixtures: + - shape: + !type:PhysShapeCircle + radius: 0.45 + layer: + - Opaque + - Impassable + - VaultImpassable + - SmallImpassable + mask: + - Impassable + mass: 150 + - type: AccessReader + access: [["Security"]] + - type: Lock + locked: false + lockOnClick: true # toggle lock just by clicking on barrier + - type: DeployableBarrier + - type: Damageable + resistances: metallicResistances + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel: + min: 5 + max: 5 + - !type:PlaySoundBehavior + sound: + path: /Audio/Effects/metalbreak.ogg + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: PointLight + enabled: false + radius: 3 + color: red + - type: Appearance + visuals: + - type: DeployableBarrierVisualizer diff --git a/Resources/Textures/Objects/Specific/Security/barrier.rsi/deployed.png b/Resources/Textures/Objects/Specific/Security/barrier.rsi/deployed.png new file mode 100644 index 0000000000000000000000000000000000000000..a3c0f1f3afd9934802ea75ae05bb69d2f491bc71 GIT binary patch literal 1463 zcmV;o1xWgdP)6O&q_kEhyHvygZ zN3exIa{?HGAV8@^2G@(~|su z1Oa*l`TK<6v4($5HvsnI7eN_ahEWL{Wpo*OC2Y*1oB0GJkBszKiTE}8Br7!UA`HVD zeJ7JiAs=iDKffRbkQ4NdY*+-X{J2emv3Lqdj;YFnIcUruGnW^S6f8Kk51Hrd38H?n}UnfHb;MzYb1Dq9B z@UH>aOQJ9gRsZY&*FKH)Rik{GOwi#n(Y~s0EB_h*JZo;z{`3kj=sI4|b+pHyf#mT@ zW|4ytiEG+482|`(4{eV>bNl`26~ttg&HTDl1O?*g&f09|gf(%ROw!SCSk14^1t~so zE~s};5v)4DZUR&vICBt6^MM6m55MpGRQ!l?dU}VdK2OzGuhY{zG=783w{JWDnyBn~ zp0rlQO@B6X5K8lbqW!dg$VUD=7eoO#iVvJQ2m#y&O0qd%1-~{G0mS~S`EUcS>vCDj zU?mKmzQ)eM7Qa?B6|n03{0U&M%AV(m`9wQYc}AeBtMuFZ4>TaFoVBOh?o(r zgkMcZr*24OY@@LymYCa6lTm4XpyRNa-w`{o?>21-n&!`aNf2jEt#wKAB^<-%UH)C{ zTXa+07JhAEmRjoq#96(cUHLWpQA%BmfWn1QtIn^@1rb8#8TYN}GHjQyQAU@cU&6*L zy4uP(-}e!Q;f=NPKbQZ?9XGzXQ5RFTDh@!rfnJb5Yqc{E9+%+PRmA~w6lRHrqbPFz zR*F-D(Ff^OTpaLi6h_&`U!#T{&@MyMz^n$`D)^T%FiR8AT|j+& zH>W;d37VDgFI!;dE&zZUxB$cnwX@@Mp$2|7j8CR``^rJPe}RNUvJYJQVblC3QJ)Vm zjQV_lVbtdX45L0DU>NoJ0K=%y2N*_uK44j3Ykj_H)aL^Xqdp&C81?x8!&sX00sB#e z!yv$65LCv`?C%SiR@0EOp#wt1jHm2tv zhRnxwj)kM%k~ECmL73hYH_g8YEcN+-{V2k2;x>Ns{kixLg8)G6Zc_G`mj>Eg(4a(p zd>LJa14Futg0?blr$l{xnW{J-zI)kxaos)sfPq{-{z+!`x`{{g2~L$Y^4 R?JEEP002ovPDHLkV1kR?yBz=k literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Security/barrier.rsi/idle.png b/Resources/Textures/Objects/Specific/Security/barrier.rsi/idle.png new file mode 100644 index 0000000000000000000000000000000000000000..e4853d5dc7efa3fbcfabb9237eceda233ce891fd GIT binary patch literal 824 zcmV-81IPS{P)`3j#9ag- z5U#M%83e8(^H@gcwGO5s_)|!0svMkj>cg1SC+KHZwADT&4en8xEVHa0c@0BM>60E(i(^QX@o z%|B+Hb@Mz&mSsWztylsa{2{c6ZDR%Ml38Vy^s&6M4Z15RiegT8I-LeFU=}2RJO&b6 z$|Gx%TYh{o+Pn|}jnZ0^sEVq8>`(@mx>j&s466kaaAjE<`Dm|0z%76d?VhXx03B{u zE%5LJ0H5C8Qh#`3l1P%o3rs5k2Q&mgjrKb9daHWT&A~d-6gb^KUx*)}!%2{g!C0{p zaDl@~?4GRR`_?78J4*mA0HA(Gke?jSlnQHVtly%If>95P!PRk86&y=R}C@%_xM0y$U*kpu0xH*&U9kjU#HKKe^`2`7I1_Ao)%EWB?$lNBzmQ@jsm1fj6_*C?=pnH}jmi zm(2B8lF(m(5lT0j7lgMo!X2p9}lGMA#G7^wQ$-C2Ux+Ik}=S&L5h&++2j znptZa#ijy0D$_KT=Q#tQ$?*)mUJt!q50m3rln2Zgqyql!-7wE{xg#n7=nrqy$ZW#} z4!5uH^|L~ZXItTmIELJg72rPsH=K&(fy9*8;awesVm$hhEP65FtlhJIy