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)
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
}
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;
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;
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);
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);
}
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();
+ }
}
--- /dev/null
+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
+{
+
+}
--- /dev/null
+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
+{
+
+}
/// 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
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,
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)
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>
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
\ No newline at end of file
+toggle-lock-verb-lock = Lock
- type: Lock
locked: true
- type: ActivatableUIRequiresLock
+ - type: LockedWiresPanel
- type: Flashable
- type: Damageable
damageContainer: Silicon
board: APECircuitboard
- type: Lock
locked: false
+ - type: LockedWiresPanel
- type: AccessReader
access: [[ "Research" ]]
- type: Emitter
True: { visible: true }
False: { visible: false }
- type: LockVisuals
+ - type: LockedAnchorable
- type: DeviceNetwork
deviceNetId: Wireless
receiveFrequencyId: BasicDevice
- type: Lock
locked: false
- type: LockVisuals
+ - type: LockedAnchorable
+ - type: LockedWiresPanel
- type: AccessReader
access: [[ "Engineering" ]]
- type: Machine