Add checks for various complex interactions (#37246)

* Prevent toggling radiation collectors

* Prevent resisting locks

* Prevent unbuckling others, early exit before doafter

* Prevent camera and camera router setup

* Prevent rotating and flipping

* Also prevent adding the unbuckle verb.

* Revert ResistLocker changes

* Prevent emitter tampering

* Also prevent lock verb

* Prevent toggling welders

* Prevent gun open/close bolt, rack and switch mode

* Prevent toggling stun batons
This commit is contained in:
Krunklehorn
2025-06-02 13:06:45 -04:00
committed by GitHub
parent 9167683d10
commit c2b1b81ec8
13 changed files with 85 additions and 16 deletions

View File

@@ -37,7 +37,9 @@ namespace Content.Server.Rotatable
private void AddFlipVerb(EntityUid uid, FlippableComponent component, GetVerbsEvent<Verb> args) private void AddFlipVerb(EntityUid uid, FlippableComponent component, GetVerbsEvent<Verb> args)
{ {
if (!args.CanAccess || !args.CanInteract) if (!args.CanAccess
|| !args.CanInteract
|| !args.CanComplexInteract)
return; return;
// Check if the object is anchored. // Check if the object is anchored.
@@ -60,6 +62,7 @@ namespace Content.Server.Rotatable
{ {
if (!args.CanAccess if (!args.CanAccess
|| !args.CanInteract || !args.CanInteract
|| !args.CanComplexInteract
|| Transform(uid).NoLocalRotation) // Good ol prototype inheritance, eh? || Transform(uid).NoLocalRotation) // Good ol prototype inheritance, eh?
return; return;
@@ -125,7 +128,9 @@ namespace Content.Server.Rotatable
if (!TryComp<RotatableComponent>(entity, out var rotatableComp)) if (!TryComp<RotatableComponent>(entity, out var rotatableComp))
return false; return false;
if (!_actionBlocker.CanInteract(player, entity) || !_interaction.InRangeAndAccessible(player, entity)) if (!_actionBlocker.CanInteract(player, entity)
|| !_actionBlocker.CanComplexInteract(player)
|| !_interaction.InRangeAndAccessible(player, entity))
return false; return false;
// Check if the object is anchored, and whether we are still allowed to rotate it. // Check if the object is anchored, and whether we are still allowed to rotate it.
@@ -148,7 +153,9 @@ namespace Content.Server.Rotatable
if (!TryComp<RotatableComponent>(entity, out var rotatableComp)) if (!TryComp<RotatableComponent>(entity, out var rotatableComp))
return false; return false;
if (!_actionBlocker.CanInteract(player, entity) || !_interaction.InRangeAndAccessible(player, entity)) if (!_actionBlocker.CanInteract(player, entity)
|| !_actionBlocker.CanComplexInteract(player)
|| !_interaction.InRangeAndAccessible(player, entity))
return false; return false;
// Check if the object is anchored, and whether we are still allowed to rotate it. // Check if the object is anchored, and whether we are still allowed to rotate it.
@@ -171,7 +178,9 @@ namespace Content.Server.Rotatable
if (!TryComp<FlippableComponent>(entity, out var flippableComp)) if (!TryComp<FlippableComponent>(entity, out var flippableComp))
return false; return false;
if (!_actionBlocker.CanInteract(player, entity) || !_interaction.InRangeAndAccessible(player, entity)) if (!_actionBlocker.CanInteract(player, entity)
|| !_actionBlocker.CanComplexInteract(player)
|| !_interaction.InRangeAndAccessible(player, entity))
return false; return false;
// Check if the object is anchored. // Check if the object is anchored.

View File

@@ -62,7 +62,7 @@ namespace Content.Server.Singularity.EntitySystems
private void OnActivate(EntityUid uid, EmitterComponent component, ActivateInWorldEvent args) private void OnActivate(EntityUid uid, EmitterComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled) if (args.Handled || !args.Complex)
return; return;
if (TryComp(uid, out LockComponent? lockComp) && lockComp.Locked) if (TryComp(uid, out LockComponent? lockComp) && lockComp.Locked)
@@ -101,7 +101,7 @@ namespace Content.Server.Singularity.EntitySystems
private void OnGetVerb(EntityUid uid, EmitterComponent component, GetVerbsEvent<Verb> args) private void OnGetVerb(EntityUid uid, EmitterComponent component, GetVerbsEvent<Verb> args)
{ {
if (!args.CanAccess || !args.CanInteract || args.Hands == null) if (!args.CanAccess || !args.CanInteract || !args.CanComplexInteract || args.Hands == null)
return; return;
if (TryComp<LockComponent>(uid, out var lockComp) && lockComp.Locked) if (TryComp<LockComponent>(uid, out var lockComp) && lockComp.Locked)

View File

@@ -68,6 +68,9 @@ public sealed class RadiationCollectorSystem : EntitySystem
private void OnActivate(EntityUid uid, RadiationCollectorComponent component, ActivateInWorldEvent args) private void OnActivate(EntityUid uid, RadiationCollectorComponent component, ActivateInWorldEvent args)
{ {
if (!args.Complex)
return;
if (TryComp(uid, out UseDelayComponent? useDelay) && !_useDelay.TryResetDelay((uid, useDelay), true)) if (TryComp(uid, out UseDelayComponent? useDelay) && !_useDelay.TryResetDelay((uid, useDelay), true))
return; return;

View File

@@ -25,7 +25,6 @@ namespace Content.Server.Stunnable.Systems
SubscribeLocalEvent<StunbatonComponent, ExaminedEvent>(OnExamined); SubscribeLocalEvent<StunbatonComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<StunbatonComponent, SolutionContainerChangedEvent>(OnSolutionChange); SubscribeLocalEvent<StunbatonComponent, SolutionContainerChangedEvent>(OnSolutionChange);
SubscribeLocalEvent<StunbatonComponent, StaminaDamageOnHitAttemptEvent>(OnStaminaHitAttempt); SubscribeLocalEvent<StunbatonComponent, StaminaDamageOnHitAttemptEvent>(OnStaminaHitAttempt);
SubscribeLocalEvent<StunbatonComponent, ItemToggleActivateAttemptEvent>(TryTurnOn);
SubscribeLocalEvent<StunbatonComponent, ChargeChangedEvent>(OnChargeChanged); SubscribeLocalEvent<StunbatonComponent, ChargeChangedEvent>(OnChargeChanged);
} }
@@ -52,8 +51,10 @@ namespace Content.Server.Stunnable.Systems
} }
} }
private void TryTurnOn(Entity<StunbatonComponent> entity, ref ItemToggleActivateAttemptEvent args) protected override void TryTurnOn(Entity<StunbatonComponent> entity, ref ItemToggleActivateAttemptEvent args)
{ {
base.TryTurnOn(entity, ref args);
if (!TryComp<BatteryComponent>(entity, out var battery) || battery.CurrentCharge < entity.Comp.EnergyPerUse) if (!TryComp<BatteryComponent>(entity, out var battery) || battery.CurrentCharge < entity.Comp.EnergyPerUse)
{ {
args.Cancelled = true; args.Cancelled = true;

View File

@@ -92,7 +92,7 @@ public sealed class SurveillanceCameraRouterSystem : EntitySystem
private void AddVerbs(EntityUid uid, SurveillanceCameraRouterComponent component, GetVerbsEvent<AlternativeVerb> verbs) private void AddVerbs(EntityUid uid, SurveillanceCameraRouterComponent component, GetVerbsEvent<AlternativeVerb> verbs)
{ {
if (!_actionBlocker.CanInteract(verbs.User, uid)) if (!_actionBlocker.CanInteract(verbs.User, uid) || !_actionBlocker.CanComplexInteract(verbs.User))
{ {
return; return;
} }

View File

@@ -129,7 +129,7 @@ public sealed class SurveillanceCameraSystem : EntitySystem
private void AddVerbs(EntityUid uid, SurveillanceCameraComponent component, GetVerbsEvent<AlternativeVerb> verbs) private void AddVerbs(EntityUid uid, SurveillanceCameraComponent component, GetVerbsEvent<AlternativeVerb> verbs)
{ {
if (!_actionBlocker.CanInteract(verbs.User, uid)) if (!_actionBlocker.CanInteract(verbs.User, uid) || !_actionBlocker.CanComplexInteract(verbs.User))
{ {
return; return;
} }

View File

@@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Numerics; using System.Numerics;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.ActionBlocker;
using Content.Shared.Buckle.Components; using Content.Shared.Buckle.Components;
using Content.Shared.Cuffs.Components; using Content.Shared.Cuffs.Components;
using Content.Shared.Database; using Content.Shared.Database;
@@ -32,6 +33,7 @@ public abstract partial class SharedBuckleSystem
public static ProtoId<AlertCategoryPrototype> BuckledAlertCategory = "Buckled"; public static ProtoId<AlertCategoryPrototype> BuckledAlertCategory = "Buckled";
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
private void InitializeBuckle() private void InitializeBuckle()
{ {
@@ -516,9 +518,15 @@ public abstract partial class SharedBuckleSystem
if (_gameTiming.CurTime < buckle.Comp.BuckleTime + buckle.Comp.Delay) if (_gameTiming.CurTime < buckle.Comp.BuckleTime + buckle.Comp.Delay)
return false; return false;
if (user != null && !_interaction.InRangeUnobstructed(user.Value, strap.Owner, buckle.Comp.Range, popup: popup)) if (user != null)
{
if (!_interaction.InRangeUnobstructed(user.Value, strap.Owner, buckle.Comp.Range, popup: popup))
return false; return false;
if (user.Value != buckle.Owner && !_actionBlocker.CanComplexInteract(user.Value))
return false;
}
var unbuckleAttempt = new UnbuckleAttemptEvent(strap, buckle!, user, popup); var unbuckleAttempt = new UnbuckleAttemptEvent(strap, buckle!, user, popup);
RaiseLocalEvent(buckle, ref unbuckleAttempt); RaiseLocalEvent(buckle, ref unbuckleAttempt);
if (unbuckleAttempt.Cancelled) if (unbuckleAttempt.Cancelled)

View File

@@ -43,6 +43,10 @@ public abstract partial class SharedBuckleSystem
} }
else else
{ {
if (!TryComp(args.Dragged, out BuckleComponent? buckle) ||
!CanBuckle(args.Dragged, args.User, uid, true, out var _, buckle))
return;
var doAfterArgs = new DoAfterArgs(EntityManager, args.User, component.BuckleDoafterTime, new BuckleDoAfterEvent(), args.Dragged, args.Dragged, uid) var doAfterArgs = new DoAfterArgs(EntityManager, args.User, component.BuckleDoafterTime, new BuckleDoAfterEvent(), args.Dragged, args.Dragged, uid)
{ {
BreakOnMove = true, BreakOnMove = true,
@@ -200,6 +204,9 @@ public abstract partial class SharedBuckleSystem
if (!args.CanAccess || !args.CanInteract || !component.Buckled) if (!args.CanAccess || !args.CanInteract || !component.Buckled)
return; return;
if (!CanUnbuckle((uid, component), args.User, false))
return;
InteractionVerb verb = new() InteractionVerb verb = new()
{ {
Act = () => TryUnbuckle(uid, args.User, buckleComp: component), Act = () => TryUnbuckle(uid, args.User, buckleComp: component),

View File

@@ -282,7 +282,7 @@ public sealed class LockSystem : EntitySystem
private void AddToggleLockVerb(EntityUid uid, LockComponent component, GetVerbsEvent<AlternativeVerb> args) private void AddToggleLockVerb(EntityUid uid, LockComponent component, GetVerbsEvent<AlternativeVerb> args)
{ {
if (!args.CanAccess || !args.CanInteract) if (!args.CanAccess || !args.CanInteract || !args.CanComplexInteract)
return; return;
AlternativeVerb verb = new() AlternativeVerb verb = new()

View File

@@ -1,9 +1,33 @@
using Content.Shared.ActionBlocker;
using Content.Shared.Item.ItemToggle.Components;
namespace Content.Shared.Stunnable; namespace Content.Shared.Stunnable;
public abstract class SharedStunbatonSystem : EntitySystem public abstract class SharedStunbatonSystem : EntitySystem
{ {
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<StunbatonComponent, ItemToggleActivateAttemptEvent>(TryTurnOn);
SubscribeLocalEvent<StunbatonComponent, ItemToggleDeactivateAttemptEvent>(TryTurnOff);
}
protected virtual void TryTurnOn(Entity<StunbatonComponent> entity, ref ItemToggleActivateAttemptEvent args)
{
if (args.User != null && !_actionBlocker.CanComplexInteract(args.User.Value)) {
args.Cancelled = true;
return;
}
}
protected virtual void TryTurnOff(Entity<StunbatonComponent> entity, ref ItemToggleDeactivateAttemptEvent args)
{
if (args.User != null && !_actionBlocker.CanComplexInteract(args.User.Value)) {
args.Cancelled = true;
return;
}
} }
} }

View File

@@ -1,3 +1,4 @@
using Content.Shared.ActionBlocker;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Database; using Content.Shared.Database;
@@ -12,6 +13,8 @@ namespace Content.Shared.Tools.Systems;
public abstract partial class SharedToolSystem public abstract partial class SharedToolSystem
{ {
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
public void InitializeWelder() public void InitializeWelder()
{ {
SubscribeLocalEvent<WelderComponent, ExaminedEvent>(OnWelderExamine); SubscribeLocalEvent<WelderComponent, ExaminedEvent>(OnWelderExamine);
@@ -27,6 +30,7 @@ public abstract partial class SharedToolSystem
SubscribeLocalEvent<WelderComponent, ItemToggledEvent>(OnToggle); SubscribeLocalEvent<WelderComponent, ItemToggledEvent>(OnToggle);
SubscribeLocalEvent<WelderComponent, ItemToggleActivateAttemptEvent>(OnActivateAttempt); SubscribeLocalEvent<WelderComponent, ItemToggleActivateAttemptEvent>(OnActivateAttempt);
SubscribeLocalEvent<WelderComponent, ItemToggleDeactivateAttemptEvent>(OnDeactivateAttempt);
} }
public void TurnOn(Entity<WelderComponent> entity, EntityUid? user) public void TurnOn(Entity<WelderComponent> entity, EntityUid? user)
@@ -165,6 +169,11 @@ public abstract partial class SharedToolSystem
private void OnActivateAttempt(Entity<WelderComponent> entity, ref ItemToggleActivateAttemptEvent args) private void OnActivateAttempt(Entity<WelderComponent> entity, ref ItemToggleActivateAttemptEvent args)
{ {
if (args.User != null && !_actionBlocker.CanComplexInteract(args.User.Value)) {
args.Cancelled = true;
return;
}
if (!SolutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.FuelSolutionName, out _, out var solution)) if (!SolutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.FuelSolutionName, out _, out var solution))
{ {
args.Cancelled = true; args.Cancelled = true;
@@ -179,4 +188,12 @@ public abstract partial class SharedToolSystem
args.Cancelled = true; args.Cancelled = true;
} }
} }
private void OnDeactivateAttempt(Entity<WelderComponent> entity, ref ItemToggleDeactivateAttemptEvent args)
{
if (args.User != null && !_actionBlocker.CanComplexInteract(args.User.Value)) {
args.Cancelled = true;
return;
}
}
} }

View File

@@ -78,7 +78,7 @@ public abstract partial class SharedGunSystem
/// </summary> /// </summary>
private void OnChamberActivationVerb(EntityUid uid, ChamberMagazineAmmoProviderComponent component, GetVerbsEvent<ActivationVerb> args) private void OnChamberActivationVerb(EntityUid uid, ChamberMagazineAmmoProviderComponent component, GetVerbsEvent<ActivationVerb> args)
{ {
if (!args.CanAccess || !args.CanInteract || component.BoltClosed == null || !component.CanRack) if (!args.CanAccess || !args.CanInteract || !args.CanComplexInteract || args.Hands == null || component.BoltClosed == null || !component.CanRack)
return; return;
args.Verbs.Add(new ActivationVerb() args.Verbs.Add(new ActivationVerb()
@@ -131,7 +131,7 @@ public abstract partial class SharedGunSystem
/// </summary> /// </summary>
private void OnChamberInteractionVerb(EntityUid uid, ChamberMagazineAmmoProviderComponent component, GetVerbsEvent<InteractionVerb> args) private void OnChamberInteractionVerb(EntityUid uid, ChamberMagazineAmmoProviderComponent component, GetVerbsEvent<InteractionVerb> args)
{ {
if (!args.CanAccess || !args.CanInteract || component.BoltClosed == null) if (!args.CanAccess || !args.CanInteract || !args.CanComplexInteract || args.Hands == null || component.BoltClosed == null)
return; return;
args.Verbs.Add(new InteractionVerb() args.Verbs.Add(new InteractionVerb()

View File

@@ -30,7 +30,7 @@ public abstract partial class SharedGunSystem
private void OnAltVerb(EntityUid uid, GunComponent component, GetVerbsEvent<AlternativeVerb> args) private void OnAltVerb(EntityUid uid, GunComponent component, GetVerbsEvent<AlternativeVerb> args)
{ {
if (!args.CanAccess || !args.CanInteract || component.SelectedMode == component.AvailableModes) if (!args.CanAccess || !args.CanInteract || !args.CanComplexInteract || args.Hands == null || component.SelectedMode == component.AvailableModes)
return; return;
var nextMode = GetNextMode(component); var nextMode = GetNextMode(component);