From 636819f4e306481bf8a7f6b531987cfe15545ba4 Mon Sep 17 00:00:00 2001 From: chromiumboy <50505512+chromiumboy@users.noreply.github.com> Date: Thu, 10 Aug 2023 03:33:03 -0500 Subject: [PATCH 1/6] Hacking protections for airlocks (#18894) * Adds the ability to better protect to the internal wiring of airlocks - Achieved by opening the maintenance panel, adding either steel or plasteel to the airlock, then welding the plate in place - To access the wiring, the plating must be cut with a welder and then pried out with a crowbar * Code revisions - Cleaned up the code - Cutting the security grille can now shock you - Atmospherics and Security dept airlocks start with a medium level of protection (a welded steel plate) - Command dept airlocks start with a high level of protection (a welded plasteel plate and electrified security grille) * Code revision - Accounted for a potentially null string * Update Content.Server/Construction/Completions/AttemptElectrocute.cs Co-authored-by: Slava0135 <40753025+Slava0135@users.noreply.github.com> * Update ChangeWiresPanelSecurityLevel.cs Adjusted scope * Update Content.Shared/Wires/SharedWiresSystem.cs Co-authored-by: Slava0135 <40753025+Slava0135@users.noreply.github.com> * Update Content.Shared/Wires/SharedWiresSystem.cs Co-authored-by: Slava0135 <40753025+Slava0135@users.noreply.github.com> * Update ChangeWiresPanelSecurityLevel.cs Removed get / setter and added [ValidatePrototypeId] attribute * Update ChangeWiresPanelSecurityLevel.cs Set security level to "Level0" as the default * Update airlock.yml Removed 'super max' level of security * Update WiresPanelSecurityLevelPrototype.cs * Update WiresSystem.cs Added check for WiresAccessible to OnInteractUsing * Update AttemptElectrocute.cs File scoped namespace * Update ChangeWiresPanelSecurityLevel.cs File scoped namespace * Update AirlockSystem.cs File scoped namespace * Update SharedWiresSystem.cs Removed boiler plate 'OnGetState' and 'OnHandleState' * Update WiresPanelComponent.cs Implemented AutoGenerateComponentState * Removed unnecessary usage references * use TryCloseAll when wires not accessible * minor changes to AttemmptElectrocute * lets try all 7 levels * fix indent in airlock graph * fix indent 2 --------- Co-authored-by: Slava0135 <40753025+Slava0135@users.noreply.github.com> Co-authored-by: Slava0135 --- .../Completions/AttemptElectrocute.cs | 25 +- .../ChangeWiresPanelSecurityLevel.cs | 27 ++ Content.Server/Doors/Systems/AirlockSystem.cs | 263 +++++++++--------- .../Electrocution/ElectrocutionSystem.cs | 1 - .../Tools/Systems/WeldableSystem.cs | 14 + Content.Server/Wires/WiresSystem.cs | 20 +- Content.Shared/Wires/SharedWiresSystem.cs | 34 +-- Content.Shared/Wires/WiresPanelComponent.cs | 17 +- .../Wires/WiresPanelSecurityLevelPrototype.cs | 16 ++ .../wires/wires_panel-security-levels.ftl | 8 + .../Structures/Doors/Airlocks/airlocks.yml | 12 + .../Structures/Doors/wires_panel_security.yml | 39 +++ .../Graphs/structures/airlock.yml | 232 +++++++++++++-- 13 files changed, 517 insertions(+), 191 deletions(-) create mode 100644 Content.Server/Construction/Completions/ChangeWiresPanelSecurityLevel.cs create mode 100644 Content.Shared/Wires/WiresPanelSecurityLevelPrototype.cs create mode 100644 Resources/Locale/en-US/wires/wires_panel-security-levels.ftl create mode 100644 Resources/Prototypes/Entities/Structures/Doors/wires_panel_security.yml diff --git a/Content.Server/Construction/Completions/AttemptElectrocute.cs b/Content.Server/Construction/Completions/AttemptElectrocute.cs index cabb792552fe59..eb6c1007648a28 100644 --- a/Content.Server/Construction/Completions/AttemptElectrocute.cs +++ b/Content.Server/Construction/Completions/AttemptElectrocute.cs @@ -1,17 +1,24 @@ using Content.Server.Electrocution; using Content.Shared.Construction; -namespace Content.Server.Construction.Completions +namespace Content.Server.Construction.Completions; + +[DataDefinition] +public sealed class AttemptElectrocute : IGraphAction { - [DataDefinition] - public sealed class AttemptElectrocute : IGraphAction + public void PerformAction(EntityUid uid, EntityUid? userUid, IEntityManager entityManager) { - public void PerformAction(EntityUid uid, EntityUid? userUid, IEntityManager entityManager) - { - if (userUid == null) - return; + if (userUid == null) + return; + + if (!entityManager.TryGetComponent(uid, out var electrified)) + return; + + var currentValue = electrified.Enabled; + electrified.Enabled = true; + + entityManager.EntitySysManager.GetEntitySystem().TryDoElectrifiedAct(uid, userUid.Value, electrified: electrified); - entityManager.EntitySysManager.GetEntitySystem().TryDoElectrifiedAct(uid, userUid.Value); - } + electrified.Enabled = currentValue; } } diff --git a/Content.Server/Construction/Completions/ChangeWiresPanelSecurityLevel.cs b/Content.Server/Construction/Completions/ChangeWiresPanelSecurityLevel.cs new file mode 100644 index 00000000000000..28d7c833d3f1b9 --- /dev/null +++ b/Content.Server/Construction/Completions/ChangeWiresPanelSecurityLevel.cs @@ -0,0 +1,27 @@ +using Content.Server.Wires; +using Content.Shared.Construction; +using Content.Shared.Wires; +using JetBrains.Annotations; + +namespace Content.Server.Construction.Completions; + +[UsedImplicitly] +[DataDefinition] +public sealed class ChangeWiresPanelSecurityLevel : IGraphAction +{ + [DataField("level")] + [ValidatePrototypeId] + public string WiresPanelSecurityLevelID = "Level0"; + + public void PerformAction(EntityUid uid, EntityUid? userUid, IEntityManager entityManager) + { + if (WiresPanelSecurityLevelID == null) + return; + + if (entityManager.TryGetComponent(uid, out WiresPanelComponent? wiresPanel) + && entityManager.TrySystem(out WiresSystem? wiresSystem)) + { + wiresSystem.SetWiresPanelSecurityData(uid, wiresPanel, WiresPanelSecurityLevelID); + } + } +} diff --git a/Content.Server/Doors/Systems/AirlockSystem.cs b/Content.Server/Doors/Systems/AirlockSystem.cs index 21e4b71236bb8c..f07dd23f4b1e0d 100644 --- a/Content.Server/Doors/Systems/AirlockSystem.cs +++ b/Content.Server/Doors/Systems/AirlockSystem.cs @@ -10,180 +10,179 @@ using Robust.Server.GameObjects; using Content.Shared.Wires; -namespace Content.Server.Doors.Systems +namespace Content.Server.Doors.Systems; + +public sealed class AirlockSystem : SharedAirlockSystem { - public sealed class AirlockSystem : SharedAirlockSystem - { - [Dependency] private readonly WiresSystem _wiresSystem = default!; - [Dependency] private readonly PowerReceiverSystem _power = default!; - [Dependency] private readonly DoorBoltSystem _bolts = default!; + [Dependency] private readonly WiresSystem _wiresSystem = default!; + [Dependency] private readonly PowerReceiverSystem _power = default!; + [Dependency] private readonly DoorBoltSystem _bolts = default!; - public override void Initialize() - { - base.Initialize(); + public override void Initialize() + { + base.Initialize(); - SubscribeLocalEvent(OnAirlockInit); - SubscribeLocalEvent(OnSignalReceived); + SubscribeLocalEvent(OnAirlockInit); + SubscribeLocalEvent(OnSignalReceived); - SubscribeLocalEvent(OnPowerChanged); - SubscribeLocalEvent(OnStateChanged); - SubscribeLocalEvent(OnBeforeDoorOpened); - SubscribeLocalEvent(OnBeforeDoorDenied); - SubscribeLocalEvent(OnActivate, before: new [] {typeof(DoorSystem)}); - SubscribeLocalEvent(OnGetPryMod); - SubscribeLocalEvent(OnDoorPry); + SubscribeLocalEvent(OnPowerChanged); + SubscribeLocalEvent(OnStateChanged); + SubscribeLocalEvent(OnBeforeDoorOpened); + SubscribeLocalEvent(OnBeforeDoorDenied); + SubscribeLocalEvent(OnActivate, before: new [] {typeof(DoorSystem)}); + SubscribeLocalEvent(OnGetPryMod); + SubscribeLocalEvent(OnDoorPry); - } + } - private void OnAirlockInit(EntityUid uid, AirlockComponent component, ComponentInit args) + private void OnAirlockInit(EntityUid uid, AirlockComponent component, ComponentInit args) + { + if (TryComp(uid, out var receiverComponent)) { - if (TryComp(uid, out var receiverComponent)) - { - Appearance.SetData(uid, DoorVisuals.Powered, receiverComponent.Powered); - } + Appearance.SetData(uid, DoorVisuals.Powered, receiverComponent.Powered); } + } - private void OnSignalReceived(EntityUid uid, AirlockComponent component, ref SignalReceivedEvent args) + private void OnSignalReceived(EntityUid uid, AirlockComponent component, ref SignalReceivedEvent args) + { + if (args.Port == component.AutoClosePort) { - if (args.Port == component.AutoClosePort) - { - component.AutoClose = false; - } + component.AutoClose = false; } + } - private void OnPowerChanged(EntityUid uid, AirlockComponent component, ref PowerChangedEvent args) + private void OnPowerChanged(EntityUid uid, AirlockComponent component, ref PowerChangedEvent args) + { + if (TryComp(uid, out var appearanceComponent)) { - if (TryComp(uid, out var appearanceComponent)) - { - Appearance.SetData(uid, DoorVisuals.Powered, args.Powered, appearanceComponent); - } + Appearance.SetData(uid, DoorVisuals.Powered, args.Powered, appearanceComponent); + } - if (!TryComp(uid, out DoorComponent? door)) - return; + if (!TryComp(uid, out DoorComponent? door)) + return; - if (!args.Powered) - { - // stop any scheduled auto-closing - if (door.State == DoorState.Open) - DoorSystem.SetNextStateChange(uid, null); - } - else - { - UpdateAutoClose(uid, door: door); - } + if (!args.Powered) + { + // stop any scheduled auto-closing + if (door.State == DoorState.Open) + DoorSystem.SetNextStateChange(uid, null); } - - private void OnStateChanged(EntityUid uid, AirlockComponent component, DoorStateChangedEvent args) + else { - // TODO move to shared? having this be server-side, but having client-side door opening/closing & prediction - // means that sometimes the panels & bolt lights may be visible despite a door being completely open. - - // Only show the maintenance panel if the airlock is closed - if (TryComp(uid, out var wiresPanel)) - { - _wiresSystem.ChangePanelVisibility(uid, wiresPanel, component.OpenPanelVisible || args.State != DoorState.Open); - } - // If the door is closed, we should look if the bolt was locked while closing - UpdateAutoClose(uid, component); - - // Make sure the airlock auto closes again next time it is opened - if (args.State == DoorState.Closed) - component.AutoClose = true; + UpdateAutoClose(uid, door: door); } + } + + private void OnStateChanged(EntityUid uid, AirlockComponent component, DoorStateChangedEvent args) + { + // TODO move to shared? having this be server-side, but having client-side door opening/closing & prediction + // means that sometimes the panels & bolt lights may be visible despite a door being completely open. - /// - /// Updates the auto close timer. - /// - public void UpdateAutoClose(EntityUid uid, AirlockComponent? airlock = null, DoorComponent? door = null) + // Only show the maintenance panel if the airlock is closed + if (TryComp(uid, out var wiresPanel)) { - if (!Resolve(uid, ref airlock, ref door)) - return; + _wiresSystem.ChangePanelVisibility(uid, wiresPanel, component.OpenPanelVisible || args.State != DoorState.Open); + } + // If the door is closed, we should look if the bolt was locked while closing + UpdateAutoClose(uid, component); - if (door.State != DoorState.Open) - return; + // Make sure the airlock auto closes again next time it is opened + if (args.State == DoorState.Closed) + component.AutoClose = true; + } - if (!airlock.AutoClose) - return; + /// + /// Updates the auto close timer. + /// + public void UpdateAutoClose(EntityUid uid, AirlockComponent? airlock = null, DoorComponent? door = null) + { + if (!Resolve(uid, ref airlock, ref door)) + return; - if (!CanChangeState(uid, airlock)) - return; + if (door.State != DoorState.Open) + return; - var autoev = new BeforeDoorAutoCloseEvent(); - RaiseLocalEvent(uid, autoev, false); - if (autoev.Cancelled) - return; + if (!airlock.AutoClose) + return; - DoorSystem.SetNextStateChange(uid, airlock.AutoCloseDelay * airlock.AutoCloseDelayModifier); - } + if (!CanChangeState(uid, airlock)) + return; - private void OnBeforeDoorOpened(EntityUid uid, AirlockComponent component, BeforeDoorOpenedEvent args) - { - if (!CanChangeState(uid, component)) - args.Cancel(); - } + var autoev = new BeforeDoorAutoCloseEvent(); + RaiseLocalEvent(uid, autoev, false); + if (autoev.Cancelled) + return; - protected override void OnBeforeDoorClosed(EntityUid uid, AirlockComponent component, BeforeDoorClosedEvent args) - { - base.OnBeforeDoorClosed(uid, component, args); + DoorSystem.SetNextStateChange(uid, airlock.AutoCloseDelay * airlock.AutoCloseDelayModifier); + } - if (args.Cancelled) - return; + private void OnBeforeDoorOpened(EntityUid uid, AirlockComponent component, BeforeDoorOpenedEvent args) + { + if (!CanChangeState(uid, component)) + args.Cancel(); + } - // only block based on bolts / power status when initially closing the door, not when its already - // mid-transition. Particularly relevant for when the door was pried-closed with a crowbar, which bypasses - // the initial power-check. + protected override void OnBeforeDoorClosed(EntityUid uid, AirlockComponent component, BeforeDoorClosedEvent args) + { + base.OnBeforeDoorClosed(uid, component, args); - if (TryComp(uid, out DoorComponent? door) - && !door.Partial - && !CanChangeState(uid, component)) - { - args.Cancel(); - } - } + if (args.Cancelled) + return; - private void OnBeforeDoorDenied(EntityUid uid, AirlockComponent component, BeforeDoorDeniedEvent args) - { - if (!CanChangeState(uid, component)) - args.Cancel(); - } + // only block based on bolts / power status when initially closing the door, not when its already + // mid-transition. Particularly relevant for when the door was pried-closed with a crowbar, which bypasses + // the initial power-check. - private void OnActivate(EntityUid uid, AirlockComponent component, ActivateInWorldEvent args) + if (TryComp(uid, out DoorComponent? door) + && !door.Partial + && !CanChangeState(uid, component)) { - if (TryComp(uid, out var panel) && panel.Open && - TryComp(args.User, out var actor)) - { - _wiresSystem.OpenUserInterface(uid, actor.PlayerSession); - args.Handled = true; - return; - } - - if (component.KeepOpenIfClicked) - { - // Disable auto close - component.AutoClose = false; - } + args.Cancel(); } + } - private void OnGetPryMod(EntityUid uid, AirlockComponent component, DoorGetPryTimeModifierEvent args) + private void OnBeforeDoorDenied(EntityUid uid, AirlockComponent component, BeforeDoorDeniedEvent args) + { + if (!CanChangeState(uid, component)) + args.Cancel(); + } + + private void OnActivate(EntityUid uid, AirlockComponent component, ActivateInWorldEvent args) + { + if (TryComp(uid, out var panel) && panel.Open && panel.WiresAccessible + && TryComp(args.User, out var actor)) { - if (_power.IsPowered(uid)) - args.PryTimeModifier *= component.PoweredPryModifier; + _wiresSystem.OpenUserInterface(uid, actor.PlayerSession); + args.Handled = true; + return; } - private void OnDoorPry(EntityUid uid, AirlockComponent component, BeforeDoorPryEvent args) + if (component.KeepOpenIfClicked) { - if (this.IsPowered(uid, EntityManager)) - { - if (HasComp(args.Tool)) - return; - Popup.PopupEntity(Loc.GetString("airlock-component-cannot-pry-is-powered-message"), uid, args.User); - args.Cancel(); - } + // Disable auto close + component.AutoClose = false; } + } - public bool CanChangeState(EntityUid uid, AirlockComponent component) + private void OnGetPryMod(EntityUid uid, AirlockComponent component, DoorGetPryTimeModifierEvent args) + { + if (_power.IsPowered(uid)) + args.PryTimeModifier *= component.PoweredPryModifier; + } + + private void OnDoorPry(EntityUid uid, AirlockComponent component, BeforeDoorPryEvent args) + { + if (this.IsPowered(uid, EntityManager)) { - return this.IsPowered(uid, EntityManager) && !_bolts.IsBolted(uid); + if (HasComp(args.Tool)) + return; + Popup.PopupEntity(Loc.GetString("airlock-component-cannot-pry-is-powered-message"), uid, args.User); + args.Cancel(); } } + + public bool CanChangeState(EntityUid uid, AirlockComponent component) + { + return this.IsPowered(uid, EntityManager) && !_bolts.IsBolted(uid); + } } diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index 70f7adfd289393..898addd0ffaef9 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -22,7 +22,6 @@ using Content.Shared.StatusEffect; using Content.Shared.Stunnable; using Content.Shared.Tag; -using Content.Shared.Weapons.Melee; using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Audio; using Robust.Shared.Map; diff --git a/Content.Server/Tools/Systems/WeldableSystem.cs b/Content.Server/Tools/Systems/WeldableSystem.cs index daaa44836cea32..e42f1f9104ef8a 100644 --- a/Content.Server/Tools/Systems/WeldableSystem.cs +++ b/Content.Server/Tools/Systems/WeldableSystem.cs @@ -1,5 +1,8 @@ using Content.Server.Administration.Logs; +using Content.Server.Construction; using Content.Server.Tools.Components; +using Content.Server.Wires; +using Content.Shared.Construction.Steps; using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.Examine; @@ -7,8 +10,10 @@ using Content.Shared.Tools; using Content.Shared.Tools.Components; using Content.Shared.Tools.Systems; +using Content.Shared.Wires; using Robust.Shared.Physics; using Robust.Shared.Physics.Systems; +using System.Linq; namespace Content.Server.Tools.Systems; @@ -18,6 +23,7 @@ public sealed class WeldableSystem : EntitySystem [Dependency] private readonly SharedToolSystem _toolSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly ConstructionSystem _construction = default!; public override void Initialize() { @@ -36,6 +42,14 @@ private void OnExamine(EntityUid uid, WeldableComponent component, ExaminedEvent private void OnInteractUsing(EntityUid uid, WeldableComponent component, InteractUsingEvent args) { + // If any construction graph edges has its conditions meet and requires welding, then this construction takes priority + if (_construction.GetCurrentNode(uid)?.Edges.Any(x => _construction.CheckConditions(uid, x.Conditions) + && x.Steps.Any(y => (y as ToolConstructionGraphStep)?.Tool == "Welding")) == true) + { + args.Handled = false; + return; + } + if (args.Handled) return; diff --git a/Content.Server/Wires/WiresSystem.cs b/Content.Server/Wires/WiresSystem.cs index 8c99852971338d..26b85e724ba4ed 100644 --- a/Content.Server/Wires/WiresSystem.cs +++ b/Content.Server/Wires/WiresSystem.cs @@ -31,6 +31,7 @@ public sealed class WiresSystem : SharedWiresSystem [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly WiresSystem _wiresSystem = default!; // This is where all the wire layouts are stored. [ViewVariables] private readonly Dictionary _layouts = new(); @@ -455,7 +456,7 @@ private void OnInteractUsing(EntityUid uid, WiresComponent component, InteractUs if (!TryComp(args.Used, out var tool) || !TryComp(uid, out var panel)) return; - if (panel.Open && + if (panel.Open && panel.WiresAccessible && (_toolSystem.HasQuality(args.Used, "Cutting", tool) || _toolSystem.HasQuality(args.Used, "Pulsing", tool))) { @@ -631,6 +632,23 @@ public void TogglePanel(EntityUid uid, WiresPanelComponent component, bool open) Dirty(component); } + public void SetWiresPanelSecurityData(EntityUid uid, WiresPanelComponent component, string wiresPanelSecurityLevelID) + { + var wiresPanelSecurityLevelPrototype = _protoMan.Index(wiresPanelSecurityLevelID); + + if (wiresPanelSecurityLevelPrototype == null) + return; + + component.WiresAccessible = wiresPanelSecurityLevelPrototype.WiresAccessible; + component.WiresPanelSecurityExamination = wiresPanelSecurityLevelPrototype.Examine; + Dirty(component); + + if (wiresPanelSecurityLevelPrototype?.WiresAccessible == false) + { + _uiSystem.TryCloseAll(uid, WiresUiKey.Key); + } + } + private void UpdateAppearance(EntityUid uid, WiresPanelComponent panel) { if (TryComp(uid, out var appearance)) diff --git a/Content.Shared/Wires/SharedWiresSystem.cs b/Content.Shared/Wires/SharedWiresSystem.cs index d7ddac4de2d5e4..78cb2980708268 100644 --- a/Content.Shared/Wires/SharedWiresSystem.cs +++ b/Content.Shared/Wires/SharedWiresSystem.cs @@ -1,5 +1,4 @@ using Content.Shared.Examine; -using Robust.Shared.GameStates; namespace Content.Shared.Wires; @@ -9,31 +8,22 @@ public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnExamine); - SubscribeLocalEvent(OnGetState); - SubscribeLocalEvent(OnHandleState); } private void OnExamine(EntityUid uid, WiresPanelComponent component, ExaminedEvent args) { - args.PushMarkup(Loc.GetString(component.Open - ? "wires-panel-component-on-examine-open" - : "wires-panel-component-on-examine-closed")); - } - - private void OnGetState(EntityUid uid, WiresPanelComponent component, ref ComponentGetState args) - { - args.State = new WiresPanelComponentState + if (!component.Open) { - Open = component.Open, - Visible = component.Visible - }; - } + args.PushMarkup(Loc.GetString("wires-panel-component-on-examine-closed")); + } + else + { + args.PushMarkup(Loc.GetString("wires-panel-component-on-examine-open")); - private void OnHandleState(EntityUid uid, WiresPanelComponent component, ref ComponentHandleState args) - { - if (args.Current is not WiresPanelComponentState state) - return; - component.Open = state.Open; - component.Visible = state.Visible; + if (component?.WiresPanelSecurityExamination != null) + { + args.PushMarkup(Loc.GetString(component.WiresPanelSecurityExamination)); + } + } } -} \ No newline at end of file +} diff --git a/Content.Shared/Wires/WiresPanelComponent.cs b/Content.Shared/Wires/WiresPanelComponent.cs index 668e7f01007e93..683bd3f830966b 100644 --- a/Content.Shared/Wires/WiresPanelComponent.cs +++ b/Content.Shared/Wires/WiresPanelComponent.cs @@ -1,23 +1,25 @@ using Robust.Shared.Audio; using Robust.Shared.GameStates; -using Robust.Shared.Serialization; namespace Content.Shared.Wires; [NetworkedComponent, RegisterComponent] [Access(typeof(SharedWiresSystem))] -public sealed class WiresPanelComponent : Component +[AutoGenerateComponentState] +public sealed partial class WiresPanelComponent : Component { /// /// Is the panel open for this entity's wires? /// [DataField("open")] + [AutoNetworkedField] public bool Open; /// /// Should this entity's wires panel be visible at all? /// [ViewVariables] + [AutoNetworkedField] public bool Visible = true; [DataField("screwdriverOpenSound")] @@ -25,11 +27,10 @@ public sealed class WiresPanelComponent : Component [DataField("screwdriverCloseSound")] public SoundSpecifier ScrewdriverCloseSound = new SoundPathSpecifier("/Audio/Machines/screwdriverclose.ogg"); -} -[Serializable, NetSerializable] -public sealed class WiresPanelComponentState : ComponentState -{ - public bool Open; - public bool Visible; + [AutoNetworkedField] + public string? WiresPanelSecurityExamination = default!; + + [AutoNetworkedField] + public bool WiresAccessible = true; } diff --git a/Content.Shared/Wires/WiresPanelSecurityLevelPrototype.cs b/Content.Shared/Wires/WiresPanelSecurityLevelPrototype.cs new file mode 100644 index 00000000000000..1fcc982f109f14 --- /dev/null +++ b/Content.Shared/Wires/WiresPanelSecurityLevelPrototype.cs @@ -0,0 +1,16 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Wires; + +[Prototype("WiresPanelSecurityLevel")] +public sealed class WiresPanelSecurityLevelPrototype : IPrototype +{ + [IdDataField] + public string ID { get; } = default!; + + [DataField("examine")] + public string? Examine = default!; + + [DataField("wiresAccessible")] + public bool WiresAccessible = true; +} diff --git a/Resources/Locale/en-US/wires/wires_panel-security-levels.ftl b/Resources/Locale/en-US/wires/wires_panel-security-levels.ftl new file mode 100644 index 00000000000000..a932877c582a41 --- /dev/null +++ b/Resources/Locale/en-US/wires/wires_panel-security-levels.ftl @@ -0,0 +1,8 @@ +# Examination for different levels of wiring protection +wires-panel-component-on-examine-security-level1 = There is a steel plate preventing access to the internal wiring. Use a [color=cyan]Crowbar[/color] to remove it. +wires-panel-component-on-examine-security-level2 = A steel plate has been welded to the inside the [color=lightgray]maintenance panel[/color]. Use a [color=cyan]Welder[/color] to free it. +wires-panel-component-on-examine-security-level3 = There is a plasteel plate preventing access to the internal wiring. Use a [color=cyan]Crowbar[/color] to remove it. +wires-panel-component-on-examine-security-level4 = A plasteel plate has been welded to the inside the [color=lightgray]maintenance panel[/color]. Use a [color=cyan]Welder[/color] to free it. +wires-panel-component-on-examine-security-level5 = The inside of the [color=lightgray]maintenance panel[/color] is protected by a security grille. Use [color=cyan]Wirecutters[/color] to remove it. +wires-panel-component-on-examine-security-level6 = A plasteel plate sits within the interior of the [color=lightgray]maintenance panel[/color]. Use a [color=cyan]Crowbar[/color] to remove it. +wires-panel-component-on-examine-security-level7 = A welded plasteel plate protects the interior of the [color=lightgray]maintenance panel[/color]. Use a [color=cyan]Welder[/color] to free it. diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml index cdd0ac6a90c54a..0d90cd1e451d7e 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml @@ -21,6 +21,8 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/atmospherics.rsi + - type: Construction + node: airlockMedSecurity - type: entity parent: Airlock @@ -69,6 +71,8 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/command.rsi + - type: Construction + node: airlockMaxSecurity - type: entity parent: Airlock @@ -77,6 +81,8 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/security.rsi + - type: Construction + node: airlockMedSecurity - type: entity parent: Airlock @@ -156,6 +162,8 @@ sprite: Structures/Doors/Airlocks/Glass/atmospherics.rsi - type: PaintableAirlock group: Glass + - type: Construction + node: airlockMedSecurity - type: entity parent: AirlockGlass @@ -206,6 +214,8 @@ sprite: Structures/Doors/Airlocks/Glass/command.rsi - type: PaintableAirlock group: Glass + - type: Construction + node: airlockMaxSecurity - type: entity parent: AirlockGlass @@ -216,3 +226,5 @@ sprite: Structures/Doors/Airlocks/Glass/security.rsi - type: PaintableAirlock group: Glass + - type: Construction + node: airlockMedSecurity diff --git a/Resources/Prototypes/Entities/Structures/Doors/wires_panel_security.yml b/Resources/Prototypes/Entities/Structures/Doors/wires_panel_security.yml new file mode 100644 index 00000000000000..fb5b7626620643 --- /dev/null +++ b/Resources/Prototypes/Entities/Structures/Doors/wires_panel_security.yml @@ -0,0 +1,39 @@ +- type: WiresPanelSecurityLevel + id: Level0 + wiresAccessible: true + +- type: WiresPanelSecurityLevel + id: Level1 + examine: wires-panel-component-on-examine-security-level1 + wiresAccessible: false + +- type: WiresPanelSecurityLevel + id: Level2 + examine: wires-panel-component-on-examine-security-level2 + wiresAccessible: false + +- type: WiresPanelSecurityLevel + id: Level3 + examine: wires-panel-component-on-examine-security-level3 + wiresAccessible: false + +- type: WiresPanelSecurityLevel + id: Level4 + examine: wires-panel-component-on-examine-security-level4 + wiresAccessible: false + +- type: WiresPanelSecurityLevel + id: Level5 + examine: wires-panel-component-on-examine-security-level5 + wiresAccessible: false + +- type: WiresPanelSecurityLevel + id: Level6 + examine: wires-panel-component-on-examine-security-level6 + wiresAccessible: false + +- type: WiresPanelSecurityLevel + id: Level7 + examine: wires-panel-component-on-examine-security-level7 + wiresAccessible: false + \ No newline at end of file diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/structures/airlock.yml b/Resources/Prototypes/Recipes/Construction/Graphs/structures/airlock.yml index 2faa18563a4a17..47478ac0fe835b 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/structures/airlock.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/structures/airlock.yml @@ -86,24 +86,6 @@ - tool: Prying doAfter: 5 - - - node: airlock - entity: Airlock - edges: - - to: wired #TODO DOOR ELECTRONICS. If door electronics ever govern access permissions, this step should probably be further down? It makes it too easy to swap permissions around. See also windoor. - conditions: - - !type:EntityAnchored {} - - !type:DoorWelded {} - - !type:DoorBolted - value: false - - !type:WirePanel {} - - !type:AllWiresCut - completed: - - !type:EmptyAllContainers {} - steps: - - tool: Prying - doAfter: 5 - - node: glassElectronics entity: AirlockAssembly edges: @@ -146,3 +128,217 @@ steps: - tool: Prying doAfter: 2 + + - node: airlock + entity: Airlock + actions: + - !type:ChangeWiresPanelSecurityLevel + level: Level0 + edges: + - to: wired #TODO DOOR ELECTRONICS. If door electronics ever govern access permissions, this step should probably be further down? It makes it too easy to swap permissions around. See also windoor. + conditions: + - !type:EntityAnchored {} + - !type:DoorWelded {} + - !type:DoorBolted + value: false + - !type:WirePanel {} + - !type:AllWiresCut + completed: + - !type:EmptyAllContainers {} + steps: + - tool: Prying + doAfter: 5 + + - to: airlockMedSecurityBreached + conditions: + - !type:WirePanel {} + steps: + - material: Steel + amount: 2 + doAfter: 2 + + - to: airlockHighSecurityBreached + conditions: + - !type:WirePanel {} + steps: + - material: Plasteel + amount: 2 + doAfter: 2 + +## Return node so that removing all internal plating doesn't reset the door + - node: airlockUnsecured + actions: + - !type:ChangeWiresPanelSecurityLevel + level: Level0 + edges: + - to: wired + conditions: + - !type:EntityAnchored {} + - !type:DoorWelded {} + - !type:DoorBolted + value: false + - !type:WirePanel {} + - !type:AllWiresCut + completed: + - !type:EmptyAllContainers {} + steps: + - tool: Prying + doAfter: 5 + + - to: airlockMedSecurityBreached + conditions: + - !type:WirePanel {} + steps: + - material: Steel + amount: 2 + doAfter: 2 + + - to: airlockHighSecurityBreached + conditions: + - !type:WirePanel {} + steps: + - material: Plasteel + amount: 2 + doAfter: 2 + +## Medium security level airlock: a layer of steel plating protects the internal wiring + - node: airlockMedSecurityBreached + actions: + - !type:ChangeWiresPanelSecurityLevel + level: Level1 + edges: + - to: airlockUnsecured + completed: + - !type:GivePrototype + prototype: SheetSteel1 + amount: 2 + conditions: + - !type:WirePanel {} + steps: + - tool: Prying + doAfter: 4 + + - to: airlockMedSecurity + conditions: + - !type:WirePanel {} + steps: + - tool: Welding + doAfter: 3 + + - node: airlockMedSecurity + actions: + - !type:ChangeWiresPanelSecurityLevel + level: Level2 + edges: + - to: airlockMedSecurityBreached + conditions: + - !type:WirePanel {} + steps: + - tool: Welding + doAfter: 10 + +## High security level airlock: a layer of plasteel plating protects the internal wiring + - node: airlockHighSecurityBreached + actions: + - !type:ChangeWiresPanelSecurityLevel + level: Level3 + edges: + - to: airlockUnsecured + completed: + - !type:GivePrototype + prototype: SheetPlasteel1 + amount: 2 + conditions: + - !type:WirePanel {} + steps: + - tool: Prying + doAfter: 4 + + - to: airlockHighSecurity + conditions: + - !type:WirePanel {} + steps: + - tool: Welding + doAfter: 5 + + - node: airlockHighSecurity + actions: + - !type:ChangeWiresPanelSecurityLevel + level: Level4 + edges: + - to: airlockHighSecurityBreached + conditions: + - !type:WirePanel {} + steps: + - tool: Welding + doAfter: 15 + + - to: airlockMaxSecurity + conditions: + - !type:WirePanel {} + steps: + - material: MetalRod + amount: 2 + doAfter: 1 + +## Max security level airlock: an electric grill is added + - node: airlockMaxSecurity + actions: + - !type:ChangeWiresPanelSecurityLevel + level: Level5 + edges: + - to: airlockHighSecurity + completed: + - !type:AttemptElectrocute + - !type:GivePrototype + prototype: PartRodMetal1 + amount: 2 + conditions: + - !type:WirePanel {} + steps: + - tool: Cutting + doAfter: 0.5 + + - to: airlockSuperMaxSecurityBreached + conditions: + - !type:WirePanel {} + steps: + - material: Plasteel + amount: 2 + doAfter: 2 + +## Super max security level airlock: an additional layer of plasteel is added + - node: airlockSuperMaxSecurityBreached + actions: + - !type:ChangeWiresPanelSecurityLevel + level: Level6 + edges: + - to: airlockMaxSecurity + completed: + - !type:GivePrototype + prototype: SheetPlasteel1 + amount: 2 + conditions: + - !type:WirePanel {} + steps: + - tool: Prying + doAfter: 4 + + - to: airlockSuperMaxSecurity + conditions: + - !type:WirePanel {} + steps: + - tool: Welding + doAfter: 5 + + - node: airlockSuperMaxSecurity + actions: + - !type:ChangeWiresPanelSecurityLevel + level: Level7 + edges: + - to: airlockSuperMaxSecurityBreached + conditions: + - !type:WirePanel {} + steps: + - tool: Welding + doAfter: 15 From 0964496a36978cf03fb9663977be0248af4d984c Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 10 Aug 2023 04:34:09 -0400 Subject: [PATCH 2/6] Automatic changelog update --- Resources/Changelog/Changelog.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 8871e0b2a30075..28f2024d958913 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,10 +1,4 @@ Entries: -- author: Slava0135 - changes: - - {message: added mass hallucinations event that temporarily makes everyone on station - hear random sounds (paracusia), type: Add} - id: 4006 - time: '2023-06-15T06:45:51.0000000+00:00' - author: EmoGarbage404 changes: - {message: Our top scientists have injected our artifacts with foaming agents to @@ -2979,3 +2973,10 @@ Entries: - {message: Advanced laser guns recharge 25% slower than before., type: Tweak} id: 4505 time: '2023-08-10T07:27:02.0000000+00:00' +- author: chromiumboy + changes: + - {message: Obstacles to deter hackers can now be added to airlocks., type: Add} + - {message: Security and Atmospherics airlocks start with a moderate level of protection. + Command airlocks start with an even higher level of security., type: Add} + id: 4506 + time: '2023-08-10T08:33:04.0000000+00:00' From 829f2f8976195a38229603696d063f24e88cc88d Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Thu, 10 Aug 2023 04:34:38 -0400 Subject: [PATCH 3/6] Remove request computer board steal objective (#18925) * remove request computer board steal objective * this thing, too --- .../Objects/Devices/Circuitboards/computer.yml | 3 +-- .../Prototypes/Objectives/objectiveGroups.yml | 1 - .../Prototypes/Objectives/traitorObjectives.yml | 15 --------------- 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml index 44ea3cf1f02e89..1066bbb07ce9fb 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml @@ -87,7 +87,6 @@ - type: Tag tags: - DroneUsable - - HighRiskItem - type: entity id: CargoBountyComputerCircuitboard @@ -377,4 +376,4 @@ - type: StaticPrice price: 150 - type: ComputerBoard - prototype: ComputerMassMedia \ No newline at end of file + prototype: ComputerMassMedia diff --git a/Resources/Prototypes/Objectives/objectiveGroups.yml b/Resources/Prototypes/Objectives/objectiveGroups.yml index c6a7b660e8f8f8..8145772d55e8f3 100644 --- a/Resources/Prototypes/Objectives/objectiveGroups.yml +++ b/Resources/Prototypes/Objectives/objectiveGroups.yml @@ -16,7 +16,6 @@ NukeDiskStealObjective: 1 IDComputerBoardStealObjective: 1 MagbootsStealObjective: 1 - SupplyConsoleBoardStealObjective: 1 CorgiMeatStealObjective: 1 CaptainGunStealObjective: 0.5 CaptainJetpackStealObjective: 0.5 diff --git a/Resources/Prototypes/Objectives/traitorObjectives.yml b/Resources/Prototypes/Objectives/traitorObjectives.yml index b66abb398fa16b..d4bd660157b004 100644 --- a/Resources/Prototypes/Objectives/traitorObjectives.yml +++ b/Resources/Prototypes/Objectives/traitorObjectives.yml @@ -187,21 +187,6 @@ prototype: ClothingShoesBootsMagAdv owner: job-name-ce -- type: objective - id: SupplyConsoleBoardStealObjective - issuer: syndicate - requirements: - - !type:TraitorRequirement {} - - !type:IncompatibleConditionsRequirement - conditions: - - DieCondition - - !type:NotRoleRequirement - roleId: Quartermaster - conditions: - - !type:StealCondition - prototype: CargoRequestComputerCircuitboard - owner: job-name-qm - - type: objective id: CorgiMeatStealObjective issuer: syndicate From 7e1e76119d16690a4852caf2fc8b07a334161d5e Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 10 Aug 2023 04:35:42 -0400 Subject: [PATCH 4/6] Automatic changelog update --- Resources/Changelog/Changelog.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 28f2024d958913..d911cf60f851bf 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: EmoGarbage404 - changes: - - {message: Our top scientists have injected our artifacts with foaming agents to - greatly increase their foam output. Let's just hope that there's nothing dangerous - inside of it., type: Fix} - id: 4007 - time: '2023-06-15T11:26:10.0000000+00:00' - author: lzk changes: - {message: Changed sprites, type: Tweak} @@ -2980,3 +2973,9 @@ Entries: Command airlocks start with an even higher level of security., type: Add} id: 4506 time: '2023-08-10T08:33:04.0000000+00:00' +- author: EmoGarbage404 + changes: + - {message: The Syndicate are no longer interested in stealing Cargo's request computer + boards., type: Remove} + id: 4507 + time: '2023-08-10T08:34:38.0000000+00:00' From b2bf49235b2959eb76d4d3a41eefe25fdd94bcb1 Mon Sep 17 00:00:00 2001 From: Sailor <109166122+Equivocateur@users.noreply.github.com> Date: Thu, 10 Aug 2023 11:40:17 +0300 Subject: [PATCH 5/6] Nerf emp pricing, add EMP kit to uplink (#18877) --- Resources/Locale/en-US/store/uplink-catalog.ftl | 3 +++ .../Prototypes/Catalog/Fills/Boxes/syndicate.yml | 12 ++++++++++++ Resources/Prototypes/Catalog/uplink_catalog.yml | 14 ++++++++++++-- 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 Resources/Prototypes/Catalog/Fills/Boxes/syndicate.yml diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index dfed0cff9e5be7..f91dc88fe231f7 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -140,6 +140,9 @@ uplink-deathrattle-implant-name = Box Of Deathrattle Implants uplink-deathrattle-implant-desc = A box containing enough deathrattle implants for the whole squad. Relays a message containing your position to the syndicate channel when you go into a critical state or die. # Bundles +uplink-emp-kit-name = Electrical Disruptor Kit +uplink-emp-kit-desc = The ultimate reversal on energy-based weaponry: Disables disablers, stuns stunbatons, discharges laser guns! Contains 3 EMP grenades and an EMP implanter. Note: Does not disrupt actual firearms. + uplink-meds-bundle-name = Medical Bundle uplink-meds-bundle-desc = All you need to get your comrades back in the fight: mainly a combat medkit, a defibrillator and three combat medipens. diff --git a/Resources/Prototypes/Catalog/Fills/Boxes/syndicate.yml b/Resources/Prototypes/Catalog/Fills/Boxes/syndicate.yml new file mode 100644 index 00000000000000..a6c3ca0d6aaab5 --- /dev/null +++ b/Resources/Prototypes/Catalog/Fills/Boxes/syndicate.yml @@ -0,0 +1,12 @@ +- type: entity + id: ElectricalDisruptionKit + parent: BoxCardboard + name: electrical disruption kit + suffix: Filled + components: + - type: StorageFill + contents: + - id: EmpGrenade + amount: 3 + - id: EmpImplanter + amount: 1 diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index 062e7c19373073..53e9704c1d05d2 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -204,7 +204,7 @@ description: uplink-emp-grenade-desc productEntity: EmpGrenade cost: - Telecrystal: 3 + Telecrystal: 2 categories: - UplinkExplosives @@ -526,7 +526,7 @@ icon: { sprite: /Textures/Objects/Magic/magicactions.rsi, state: shield } productEntity: EmpImplanter cost: - Telecrystal: 5 + Telecrystal: 3 categories: - UplinkImplants @@ -587,6 +587,16 @@ # Bundles +- type: listing + id: UplinkEmpKit + name: uplink-emp-kit-name + description: uplink-emp-kit-desc + productEntity: ElectricalDisruptionKit + cost: + Telecrystal: 6 + categories: + - UplinkBundles + - type: listing id: UplinkAmmoBundle name: uplink-ammo-bundle-name From 4b803afa62e09a31281ebad0661513c059a36fca Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 10 Aug 2023 04:41:21 -0400 Subject: [PATCH 6/6] Automatic changelog update --- Resources/Changelog/Changelog.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index d911cf60f851bf..2d6d4cb7a2585c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,9 +1,4 @@ Entries: -- author: lzk - changes: - - {message: Changed sprites, type: Tweak} - id: 4008 - time: '2023-06-15T11:55:07.0000000+00:00' - author: deltanedas changes: - {message: Fixed being able to insert tanks/jetpacks into locked gas canisters., @@ -2979,3 +2974,11 @@ Entries: boards., type: Remove} id: 4507 time: '2023-08-10T08:34:38.0000000+00:00' +- author: Equivocateur + changes: + - {message: 'Added an electrical disruption kit to syndicate operatives, containing + 3 EMP grenades and an EMP implanter. Costs 6 telecrystals.', type: Add} + - {message: Changed EMP grenade pricing to 2 telecrystals., type: Tweak} + - {message: Changed EMP implant pricing to 3 telecrystals., type: Tweak} + id: 4508 + time: '2023-08-10T08:40:17.0000000+00:00'