disallow unanchoring or opening panels on locked emitters/APEs (#26600)

* disallow unanchoring or opening panels on locked emitters/APEs

* no locking open panels

* oops

* needback feedback

* Update Content.Shared/Lock/LockSystem.cs

* Update Content.Shared/Lock/LockSystem.cs

* Update Content.Shared/Lock/LockSystem.cs

* sanity

---------

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
Nemanja
2024-03-31 02:34:17 -04:00
committed by GitHub
parent d512bc141a
commit 1b69762816
12 changed files with 129 additions and 15 deletions

View File

@@ -163,10 +163,13 @@ namespace Content.Client.Popups
PopupEntity(message, uid, type);
}
public override void PopupClient(string? message, EntityUid uid, EntityUid recipient, PopupType type = PopupType.Small)
public override void PopupClient(string? message, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small)
{
if (recipient == null)
return;
if (_timing.IsFirstTimePredicted)
PopupEntity(message, uid, recipient, type);
PopupEntity(message, uid, recipient.Value, type);
}
public override void PopupEntity(string? message, EntityUid uid, PopupType type = PopupType.Small)

View File

@@ -88,7 +88,7 @@ namespace Content.Server.Popups
RaiseNetworkEvent(new PopupEntityEvent(message, type, GetNetEntity(uid)), actor.PlayerSession);
}
public override void PopupClient(string? message, EntityUid uid, EntityUid recipient, PopupType type = PopupType.Small)
public override void PopupClient(string? message, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small)
{
// do nothing duh its for client only
}

View File

@@ -1,5 +1,6 @@
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Construction.Components;
using Content.Shared.DoAfter;
using Content.Shared.Emag.Systems;
using Content.Shared.Examine;
@@ -9,6 +10,7 @@ using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared.Storage.Components;
using Content.Shared.Verbs;
using Content.Shared.Wires;
using JetBrains.Annotations;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Utility;
@@ -40,8 +42,11 @@ public sealed class LockSystem : EntitySystem
SubscribeLocalEvent<LockComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<LockComponent, LockDoAfter>(OnDoAfterLock);
SubscribeLocalEvent<LockComponent, UnlockDoAfter>(OnDoAfterUnlock);
}
SubscribeLocalEvent<LockedWiresPanelComponent, LockToggleAttemptEvent>(OnLockToggleAttempt);
SubscribeLocalEvent<LockedWiresPanelComponent, AttemptChangePanelEvent>(OnAttemptChangePanel);
SubscribeLocalEvent<LockedAnchorableComponent, UnanchorAttemptEvent>(OnUnanchorAttempt);
}
private void OnStartup(EntityUid uid, LockComponent lockComp, ComponentStartup args)
{
_appearanceSystem.SetData(uid, LockVisuals.Locked, lockComp.Locked);
@@ -226,18 +231,18 @@ public sealed class LockSystem : EntitySystem
private void AddToggleLockVerb(EntityUid uid, LockComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanAccess || !args.CanInteract || !CanToggleLock(uid, args.User))
if (!args.CanAccess || !args.CanInteract)
return;
AlternativeVerb verb = new()
{
Act = component.Locked ?
() => TryUnlock(uid, args.User, component) :
() => TryLock(uid, args.User, component),
Act = component.Locked
? () => TryUnlock(uid, args.User, component)
: () => TryLock(uid, args.User, component),
Text = Loc.GetString(component.Locked ? "toggle-lock-verb-unlock" : "toggle-lock-verb-lock"),
Icon = component.Locked ?
new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/unlock.svg.192dpi.png")) :
new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/lock.svg.192dpi.png")),
Icon = !component.Locked
? new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/lock.svg.192dpi.png"))
: new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/unlock.svg.192dpi.png")),
};
args.Verbs.Add(verb);
}
@@ -275,5 +280,53 @@ public sealed class LockSystem : EntitySystem
TryUnlock(uid, args.User, skipDoAfter: true);
}
private void OnLockToggleAttempt(Entity<LockedWiresPanelComponent> ent, ref LockToggleAttemptEvent args)
{
if (args.Cancelled)
return;
if (!TryComp<WiresPanelComponent>(ent, out var panel) || !panel.Open)
return;
if (!args.Silent)
{
_sharedPopupSystem.PopupClient(Loc.GetString("construction-step-condition-wire-panel-close"),
ent,
args.User);
}
args.Cancelled = true;
}
private void OnAttemptChangePanel(Entity<LockedWiresPanelComponent> ent, ref AttemptChangePanelEvent args)
{
if (args.Cancelled)
return;
if (!TryComp<LockComponent>(ent, out var lockComp) || !lockComp.Locked)
return;
_sharedPopupSystem.PopupClient(Loc.GetString("lock-comp-generic-fail",
("target", Identity.Entity(ent, EntityManager))),
ent,
args.User);
args.Cancelled = true;
}
private void OnUnanchorAttempt(Entity<LockedAnchorableComponent> ent, ref UnanchorAttemptEvent args)
{
if (args.Cancelled)
return;
if (!TryComp<LockComponent>(ent, out var lockComp) || !lockComp.Locked)
return;
_sharedPopupSystem.PopupClient(Loc.GetString("lock-comp-generic-fail",
("target", Identity.Entity(ent, EntityManager))),
ent,
args.User);
args.Cancel();
}
}

View File

@@ -0,0 +1,13 @@
using Content.Shared.Construction.Components;
using Robust.Shared.GameStates;
namespace Content.Shared.Lock;
/// <summary>
/// This is used for a <see cref="AnchorableComponent"/> that cannot be unanchored while locked.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(LockSystem))]
public sealed partial class LockedAnchorableComponent : Component
{
}

View File

@@ -0,0 +1,13 @@
using Content.Shared.Wires;
using Robust.Shared.GameStates;
namespace Content.Shared.Lock;
/// <summary>
/// This is used for a <see cref="WiresPanelComponent"/> that cannot be opened while locked.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(LockSystem))]
public sealed partial class LockedWiresPanelComponent : Component
{
}

View File

@@ -86,7 +86,7 @@ namespace Content.Shared.Popups
/// Variant of <see cref="PopupEntity(string, EntityUid, EntityUid, PopupType)"/> that only runs on the client, outside of prediction.
/// Useful for shared code that is always ran by both sides to avoid duplicate popups.
/// </summary>
public abstract void PopupClient(string? message, EntityUid uid, EntityUid recipient, PopupType type = PopupType.Small);
public abstract void PopupClient(string? message, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small);
/// <summary>
/// Variant of <see cref="PopupEntity(string, EntityUid, EntityUid, PopupType)"/> for use with prediction. The local client will show

View File

@@ -28,15 +28,24 @@ public abstract class SharedWiresSystem : EntitySystem
if (args.Cancelled)
return;
TogglePanel(uid, panel, !panel.Open);
if (!TogglePanel(uid, panel, !panel.Open, args.User))
return;
AdminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.User):user} screwed {ToPrettyString(uid):target}'s maintenance panel {(panel.Open ? "open" : "closed")}");
var sound = panel.Open ? panel.ScrewdriverOpenSound : panel.ScrewdriverCloseSound;
Audio.PlayPredicted(sound, uid, args.User);
args.Handled = true;
}
private void OnInteractUsing(Entity<WiresPanelComponent> ent, ref InteractUsingEvent args)
{
if (!Tool.HasQuality(args.Used, ent.Comp.OpeningTool))
return;
if (!CanTogglePanel(ent, args.User))
return;
if (!Tool.UseTool(
args.Used,
args.User,
@@ -89,14 +98,25 @@ public abstract class SharedWiresSystem : EntitySystem
Appearance.SetData(uid, WiresVisuals.MaintenancePanelState, panel.Open && panel.Visible, appearance);
}
public void TogglePanel(EntityUid uid, WiresPanelComponent component, bool open)
public bool TogglePanel(EntityUid uid, WiresPanelComponent component, bool open, EntityUid? user = null)
{
if (!CanTogglePanel((uid, component), user))
return false;
component.Open = open;
UpdateAppearance(uid, component);
Dirty(uid, component);
var ev = new PanelChangedEvent(component.Open);
RaiseLocalEvent(uid, ref ev);
return true;
}
public bool CanTogglePanel(Entity<WiresPanelComponent> ent, EntityUid? user)
{
var attempt = new AttemptChangePanelEvent(ent.Comp.Open, user);
RaiseLocalEvent(ent, ref attempt);
return !attempt.Cancelled;
}
public bool IsPanelOpen(Entity<WiresPanelComponent?> entity)

View File

@@ -57,6 +57,12 @@ public sealed partial class WiresPanelComponent : Component
public LocId? ExamineTextOpen = "wires-panel-component-on-examine-open";
}
/// <summary>
/// Event raised on a <see cref="WiresPanelComponent"/> before its open state is about to be changed.
/// </summary>
[ByRefEvent]
public record struct AttemptChangePanelEvent(bool Open, EntityUid? User, bool Cancelled = false);
/// <summary>
/// Event raised when a panel is opened or closed.
/// </summary>

View File

@@ -3,8 +3,9 @@ lock-comp-on-examined-is-unlocked = The {$entityName} seems to be unlocked.
lock-comp-do-lock-success = You lock the {$entityName}.
lock-comp-do-unlock-success = You unlock the {$entityName}.
lock-comp-has-user-access-fail = Access denied
lock-comp-generic-fail = {CAPITALIZE(SUBJECT($target))} {CONJUGATE-BE($target)} locked.
## ToggleLockVerb
toggle-lock-verb-unlock = Unlock
toggle-lock-verb-lock = Lock
toggle-lock-verb-lock = Lock

View File

@@ -140,6 +140,7 @@
- type: Lock
locked: true
- type: ActivatableUIRequiresLock
- type: LockedWiresPanel
- type: Flashable
- type: Damageable
damageContainer: Silicon

View File

@@ -160,6 +160,7 @@
board: APECircuitboard
- type: Lock
locked: false
- type: LockedWiresPanel
- type: AccessReader
access: [[ "Research" ]]
- type: Emitter
@@ -204,6 +205,7 @@
True: { visible: true }
False: { visible: false }
- type: LockVisuals
- type: LockedAnchorable
- type: DeviceNetwork
deviceNetId: Wireless
receiveFrequencyId: BasicDevice

View File

@@ -84,6 +84,8 @@
- type: Lock
locked: false
- type: LockVisuals
- type: LockedAnchorable
- type: LockedWiresPanel
- type: AccessReader
access: [[ "Engineering" ]]
- type: Machine