diff --git a/Content.Shared/Cuffs/Components/HandcuffComponent.cs b/Content.Shared/Cuffs/Components/HandcuffComponent.cs
index 77a77cf2f8..30577da064 100644
--- a/Content.Shared/Cuffs/Components/HandcuffComponent.cs
+++ b/Content.Shared/Cuffs/Components/HandcuffComponent.cs
@@ -52,6 +52,14 @@ public sealed partial class HandcuffComponent : Component
[DataField]
public bool Removing;
+ ///
+ /// Whether the cuffs are currently being used to cuff someone.
+ /// We need the extra information for when the virtual item is deleted because that can happen when you simply stop
+ /// pulling them on the ground.
+ ///
+ [DataField]
+ public bool Used;
+
///
/// The path of the RSI file used for the player cuffed overlay.
///
@@ -87,7 +95,7 @@ public sealed partial class HandcuffComponent : Component
}
///
-/// Event fired on the User when the User attempts to cuff the Target.
+/// Event fired on the User when the User attempts to uncuff the Target.
/// Should generate popups on the User.
///
[ByRefEvent]
diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs
index ca903719c4..0077f5a358 100644
--- a/Content.Shared/Cuffs/SharedCuffableSystem.cs
+++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs
@@ -3,12 +3,10 @@ using Content.Shared.ActionBlocker;
using Content.Shared.Administration.Components;
using Content.Shared.Administration.Logs;
using Content.Shared.Alert;
-using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Buckle.Components;
using Content.Shared.Cuffs.Components;
using Content.Shared.Database;
using Content.Shared.DoAfter;
-using Content.Shared.Effects;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
@@ -19,7 +17,6 @@ using Content.Shared.Interaction.Events;
using Content.Shared.Inventory.Events;
using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Item;
-using Content.Shared.Mobs.Systems;
using Content.Shared.Movement.Events;
using Content.Shared.Movement.Pulling.Events;
using Content.Shared.Popups;
@@ -29,12 +26,12 @@ using Content.Shared.Stunnable;
using Content.Shared.Timing;
using Content.Shared.Verbs;
using Content.Shared.Weapons.Melee.Events;
-using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Serialization;
+using Robust.Shared.Utility;
using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent;
namespace Content.Shared.Cuffs
@@ -47,8 +44,6 @@ namespace Content.Shared.Cuffs
[Dependency] private readonly ISharedAdminLogManager _adminLog = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly AlertsSystem _alerts = default!;
- [Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
- [Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
@@ -95,9 +90,8 @@ namespace Content.Shared.Cuffs
private void OnUncuffAttempt(ref UncuffAttemptEvent args)
{
if (args.Cancelled)
- {
return;
- }
+
if (!Exists(args.User) || Deleted(args.User))
{
// Should this even be possible?
@@ -109,23 +103,29 @@ namespace Content.Shared.Cuffs
// This is because the CanInteract blocking of the cuffs prevents self-uncuff.
if (args.User == args.Target)
{
- // This UncuffAttemptEvent check should probably be In MobStateSystem, not here?
- if (_mobState.IsIncapacitated(args.User))
+ if (!TryComp(args.User, out var cuffable))
{
+ DebugTools.Assert($"{args.User} tried to uncuff themselves but they are not cuffable.");
+ return;
+ }
+
+ // We temporarily allow interactions so the cuffable system does not block itself.
+ // It's assumed that this will always be false.
+ // Otherwise they would not be trying to uncuff themselves.
+ cuffable.CanStillInteract = true;
+ Dirty(args.User, cuffable);
+
+ if (!_actionBlocker.CanInteract(args.User, args.User))
args.Cancelled = true;
- }
- else
- {
- // TODO Find a way for cuffable to check ActionBlockerSystem.CanInteract() without blocking itself
- }
+
+ cuffable.CanStillInteract = false;
+ Dirty(args.User, cuffable);
}
else
{
// Check if the user can interact.
if (!_actionBlocker.CanInteract(args.User, args.Target))
- {
args.Cancelled = true;
- }
}
if (args.Cancelled)
@@ -310,6 +310,7 @@ namespace Content.Shared.Cuffs
if (!args.Cancelled && TryAddNewCuffs(target, user, uid, cuffable))
{
+ component.Used = true;
_audio.PlayPredicted(component.EndCuffSound, uid, user);
_popup.PopupEntity(Loc.GetString("handcuff-component-cuff-observer-success-message",
@@ -613,7 +614,7 @@ namespace Content.Shared.Cuffs
if (!Resolve(target, ref cuffable) || !Resolve(cuffsToRemove, ref cuff))
return;
- if (cuff.Removing || TerminatingOrDeleted(cuffsToRemove) || TerminatingOrDeleted(target))
+ if (!cuff.Used || cuff.Removing || TerminatingOrDeleted(cuffsToRemove) || TerminatingOrDeleted(target))
return;
if (user != null)
@@ -625,10 +626,9 @@ namespace Content.Shared.Cuffs
}
cuff.Removing = true;
+ cuff.Used = false;
_audio.PlayPredicted(cuff.EndUncuffSound, target, user);
- var isOwner = user == target;
-
_container.Remove(cuffsToRemove, cuffable.Container);
if (_net.IsServer)
@@ -644,43 +644,42 @@ namespace Content.Shared.Cuffs
{
_hands.PickupOrDrop(user, cuffsToRemove);
}
+ }
- // Only play popups on server because popups suck
- if (cuffable.CuffedHandCount == 0)
+ if (cuffable.CuffedHandCount == 0)
+ {
+ if (user != null)
+ _popup.PopupPredicted(Loc.GetString("cuffable-component-remove-cuffs-success-message"), user.Value, user.Value);
+
+ if (target != user && user != null)
{
- if (user != null)
- _popup.PopupEntity(Loc.GetString("cuffable-component-remove-cuffs-success-message"), user.Value, user.Value);
-
- if (target != user && user != null)
- {
- _popup.PopupEntity(Loc.GetString("cuffable-component-remove-cuffs-by-other-success-message",
- ("otherName", Identity.Name(user.Value, EntityManager, user))), target, target);
- _adminLog.Add(LogType.Action, LogImpact.Medium,
- $"{ToPrettyString(user):player} has successfully uncuffed {ToPrettyString(target):player}");
- }
- else
- {
- _adminLog.Add(LogType.Action, LogImpact.Medium,
- $"{ToPrettyString(user):player} has successfully uncuffed themselves");
- }
+ _popup.PopupPredicted(Loc.GetString("cuffable-component-remove-cuffs-by-other-success-message",
+ ("otherName", Identity.Name(user.Value, EntityManager, user))), target, target);
+ _adminLog.Add(LogType.Action, LogImpact.Medium,
+ $"{ToPrettyString(user):player} has successfully uncuffed {ToPrettyString(target):player}");
}
- else if (user != null)
+ else
{
- if (user != target)
- {
- _popup.PopupEntity(Loc.GetString("cuffable-component-remove-cuffs-partial-success-message",
- ("cuffedHandCount", cuffable.CuffedHandCount),
- ("otherName", Identity.Name(user.Value, EntityManager, user.Value))), user.Value, user.Value);
- _popup.PopupEntity(Loc.GetString(
- "cuffable-component-remove-cuffs-by-other-partial-success-message",
- ("otherName", Identity.Name(user.Value, EntityManager, user.Value)),
- ("cuffedHandCount", cuffable.CuffedHandCount)), target, target);
- }
- else
- {
- _popup.PopupEntity(Loc.GetString("cuffable-component-remove-cuffs-partial-success-message",
- ("cuffedHandCount", cuffable.CuffedHandCount)), user.Value, user.Value);
- }
+ _adminLog.Add(LogType.Action, LogImpact.Medium,
+ $"{ToPrettyString(user):player} has successfully uncuffed themselves");
+ }
+ }
+ else if (user != null)
+ {
+ if (user != target)
+ {
+ _popup.PopupPredicted(Loc.GetString("cuffable-component-remove-cuffs-partial-success-message",
+ ("cuffedHandCount", cuffable.CuffedHandCount),
+ ("otherName", Identity.Name(user.Value, EntityManager, user.Value))), user.Value, user.Value);
+ _popup.PopupPredicted(Loc.GetString(
+ "cuffable-component-remove-cuffs-by-other-partial-success-message",
+ ("otherName", Identity.Name(user.Value, EntityManager, user.Value)),
+ ("cuffedHandCount", cuffable.CuffedHandCount)), target, target);
+ }
+ else
+ {
+ _popup.PopupPredicted(Loc.GetString("cuffable-component-remove-cuffs-partial-success-message",
+ ("cuffedHandCount", cuffable.CuffedHandCount)), user.Value, user.Value);
}
}
cuff.Removing = false;