using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.Interaction; using Content.Shared.Lock.BypassLock.Components; using Content.Shared.Tools; using Content.Shared.Tools.Systems; using Content.Shared.Verbs; using Content.Shared.Wires; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Lock.BypassLock.Systems; public sealed partial class BypassLockSystem : EntitySystem { [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly LockSystem _lock = default!; [Dependency] private readonly SharedToolSystem _tool = default!; [Dependency] private readonly SharedWiresSystem _wires = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnBypassAccessDoAfterEvent); SubscribeLocalEvent>(OnGetVerb); InitializeMobStateLockSystem(); } private void OnInteractUsing(Entity target, ref InteractUsingEvent args) { if (target.Owner == args.User) return; if (!_tool.HasQuality(args.Used, target.Comp.BypassingTool) || !_lock.IsLocked(target.Owner)) return; var ev = new ForceOpenLockAttemptEvent(args.User); RaiseLocalEvent(target.Owner, ref ev); if (!ev.CanForceOpen) return; args.Handled = TryStartDoAfter(target, args.User, args.Used); } private bool TryStartDoAfter(Entity target, EntityUid user, EntityUid used) { if (!_tool.UseTool( used, user, target, (float) target.Comp.BypassDelay.TotalSeconds, target.Comp.BypassingTool, new ForceOpenLockDoAfterEvent())) { return false; } _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user):user} is prying {ToPrettyString(target):target}'s lock open at {Transform(target).Coordinates:targetlocation}"); return true; } private void OnBypassAccessDoAfterEvent(Entity target, ref ForceOpenLockDoAfterEvent args) { if (args.Cancelled) return; _lock.Unlock(target, args.User, target.Comp); if (TryComp(target, out var wiresPanel) && TryComp(target, out var bypassLock) && bypassLock.OpenWiresPanel) _wires.TogglePanel(target, wiresPanel, true, args.User); } private void OnGetVerb(Entity target, ref GetVerbsEvent args) { if (!args.CanInteract || !args.CanAccess || args.Using == null) return; var rightTool = _tool.HasQuality(args.Using.Value, target.Comp.BypassingTool); var item = args.Using.Value; var bypassVerb = new InteractionVerb { IconEntity = GetNetEntity(item), }; bypassVerb.Text = bypassVerb.Message = Loc.GetString("bypass-lock-verb"); var ev = new CheckBypassLockVerbRequirements(bypassVerb, rightTool, rightTool, target.Comp.BypassingTool); RaiseLocalEvent(target, ref ev); if (!ev.ShowVerb) return; var user = args.User; bypassVerb.Act = () => TryStartDoAfter(target, user, item); if (!_lock.IsLocked(target.Owner)) { bypassVerb.Disabled = true; bypassVerb.Message = Loc.GetString("bypass-lock-disabled-already-open"); } args.Verbs.Add(bypassVerb); } } /// /// This event gets raised on the entity with the after someone finished /// a doafter forcing the lock open. /// [Serializable, NetSerializable] public sealed partial class ForceOpenLockDoAfterEvent : SimpleDoAfterEvent; /// /// This gets raised on the target whose lock is attempted to be forced open. /// /// Entity attempting to open this. /// Whether the lock can be forced open. [ByRefEvent] public record struct ForceOpenLockAttemptEvent(EntityUid User, bool CanForceOpen = true); /// /// This gets raised on the target that is being right-clicked to check for verb requirements. /// /// The interaction verb that will be shown. /// Whether the tool has the right properties to force the lock open. /// Whether the verb should be shown. /// The required tool quality to force the lock open. [ByRefEvent] public record struct CheckBypassLockVerbRequirements(InteractionVerb Verb, bool RightTool, bool ShowVerb, ProtoId ToolQuality);