diff --git a/Content.Client/DragDrop/DragDropSystem.cs b/Content.Client/DragDrop/DragDropSystem.cs index 7c772ff698..1bf2dac727 100644 --- a/Content.Client/DragDrop/DragDropSystem.cs +++ b/Content.Client/DragDrop/DragDropSystem.cs @@ -5,6 +5,7 @@ using Content.Client.Viewport; using Content.Shared.ActionBlocker; using Content.Shared.DragDrop; using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Helpers; using Content.Shared.Popups; using JetBrains.Annotations; @@ -428,11 +429,18 @@ namespace Content.Client.DragDrop /// null if the target doesn't support IDragDropOn private bool? ValidDragDrop(DragDropEvent eventArgs) { - if (!_actionBlockerSystem.CanInteract(eventArgs.User)) + if (!_actionBlockerSystem.CanInteract(eventArgs.User, eventArgs.Target)) { return false; } + // CanInteract() doesn't support checking a second "target" entity. + // Doing so manually: + var ev = new GettingInteractedWithAttemptEvent(eventArgs.User, eventArgs.Dragged); + RaiseLocalEvent(eventArgs.Dragged, ev); + if (ev.Cancelled) + return false; + var valid = CheckDragDropOn(eventArgs); foreach (var comp in EntityManager.GetComponents(eventArgs.Target)) diff --git a/Content.Server/AME/Components/AMEControllerComponent.cs b/Content.Server/AME/Components/AMEControllerComponent.cs index 7e8b5035c0..2b567848e5 100644 --- a/Content.Server/AME/Components/AMEControllerComponent.cs +++ b/Content.Server/AME/Components/AMEControllerComponent.cs @@ -172,11 +172,6 @@ namespace Content.Server.AME.Components if (playerEntity == default) return false; - var actionBlocker = EntitySystem.Get(); - - //Check if player can interact in their current state - if (!actionBlocker.CanInteract(playerEntity) || !actionBlocker.CanUse(playerEntity)) - return false; //Check if device is powered if (needsPower && !Powered) return false; diff --git a/Content.Server/Actions/Spells/GiveItemSpell.cs b/Content.Server/Actions/Spells/GiveItemSpell.cs index cd916e7a90..58fa0d8bf0 100644 --- a/Content.Server/Actions/Spells/GiveItemSpell.cs +++ b/Content.Server/Actions/Spells/GiveItemSpell.cs @@ -45,7 +45,7 @@ namespace Content.Server.Actions.Spells return; } - if (!EntitySystem.Get().CanInteract(caster)) return; + if (!EntitySystem.Get().CanInteract(caster, null)) return; // TODO: Nix when we get EntityPrototype serializers if (!IoCManager.Resolve().HasIndex(ItemProto)) diff --git a/Content.Server/Alert/Click/StopBeingPulled.cs b/Content.Server/Alert/Click/StopBeingPulled.cs index 4046b324bd..56bcd0ce3d 100644 --- a/Content.Server/Alert/Click/StopBeingPulled.cs +++ b/Content.Server/Alert/Click/StopBeingPulled.cs @@ -18,7 +18,7 @@ namespace Content.Server.Alert.Click { public void AlertClicked(EntityUid player) { - if (!EntitySystem.Get().CanInteract(player)) + if (!EntitySystem.Get().CanInteract(player, null)) return; if (IoCManager.Resolve().TryGetComponent(player, out var playerPullable)) diff --git a/Content.Server/Arcade/Components/BlockGameArcadeComponent.cs b/Content.Server/Arcade/Components/BlockGameArcadeComponent.cs index c6f35c94fe..4593aa6240 100644 --- a/Content.Server/Arcade/Components/BlockGameArcadeComponent.cs +++ b/Content.Server/Arcade/Components/BlockGameArcadeComponent.cs @@ -49,9 +49,6 @@ namespace Content.Server.Arcade.Components if(!Powered || !IoCManager.Resolve().TryGetComponent(eventArgs.User, out ActorComponent? actor)) return; - if(!EntitySystem.Get().CanInteract(eventArgs.User)) - return; - UserInterface?.Toggle(actor.PlayerSession); if (UserInterface?.SessionHasOpen(actor.PlayerSession) == true) { @@ -130,13 +127,6 @@ namespace Content.Server.Arcade.Components case BlockGameMessages.BlockGamePlayerActionMessage playerActionMessage: if (obj.Session != _player) break; - // TODO: Should this check if the Owner can interact...? - if (!EntitySystem.Get().CanInteract(Owner)) - { - DeactivePlayer(obj.Session); - break; - } - if (playerActionMessage.PlayerAction == BlockGamePlayerAction.NewGame) { if(_game?.Started == true) _game = new BlockGame(this); diff --git a/Content.Server/Arcade/Components/SpaceVillainArcadeComponent.cs b/Content.Server/Arcade/Components/SpaceVillainArcadeComponent.cs index ab2d174d3c..07d3ead6a2 100644 --- a/Content.Server/Arcade/Components/SpaceVillainArcadeComponent.cs +++ b/Content.Server/Arcade/Components/SpaceVillainArcadeComponent.cs @@ -77,9 +77,6 @@ namespace Content.Server.Arcade.Components if (!Powered || !IoCManager.Resolve().TryGetComponent(eventArgs.User, out ActorComponent? actor)) return; - if (!EntitySystem.Get().CanInteract(eventArgs.User)) - return; - _game ??= new SpaceVillainGame(this); if (_entityManager.TryGetComponent(Owner, out var wiresComponent) && wiresComponent.IsPanelOpen) diff --git a/Content.Server/Atmos/Components/GasTankComponent.cs b/Content.Server/Atmos/Components/GasTankComponent.cs index 1a8c10e7aa..aa6c046d8c 100644 --- a/Content.Server/Atmos/Components/GasTankComponent.cs +++ b/Content.Server/Atmos/Components/GasTankComponent.cs @@ -207,11 +207,6 @@ namespace Content.Server.Atmos.Components internal void ToggleInternals() { - var user = GetInternalsComponent()?.Owner; - - if (user == null || !EntitySystem.Get().CanUse(user.Value)) - return; - if (IsConnected) { DisconnectFromInternals(); @@ -321,6 +316,9 @@ namespace Content.Server.Atmos.Components { public bool DoToggleAction(ToggleItemActionEventArgs args) { + if (!EntitySystem.Get().CanInteract(args.Performer, args.Item)) + return false; + if (!IoCManager.Resolve().TryGetComponent(args.Item, out var gasTankComponent)) return false; // no change if (gasTankComponent.IsConnected == args.ToggledOn) return false; diff --git a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs index 8262946b8a..d657b0d081 100644 --- a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs +++ b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs @@ -181,7 +181,7 @@ namespace Content.Server.Atmos.EntitySystems if (!Resolve(uid, ref flammable)) return; - if (!flammable.OnFire || !_actionBlockerSystem.CanInteract(flammable.Owner) || flammable.Resisting) + if (!flammable.OnFire || !_actionBlockerSystem.CanInteract(flammable.Owner, null) || flammable.Resisting) return; flammable.Resisting = true; diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs index dd31411b1f..cec4cdab22 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs @@ -18,10 +18,8 @@ using Robust.Shared.Player; namespace Content.Server.Atmos.Piping.Binary.EntitySystems { [UsedImplicitly] - public class GasValveSystem : EntitySystem + public sealed class GasValveSystem : EntitySystem { - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; - public override void Initialize() { base.Initialize(); @@ -51,11 +49,8 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems private void OnActivate(EntityUid uid, GasValveComponent component, ActivateInWorldEvent args) { - if (args.User.InRangeUnobstructed(args.Target) && _actionBlockerSystem.CanInteract(args.User)) - { - Toggle(uid, component); - SoundSystem.Play(Filter.Pvs(component.Owner), component.ValveSound.GetSound(), component.Owner, AudioHelpers.WithVariation(0.25f)); - } + Toggle(uid, component); + SoundSystem.Play(Filter.Pvs(component.Owner), component.ValveSound.GetSound(), component.Owner, AudioHelpers.WithVariation(0.25f)); } public void Set(EntityUid uid, GasValveComponent component, bool value) diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs index 7f0352547f..87144b4c55 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs @@ -25,11 +25,10 @@ using Robust.Shared.Players; namespace Content.Server.Atmos.Piping.Unary.EntitySystems { [UsedImplicitly] - public class GasCanisterSystem : EntitySystem + public sealed class GasCanisterSystem : EntitySystem { [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly AdminLogSystem _adminLogSystem = default!; public override void Initialize() diff --git a/Content.Server/Botany/Components/PlantHolderComponent.cs b/Content.Server/Botany/Components/PlantHolderComponent.cs index 720a545577..6443aefd77 100644 --- a/Content.Server/Botany/Components/PlantHolderComponent.cs +++ b/Content.Server/Botany/Components/PlantHolderComponent.cs @@ -422,7 +422,7 @@ namespace Content.Server.Botany.Components public bool DoHarvest(EntityUid user) { - if (Seed == null || _entMan.Deleted(user) || !EntitySystem.Get().CanInteract(user)) + if (Seed == null || _entMan.Deleted(user)) return false; var botanySystem = EntitySystem.Get(); @@ -657,7 +657,7 @@ namespace Content.Server.Botany.Components var user = eventArgs.User; var usingItem = eventArgs.Using; - if ((!_entMan.EntityExists(usingItem) ? EntityLifeStage.Deleted : _entMan.GetComponent(usingItem).EntityLifeStage) >= EntityLifeStage.Deleted || !EntitySystem.Get().CanInteract(user)) + if ((!_entMan.EntityExists(usingItem) ? EntityLifeStage.Deleted : _entMan.GetComponent(usingItem).EntityLifeStage) >= EntityLifeStage.Deleted) return false; var botanySystem = EntitySystem.Get(); diff --git a/Content.Server/Buckle/Components/BuckleComponent.cs b/Content.Server/Buckle/Components/BuckleComponent.cs index 02169ab846..6f720460fe 100644 --- a/Content.Server/Buckle/Components/BuckleComponent.cs +++ b/Content.Server/Buckle/Components/BuckleComponent.cs @@ -138,12 +138,6 @@ namespace Content.Server.Buckle.Components return false; } - if (!EntitySystem.Get().CanInteract(user)) - { - popupSystem.PopupEntity(Loc.GetString("buckle-component-cannot-do-that-message"), user, Filter.Entities(user)); - return false; - } - if (!_entMan.TryGetComponent(to, out strap)) { return false; @@ -295,13 +289,6 @@ namespace Content.Server.Buckle.Components return false; } - if (!EntitySystem.Get().CanInteract(user)) - { - var popupSystem = EntitySystem.Get(); - popupSystem.PopupEntity(Loc.GetString("buckle-component-cannot-do-that-message"), user, Filter.Entities(user)); - return false; - } - if (!user.InRangeUnobstructed(oldBuckledTo.Owner, Range, popup: true)) { return false; diff --git a/Content.Server/Chemistry/Components/ChemMasterComponent.cs b/Content.Server/Chemistry/Components/ChemMasterComponent.cs index 0484479817..e772cf4514 100644 --- a/Content.Server/Chemistry/Components/ChemMasterComponent.cs +++ b/Content.Server/Chemistry/Components/ChemMasterComponent.cs @@ -162,11 +162,6 @@ namespace Content.Server.Chemistry.Components if (playerEntity == default) return false; - var actionBlocker = EntitySystem.Get(); - - //Check if player can interact in their current state - if (!actionBlocker.CanInteract(playerEntity) || !actionBlocker.CanUse(playerEntity)) - return false; //Check if device is powered if (needsPower && !Powered) return false; diff --git a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs index ac9b846da5..bb1538cd6e 100644 --- a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs +++ b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs @@ -207,11 +207,6 @@ namespace Content.Server.Chemistry.Components if (playerEntity == null) return false; - var actionBlocker = EntitySystem.Get(); - - //Check if player can interact in their current state - if (!actionBlocker.CanInteract(playerEntity.Value) || !actionBlocker.CanUse(playerEntity.Value)) - return false; //Check if device is powered if (needsPower && !Powered) return false; diff --git a/Content.Server/Climbing/Components/ClimbableComponent.cs b/Content.Server/Climbing/Components/ClimbableComponent.cs index df8a23d782..a5a6f6397a 100644 --- a/Content.Server/Climbing/Components/ClimbableComponent.cs +++ b/Content.Server/Climbing/Components/ClimbableComponent.cs @@ -6,6 +6,7 @@ using Content.Shared.Body.Components; using Content.Shared.Body.Part; using Content.Shared.Climbing; using Content.Shared.DragDrop; +using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Helpers; using Content.Shared.Popups; using Robust.Shared.GameObjects; @@ -70,7 +71,7 @@ namespace Content.Server.Climbing.Components /// private bool CanVault(EntityUid user, EntityUid target, out string reason) { - if (!EntitySystem.Get().CanInteract(user)) + if (!EntitySystem.Get().CanInteract(user, target)) { reason = Loc.GetString("comp-climbable-cant-interact"); return false; @@ -110,7 +111,17 @@ namespace Content.Server.Climbing.Components /// private bool CanVault(EntityUid user, EntityUid dragged, EntityUid target, out string reason) { - if (!EntitySystem.Get().CanInteract(user)) + if (!EntitySystem.Get().CanInteract(user, dragged)) + { + reason = Loc.GetString("comp-climbable-cant-interact"); + return false; + } + + // CanInteract() doesn't support checking a second "target" entity. + // Doing so manually: + var ev = new GettingInteractedWithAttemptEvent(user, target); + _entities.EventBus.RaiseLocalEvent(target, ev); + if (ev.Cancelled) { reason = Loc.GetString("comp-climbable-cant-interact"); return false; diff --git a/Content.Server/Construction/ConstructionSystem.Initial.cs b/Content.Server/Construction/ConstructionSystem.Initial.cs index 093f132c91..00af70c522 100644 --- a/Content.Server/Construction/ConstructionSystem.Initial.cs +++ b/Content.Server/Construction/ConstructionSystem.Initial.cs @@ -301,7 +301,7 @@ namespace Content.Server.Construction var pathFind = constructionGraph.Path(startNode.Name, targetNode.Name); if (args.SenderSession.AttachedEntity is not {Valid: true} user || - !Get().CanInteract(user)) return; + !Get().CanInteract(user, null)) return; if (!EntityManager.TryGetComponent(user, out HandsComponent? hands)) return; @@ -399,7 +399,7 @@ namespace Content.Server.Construction _beingBuilt[args.SenderSession].Remove(ev.Ack); } - if (!Get().CanInteract(user) + if (!Get().CanInteract(user, null) || !EntityManager.TryGetComponent(user, out HandsComponent? hands) || hands.GetActiveHandItem == null || !user.InRangeUnobstructed(ev.Location, ignoreInsideBlocker:constructionPrototype.CanBuildInImpassable)) { diff --git a/Content.Server/Cuffs/Components/HandcuffComponent.cs b/Content.Server/Cuffs/Components/HandcuffComponent.cs index dbc07472e9..51637946d6 100644 --- a/Content.Server/Cuffs/Components/HandcuffComponent.cs +++ b/Content.Server/Cuffs/Components/HandcuffComponent.cs @@ -149,7 +149,6 @@ namespace Content.Server.Cuffs.Components if (_cuffing) return true; if (eventArgs.Target is not {Valid: true} target || - !EntitySystem.Get().CanUse(eventArgs.User) || !_entities.TryGetComponent(eventArgs.Target.Value, out var cuffed)) { return false; diff --git a/Content.Server/Cuffs/CuffableSystem.cs b/Content.Server/Cuffs/CuffableSystem.cs index 4ad2953664..4953e09747 100644 --- a/Content.Server/Cuffs/CuffableSystem.cs +++ b/Content.Server/Cuffs/CuffableSystem.cs @@ -83,7 +83,7 @@ namespace Content.Server.Cuffs else { // Check if the user can interact. - if (!_actionBlockerSystem.CanInteract(args.User)) + if (!_actionBlockerSystem.CanInteract(args.User, args.Target)) { args.Cancel(); } diff --git a/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs index 88fd0b8166..5fc44224d7 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs +++ b/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs @@ -81,7 +81,7 @@ namespace Content.Server.Disposal.Tube.Components var msg = (UiActionMessage) obj.Message; - if (!PlayerCanUseDisposalTagger(obj.Session)) + if (!Anchored) return; //Check for correct message and ignore maleformed strings @@ -96,29 +96,6 @@ namespace Content.Server.Disposal.Tube.Components } } - /// - /// Checks whether the player entity is able to use the configuration interface of the pipe tagger. - /// - /// The player session. - /// Returns true if the entity can use the configuration interface, and false if it cannot. - private bool PlayerCanUseDisposalTagger(IPlayerSession session) - { - //Need player entity to check if they are still able to use the configuration interface - if (session.AttachedEntity is not {} attached) - return false; - if (!Anchored) - return false; - - var actionBlocker = EntitySystem.Get(); - var groupController = IoCManager.Resolve(); - //Check if player can interact in their current state - if (!groupController.CanAdminMenu(session) && (!actionBlocker.CanInteract(attached) || !actionBlocker.CanUse(attached))) - return false; - - return true; - } - - /// /// Gets component data to be used to update the user interface client-side. /// diff --git a/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs index 8dbc160e4c..8f616217a3 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs +++ b/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs @@ -67,7 +67,7 @@ namespace Content.Server.Disposal.Tube.Components { var msg = (UiActionMessage) obj.Message; - if (!PlayerCanUseDisposalTagger(obj.Session)) + if (!Anchored) return; //Check for correct message and ignore maleformed strings @@ -78,28 +78,6 @@ namespace Content.Server.Disposal.Tube.Components } } - /// - /// Checks whether the player entity is able to use the configuration interface of the pipe tagger. - /// - /// The player entity. - /// Returns true if the entity can use the configuration interface, and false if it cannot. - private bool PlayerCanUseDisposalTagger(IPlayerSession session) - { - //Need player entity to check if they are still able to use the configuration interface - if (session.AttachedEntity is not {} attached) - return false; - if (!Anchored) - return false; - - var actionBlocker = EntitySystem.Get(); - var groupController = IoCManager.Resolve(); - //Check if player can interact in their current state - if (!groupController.CanAdminMenu(session) && (!actionBlocker.CanInteract(attached) || !actionBlocker.CanUse(attached))) - return false; - - return true; - } - /// /// Gets component data to be used to update the user interface client-side. /// diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index 3a305477cc..e85253ae0d 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -184,11 +184,6 @@ namespace Content.Server.Disposal.Unit.EntitySystems return; } - if (!_actionBlockerSystem.CanInteract(player) || !_actionBlockerSystem.CanUse(player)) - { - return; - } - switch (args.Button) { case SharedDisposalUnitComponent.UiButton.Eject: @@ -241,11 +236,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems } args.Handled = true; - - if (IsValidInteraction(args)) - { - component.Owner.GetUIOrNull(SharedDisposalUnitComponent.DisposalUnitUiKey.Key)?.Open(actor.PlayerSession); - } + component.Owner.GetUIOrNull(SharedDisposalUnitComponent.DisposalUnitUiKey.Key)?.Open(actor.PlayerSession); } private void HandleAfterInteractUsing(EntityUid uid, DisposalUnitComponent component, AfterInteractUsingEvent args) @@ -439,30 +430,6 @@ namespace Content.Server.Disposal.Unit.EntitySystems return state == SharedDisposalUnitComponent.PressureState.Ready && component.RecentlyEjected.Count == 0; } - private bool IsValidInteraction(ITargetedInteractEventArgs eventArgs) - { - if (!Get().CanInteract(eventArgs.User)) - { - eventArgs.Target.PopupMessage(eventArgs.User, Loc.GetString("ui-disposal-unit-is-valid-interaction-cannot=interact")); - return false; - } - - if (eventArgs.User.IsInContainer()) - { - eventArgs.Target.PopupMessage(eventArgs.User, Loc.GetString("ui-disposal-unit-is-valid-interaction-cannot-reach")); - return false; - } - // This popup message doesn't appear on clicks, even when code was seperate. Unsure why. - - if (!EntityManager.HasComponent(eventArgs.User)) - { - eventArgs.Target.PopupMessage(eventArgs.User, Loc.GetString("ui-disposal-unit-is-valid-interaction-no-hands")); - return false; - } - - return true; - } - public bool TryInsert(EntityUid unitId, EntityUid toInsertId, EntityUid userId, DisposalUnitComponent? unit = null) { if (!Resolve(unitId, ref unit)) diff --git a/Content.Server/Extinguisher/FireExtinguisherSystem.cs b/Content.Server/Extinguisher/FireExtinguisherSystem.cs index a76a72e7bd..f3c463daad 100644 --- a/Content.Server/Extinguisher/FireExtinguisherSystem.cs +++ b/Content.Server/Extinguisher/FireExtinguisherSystem.cs @@ -19,7 +19,6 @@ namespace Content.Server.Extinguisher; public class FireExtinguisherSystem : EntitySystem { - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; @@ -145,9 +144,6 @@ public class FireExtinguisherSystem : EntitySystem if (!Resolve(uid, ref extinguisher)) return; - if (!_actionBlockerSystem.CanInteract(user) || !extinguisher.HasSafety) - return; - extinguisher.Safety = !extinguisher.Safety; SoundSystem.Play(Filter.Pvs(uid), extinguisher.SafetySound.GetSound(), uid, AudioHelpers.WithVariation(0.125f).WithVolume(-4f)); diff --git a/Content.Server/Interaction/InteractionSystem.cs b/Content.Server/Interaction/InteractionSystem.cs index dbb6cb80cc..34f752d893 100644 --- a/Content.Server/Interaction/InteractionSystem.cs +++ b/Content.Server/Interaction/InteractionSystem.cs @@ -100,10 +100,10 @@ namespace Content.Server.Interaction return; } - if (!_actionBlockerSystem.CanInteract(userEntity.Value)) + if (Deleted(msg.Dropped) || Deleted(msg.Target)) return; - if (Deleted(msg.Dropped) || Deleted(msg.Target)) + if (!_actionBlockerSystem.CanInteract(userEntity.Value, msg.Target)) return; var interactionArgs = new DragDropEvent(userEntity.Value, msg.DropLocation, msg.Dropped, msg.Target); @@ -232,13 +232,11 @@ namespace Content.Server.Interaction /// /// Uses an empty hand on an entity /// Finds components with the InteractHand interface and calls their function - /// NOTE: Does not have an InRangeUnobstructed check + /// NOTE: Does not have any range or can-interact checks. These should all have been done before this function is called. /// - public override void InteractHand(EntityUid user, EntityUid target, bool checkActionBlocker = true) + public override void InteractHand(EntityUid user, EntityUid target) { // TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction. - if (checkActionBlocker && !_actionBlockerSystem.CanInteract(user)) - return; // all interactions should only happen when in range / unobstructed, so no range check is needed var message = new InteractHandEvent(user, target); @@ -260,18 +258,26 @@ namespace Content.Server.Interaction } // Else we run Activate. - InteractionActivate(user, target); + InteractionActivate(user, target, + checkCanInteract: false, + checkUseDelay: true, + checkAccess: false); } /// /// Will have two behaviors, either "uses" the used entity at range on the target entity if it is capable of accepting that action /// Or it will use the used entity itself on the position clicked, regardless of what was there /// - public override async Task InteractUsingRanged(EntityUid user, EntityUid used, EntityUid? target, EntityCoordinates clickLocation, bool inRangeUnobstructed) + public override void InteractUsingRanged( + EntityUid user, + EntityUid used, + EntityUid? target, + EntityCoordinates clickLocation, + bool inRangeUnobstructed) { // TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction. - if (InteractDoBefore(user, used, target, clickLocation, inRangeUnobstructed)) - return true; + if (RangedInteractDoBefore(user, used, target, clickLocation, inRangeUnobstructed)) + return; if (target != null) { @@ -279,10 +285,10 @@ namespace Content.Server.Interaction RaiseLocalEvent(target.Value, rangedMsg); if (rangedMsg.Handled) - return true; + return; } - return await InteractDoAfter(user, used, target, clickLocation, inRangeUnobstructed); + InteractDoAfter(user, used, target, clickLocation, inRangeUnobstructed); } public override void DoAttack(EntityUid user, EntityCoordinates coordinates, bool wideAttack, EntityUid? target = null) diff --git a/Content.Server/Labels/Label/HandLabelerSystem.cs b/Content.Server/Labels/Label/HandLabelerSystem.cs index 0288bbad03..4afb84f16e 100644 --- a/Content.Server/Labels/Label/HandLabelerSystem.cs +++ b/Content.Server/Labels/Label/HandLabelerSystem.cs @@ -79,21 +79,8 @@ namespace Content.Server.Labels args.Handled = true; } - private bool CheckInteract(ICommonSession session) - { - if (session.AttachedEntity is not {Valid: true } uid - || !Get().CanInteract(uid) - || !Get().CanUse(uid)) - return false; - - return true; - } - private void OnHandLabelerLabelChanged(EntityUid uid, HandLabelerComponent handLabeler, HandLabelerLabelChangedMessage args) { - if (!CheckInteract(args.Session)) - return; - handLabeler.AssignedLabel = args.Label.Trim().Substring(0, Math.Min(handLabeler.MaxLabelChars, args.Label.Length)); DirtyUI(uid, handLabeler); } diff --git a/Content.Server/Light/Components/HandheldLightComponent.cs b/Content.Server/Light/Components/HandheldLightComponent.cs index 7f84497f66..a24dba0fd2 100644 --- a/Content.Server/Light/Components/HandheldLightComponent.cs +++ b/Content.Server/Light/Components/HandheldLightComponent.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using Content.Server.Clothing.Components; using Content.Server.Light.EntitySystems; +using Content.Shared.ActionBlocker; using Content.Shared.Actions.Behaviors.Item; using Content.Shared.Examine; using Content.Shared.Interaction; @@ -50,10 +51,11 @@ namespace Content.Server.Light.Components [UsedImplicitly] [DataDefinition] - public class ToggleLightAction : IToggleItemAction + public sealed class ToggleLightAction : IToggleItemAction { public bool DoToggleAction(ToggleItemActionEventArgs args) { + if (!EntitySystem.Get().CanInteract(args.Performer, args.Item)) return false; if (!IoCManager.Resolve().TryGetComponent(args.Item, out var lightComponent)) return false; if (lightComponent.Activated == args.ToggledOn) return false; return EntitySystem.Get().ToggleStatus(args.Performer, lightComponent); diff --git a/Content.Server/Light/EntitySystems/HandheldLightSystem.cs b/Content.Server/Light/EntitySystems/HandheldLightSystem.cs index 001867f050..2b462bb696 100644 --- a/Content.Server/Light/EntitySystems/HandheldLightSystem.cs +++ b/Content.Server/Light/EntitySystems/HandheldLightSystem.cs @@ -28,7 +28,6 @@ namespace Content.Server.Light.EntitySystems [UsedImplicitly] public sealed class HandheldLightSystem : EntitySystem { - [Dependency] private readonly ActionBlockerSystem _blocker = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PowerCellSystem _powerCell = default!; @@ -96,7 +95,6 @@ namespace Content.Server.Light.EntitySystems /// True if the light's status was toggled, false otherwise. public bool ToggleStatus(EntityUid user, HandheldLightComponent component) { - if (!_blocker.CanUse(user)) return false; return component.Activated ? TurnOff(component) : TurnOn(user, component); } diff --git a/Content.Server/Light/EntitySystems/LightReplacerSystem.cs b/Content.Server/Light/EntitySystems/LightReplacerSystem.cs index ab361124a1..82d2aa2932 100644 --- a/Content.Server/Light/EntitySystems/LightReplacerSystem.cs +++ b/Content.Server/Light/EntitySystems/LightReplacerSystem.cs @@ -16,9 +16,8 @@ using Robust.Shared.Player; namespace Content.Server.Light.EntitySystems { [UsedImplicitly] - public class LightReplacerSystem : EntitySystem + public sealed class LightReplacerSystem : EntitySystem { - [Dependency] private readonly ActionBlockerSystem _blocker = default!; [Dependency] private readonly PoweredLightSystem _poweredLight = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; @@ -42,7 +41,6 @@ namespace Content.Server.Light.EntitySystems return; // standard interaction checks - if (!_blocker.CanUse(eventArgs.User)) return; if (!eventArgs.CanReach) return; // behaviour will depends on target type @@ -64,9 +62,6 @@ namespace Content.Server.Light.EntitySystems if (eventArgs.Handled) return; - // standard interaction checks - if (!_blocker.CanInteract(eventArgs.User)) return; - var usedUid = eventArgs.Used; // want to insert a new light bulb? diff --git a/Content.Server/Medical/MedicalScannerSystem.cs b/Content.Server/Medical/MedicalScannerSystem.cs index 62b1345e0d..0e8d22cc10 100644 --- a/Content.Server/Medical/MedicalScannerSystem.cs +++ b/Content.Server/Medical/MedicalScannerSystem.cs @@ -75,7 +75,7 @@ namespace Content.Server.Medical private void OnRelayMovement(EntityUid uid, MedicalScannerComponent component, RelayMovementEntityEvent args) { - if (_blocker.CanInteract(args.Entity)) + if (_blocker.CanInteract(args.Entity, null)) { if (_gameTiming.CurTime < component.LastInternalOpenAttempt + MedicalScannerComponent.InternalOpenAttemptDelay) diff --git a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs index 7471f6b6c7..7d4ed128c9 100644 --- a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs +++ b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs @@ -24,7 +24,6 @@ namespace Content.Server.Medical.SuitSensors { public class SuitSensorSystem : EntitySystem { - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IdCardSystem _idCardSystem = default!; @@ -142,7 +141,7 @@ namespace Content.Server.Medical.SuitSensors return; // standard interaction checks - if (!args.CanAccess || !args.CanInteract || !_actionBlockerSystem.CanDrop(args.User)) + if (!args.CanAccess || !args.CanInteract) return; args.Verbs.UnionWith(new[] diff --git a/Content.Server/Nuke/NukeSystem.cs b/Content.Server/Nuke/NukeSystem.cs index 0393e807ea..05b151c5b4 100644 --- a/Content.Server/Nuke/NukeSystem.cs +++ b/Content.Server/Nuke/NukeSystem.cs @@ -117,12 +117,6 @@ namespace Content.Server.Nuke if (args.Handled) return; - // standard interactions check - if (!args.InRangeUnobstructed()) - return; - if (!_actionBlocker.CanInteract(args.User) || !_actionBlocker.CanUse(args.User)) - return; - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) return; diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs index e6de25dd53..f1d6ca264a 100644 --- a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs @@ -40,7 +40,6 @@ namespace Content.Server.Nutrition.EntitySystems [Dependency] private readonly StomachSystem _stomachSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedAdminLogSystem _logSystem = default!; - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly SpillableSystem _spillableSystem = default!; public override void Initialize() @@ -111,10 +110,6 @@ namespace Content.Server.Nutrition.EntitySystems if (args.Handled || args.Target == null || !args.CanReach) return; - // CanInteract already checked CanInteract - if (!_actionBlockerSystem.CanUse(args.User)) - return; - args.Handled = TryDrink(args.User, args.Target.Value, component); } @@ -122,12 +117,6 @@ namespace Content.Server.Nutrition.EntitySystems { if (args.Handled) return; - if (!args.User.InRangeUnobstructed(uid, popup: true)) - { - args.Handled = true; - return; - } - if (!component.Opened) { //Do the opening stuff like playing the sounds. @@ -137,10 +126,6 @@ namespace Content.Server.Nutrition.EntitySystems return; } - // CanUse already checked; trying to keep it consistent if we interact with ourselves. - if (!_actionBlockerSystem.CanInteract(args.User)) - return; - args.Handled = TryDrink(args.User, args.User, component); } diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index 25605c7a4c..1c62e00a5d 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -40,7 +40,6 @@ namespace Content.Server.Nutrition.EntitySystems [Dependency] private readonly UtensilSystem _utensilSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedAdminLogSystem _logSystem = default!; - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; public override void Initialize() @@ -79,9 +78,6 @@ namespace Content.Server.Nutrition.EntitySystems public bool TryFeed(EntityUid user, EntityUid target, FoodComponent food) { - if (!_actionBlockerSystem.CanInteract(user) || !_actionBlockerSystem.CanUse(user)) - return false; - // if currently being used to feed, cancel that action. if (food.CancelToken != null) { diff --git a/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorControlBoxComponent.cs b/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorControlBoxComponent.cs index ac987f92e2..d413fb3bb4 100644 --- a/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorControlBoxComponent.cs +++ b/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorControlBoxComponent.cs @@ -150,13 +150,6 @@ namespace Content.Server.ParticleAccelerator.Components return; } - - if (obj.Session.AttachedEntity is not {Valid: true} attached || - !EntitySystem.Get().CanInteract(attached)) - { - return; - } - if (_wireInterfaceBlocked) { return; diff --git a/Content.Server/Plants/Systems/PottedPlantHideSystem.cs b/Content.Server/Plants/Systems/PottedPlantHideSystem.cs index f0dfe1aae8..10dd5e5dbd 100644 --- a/Content.Server/Plants/Systems/PottedPlantHideSystem.cs +++ b/Content.Server/Plants/Systems/PottedPlantHideSystem.cs @@ -37,9 +37,6 @@ namespace Content.Server.Plants.Systems if (args.Handled) return; - // standard interaction checks - if (!_blocker.CanInteract(args.User)) return; - Rustle(uid, component); args.Handled = _stashSystem.TryHideItem(uid, args.User, args.Used); } @@ -49,9 +46,6 @@ namespace Content.Server.Plants.Systems if (args.Handled) return; - // standard interaction checks - if (!_blocker.CanInteract(args.User)) return; - Rustle(uid, component); var gotItem = _stashSystem.TryGetItem(uid, args.User); diff --git a/Content.Server/Shuttles/EntitySystems/ShuttleConsoleSystem.cs b/Content.Server/Shuttles/EntitySystems/ShuttleConsoleSystem.cs index c060da4298..53992d56be 100644 --- a/Content.Server/Shuttles/EntitySystems/ShuttleConsoleSystem.cs +++ b/Content.Server/Shuttles/EntitySystems/ShuttleConsoleSystem.cs @@ -96,7 +96,7 @@ namespace Content.Server.Shuttles.EntitySystems { if (comp.Console == null) continue; - if (!_blocker.CanInteract((comp).Owner)) + if (!_blocker.CanInteract(comp.Owner, comp.Console.Owner)) { toRemove.Add(comp); } @@ -189,8 +189,7 @@ namespace Content.Server.Shuttles.EntitySystems public void AddPilot(EntityUid entity, ShuttleConsoleComponent component) { - if (!_blocker.CanInteract(entity) || - !EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent) || + if (!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent) || component.SubscribedPilots.Contains(pilotComponent)) { return; diff --git a/Content.Server/Strip/StrippableComponent.cs b/Content.Server/Strip/StrippableComponent.cs index 8cf8acc606..f48ab805f0 100644 --- a/Content.Server/Strip/StrippableComponent.cs +++ b/Content.Server/Strip/StrippableComponent.cs @@ -88,9 +88,6 @@ namespace Content.Server.Strip bool Check() { - if (!EntitySystem.Get().CanInteract(user)) - return false; - if (item == null) { user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything")); @@ -153,9 +150,6 @@ namespace Content.Server.Strip bool Check() { - if (!EntitySystem.Get().CanInteract(user)) - return false; - if (item == null) { user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything")); @@ -219,9 +213,6 @@ namespace Content.Server.Strip bool Check() { - if (!EntitySystem.Get().CanInteract(user)) - return false; - if (!invSystem.HasSlot(Owner, slot)) return false; @@ -272,9 +263,6 @@ namespace Content.Server.Strip bool Check() { - if (!EntitySystem.Get().CanInteract(user)) - return false; - if (!hands.HasHand(hand)) return false; diff --git a/Content.Server/Stunnable/StunbatonSystem.cs b/Content.Server/Stunnable/StunbatonSystem.cs index caeffc02af..1d197ff78d 100644 --- a/Content.Server/Stunnable/StunbatonSystem.cs +++ b/Content.Server/Stunnable/StunbatonSystem.cs @@ -73,9 +73,6 @@ namespace Content.Server.Stunnable private void OnUseInHand(EntityUid uid, StunbatonComponent comp, UseInHandEvent args) { - if (!Get().CanUse(args.User)) - return; - if (comp.Activated) { TurnOff(comp); diff --git a/Content.Server/Tabletop/TabletopSystem.cs b/Content.Server/Tabletop/TabletopSystem.cs index 88a2ff9254..bd0618777b 100644 --- a/Content.Server/Tabletop/TabletopSystem.cs +++ b/Content.Server/Tabletop/TabletopSystem.cs @@ -58,9 +58,7 @@ namespace Content.Server.Tabletop if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) return; - // Check that the entity can interact with the game board. - if(_actionBlockerSystem.CanInteract(args.User)) - OpenSessionFor(actor.PlayerSession, uid); + OpenSessionFor(actor.PlayerSession, uid); } private void OnGameShutdown(EntityUid uid, TabletopGameComponent component, ComponentShutdown args) diff --git a/Content.Server/Tools/ToolSystem.Welder.cs b/Content.Server/Tools/ToolSystem.Welder.cs index a4649bd07f..4990ab0e84 100644 --- a/Content.Server/Tools/ToolSystem.Welder.cs +++ b/Content.Server/Tools/ToolSystem.Welder.cs @@ -102,9 +102,6 @@ namespace Content.Server.Tools return false; } - if (user != null && !_actionBlockerSystem.CanInteract(user.Value)) - return false; - solution.RemoveReagent(welder.FuelReagent, welder.FuelLitCost); welder.Lit = true; @@ -140,9 +137,6 @@ namespace Content.Server.Tools // Optional components. Resolve(uid, ref item, ref light, ref sprite); - if (user != null && !_actionBlockerSystem.CanInteract(user.Value)) - return false; - welder.Lit = false; // TODO: Make all this use visualizers. diff --git a/Content.Server/Tools/ToolSystem.cs b/Content.Server/Tools/ToolSystem.cs index d7a166e715..fc11270f29 100644 --- a/Content.Server/Tools/ToolSystem.cs +++ b/Content.Server/Tools/ToolSystem.cs @@ -23,7 +23,6 @@ namespace Content.Server.Tools [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; @@ -214,7 +213,7 @@ namespace Content.Server.Tools if (!Resolve(tool, ref toolComponent)) return false; - if (!toolComponent.Qualities.ContainsAll(toolQualitiesNeeded) || !_actionBlockerSystem.CanInteract(user)) + if (!toolComponent.Qualities.ContainsAll(toolQualitiesNeeded)) return false; var beforeAttempt = new ToolUseAttemptEvent(fuel, user); diff --git a/Content.Server/Traitor/Uplink/UplinkSystem.cs b/Content.Server/Traitor/Uplink/UplinkSystem.cs index 925a9741e2..0534d06fc3 100644 --- a/Content.Server/Traitor/Uplink/UplinkSystem.cs +++ b/Content.Server/Traitor/Uplink/UplinkSystem.cs @@ -89,10 +89,6 @@ namespace Content.Server.Traitor.Uplink if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) return; - var actionBlocker = EntitySystem.Get(); - if (!actionBlocker.CanInteract(uid) || !actionBlocker.CanUse(uid)) - return; - ToggleUplinkUI(component, actor.PlayerSession); args.Handled = true; } diff --git a/Content.Server/UserInterface/ActivatableUISystem.cs b/Content.Server/UserInterface/ActivatableUISystem.cs index 1eae822e41..2df6b3dff0 100644 --- a/Content.Server/UserInterface/ActivatableUISystem.cs +++ b/Content.Server/UserInterface/ActivatableUISystem.cs @@ -18,7 +18,6 @@ namespace Content.Server.UserInterface [UsedImplicitly] internal sealed class ActivatableUISystem : EntitySystem { - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly IAdminManager _adminManager = default!; public override void Initialize() @@ -82,12 +81,6 @@ namespace Content.Server.UserInterface if (aui.AdminOnly && !_adminManager.IsAdmin(actor.PlayerSession)) return false; - if (!HasComp(user) && !_actionBlockerSystem.CanInteract(user)) - { - user.PopupMessageCursor(Loc.GetString("base-computer-ui-component-cannot-interact")); - return true; - } - var ui = aui.UserInterface; if (ui == null) return false; diff --git a/Content.Server/Weapon/Melee/EnergySword/EnergySwordSystem.cs b/Content.Server/Weapon/Melee/EnergySword/EnergySwordSystem.cs index c4311548e6..8a50244fbf 100644 --- a/Content.Server/Weapon/Melee/EnergySword/EnergySwordSystem.cs +++ b/Content.Server/Weapon/Melee/EnergySword/EnergySwordSystem.cs @@ -44,9 +44,6 @@ namespace Content.Server.Weapon.Melee.EnergySword { if (args.Handled) return; - if (!_blockerSystem.CanUse(args.User)) - return; - args.Handled = true; if (comp.Activated) @@ -114,7 +111,7 @@ namespace Content.Server.Weapon.Melee.EnergySword { if (args.Handled) return; - if (comp.Hacked || !_blockerSystem.CanInteract(args.User)) + if (comp.Hacked) return; if (!TryComp(args.Used, out ToolComponent? tool) || !tool.Qualities.ContainsAny("Pulsing")) return; diff --git a/Content.Server/Weapon/Ranged/GunSystem.Guns.cs b/Content.Server/Weapon/Ranged/GunSystem.Guns.cs index 2477d10644..7eac43dfaf 100644 --- a/Content.Server/Weapon/Ranged/GunSystem.Guns.cs +++ b/Content.Server/Weapon/Ranged/GunSystem.Guns.cs @@ -40,7 +40,7 @@ public sealed partial class GunSystem if (!TryComp(user, out CombatModeComponent? combat) || !combat.IsInCombatMode || - !_blocker.CanInteract(user)) return; + !_blocker.CanInteract(user, gun.Owner)) return; var fireAttempt = new GunFireAttemptEvent(user, gun); EntityManager.EventBus.RaiseLocalEvent(gun.Owner, fireAttempt); diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index bd36316bae..e9f2f19fda 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -16,7 +16,7 @@ namespace Content.Shared.ActionBlocker /// Utility methods to check if a specific entity is allowed to perform an action. /// [UsedImplicitly] - public class ActionBlockerSystem : EntitySystem + public sealed class ActionBlockerSystem : EntitySystem { public bool CanMove(EntityUid uid) { @@ -26,26 +26,54 @@ namespace Content.Shared.ActionBlocker return !ev.Cancelled; } - public bool CanInteract(EntityUid uid) + /// + /// Raises an event directed at both the user and the target entity to check whether a user is capable of + /// interacting with this entity. + /// + /// + /// If this is a generic interaction without a target (e.g., stop-drop-and-roll when burning), the target + /// may be null. Note that this is checked by . In the majority of + /// cases, systems that provide interactions will not need to check this themselves, though they may need to + /// check other blockers like + /// + /// + public bool CanInteract(EntityUid user, EntityUid? target) { - var ev = new InteractionAttemptEvent(uid); - RaiseLocalEvent(uid, ev); + var ev = new InteractionAttemptEvent(user, target); + RaiseLocalEvent(user, ev); + + if (ev.Cancelled) + return false; + + if (target == null) + return true; + + var targetEv = new GettingInteractedWithAttemptEvent(user, target); + RaiseLocalEvent(target.Value, targetEv); + + return !targetEv.Cancelled; + } + + /// + /// Can a user utilize the entity that they are currently holding in their hands. + /// > + /// + /// This event is automatically checked by for any interactions that + /// involve using a held entity. In the majority of cases, systems that provide interactions will not need + /// to check this themselves. + /// + public bool CanUseHeldEntity(EntityUid user) + { + var ev = new UseAttemptEvent(user); + RaiseLocalEvent(user, ev); return !ev.Cancelled; } - public bool CanUse(EntityUid uid) + public bool CanThrow(EntityUid user) { - var ev = new UseAttemptEvent(uid); - RaiseLocalEvent(uid, ev); - - return !ev.Cancelled; - } - - public bool CanThrow(EntityUid uid) - { - var ev = new ThrowAttemptEvent(uid); - RaiseLocalEvent(uid, ev); + var ev = new ThrowAttemptEvent(user); + RaiseLocalEvent(user, ev); return !ev.Cancelled; } diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index 05ecdc05f0..106fec4e6e 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -278,6 +278,7 @@ namespace Content.Shared.Containers.ItemSlots /// /// Tries to insert item into a specific slot from an entity's hand. + /// Does not check action blockers. /// /// False if failed to insert item public bool TryInsertFromHand(EntityUid uid, ItemSlot slot, EntityUid user, SharedHandsComponent? hands = null) @@ -293,7 +294,7 @@ namespace Content.Shared.Containers.ItemSlots return false; // hands.Drop(item) checks CanDrop action blocker - if (!_actionBlockerSystem.CanInteract(user) && hands.Drop(heldItem)) + if (hands.Drop(heldItem)) return false; Insert(uid, slot, heldItem, user); diff --git a/Content.Shared/Hands/Components/SharedHandsComponent.cs b/Content.Shared/Hands/Components/SharedHandsComponent.cs index 448ad6d4bb..777c79c07d 100644 --- a/Content.Shared/Hands/Components/SharedHandsComponent.cs +++ b/Content.Shared/Hands/Components/SharedHandsComponent.cs @@ -561,7 +561,7 @@ namespace Content.Shared.Hands.Components /// /// Attempts to interact with the item in a hand using the active held item. /// - public async void InteractHandWithActiveHand(string handName) + public void InteractHandWithActiveHand(string handName) { if (!TryGetActiveHeldEntity(out var activeHeldEntity)) return; @@ -572,7 +572,7 @@ namespace Content.Shared.Hands.Components if (activeHeldEntity == heldEntity) return; - await EntitySystem.Get() + EntitySystem.Get() .InteractUsing(Owner, activeHeldEntity.Value, heldEntity.Value, EntityCoordinates.Invalid); } @@ -585,7 +585,7 @@ namespace Content.Shared.Hands.Components if (altInteract) sys.AltInteract(Owner, heldEntity.Value); else - sys.TryUseInteraction(Owner, heldEntity.Value); + sys.UseInHandInteraction(Owner, heldEntity.Value); } public void ActivateHeldEntity(string handName) @@ -594,7 +594,7 @@ namespace Content.Shared.Hands.Components return; EntitySystem.Get() - .TryInteractionActivate(Owner, heldEntity); + .InteractionActivate(Owner, heldEntity.Value); } /// diff --git a/Content.Shared/Hands/SharedHandVirtualItemSystem.cs b/Content.Shared/Hands/SharedHandVirtualItemSystem.cs index 57345608e5..fd7b2e8a22 100644 --- a/Content.Shared/Hands/SharedHandVirtualItemSystem.cs +++ b/Content.Shared/Hands/SharedHandVirtualItemSystem.cs @@ -12,7 +12,7 @@ public abstract class SharedHandVirtualItemSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnBeingEquippedAttempt); - SubscribeLocalEvent(HandleBeforeInteract); + SubscribeLocalEvent(HandleBeforeInteract); } private void OnBeingEquippedAttempt(EntityUid uid, HandVirtualItemComponent component, BeingEquippedAttemptEvent args) @@ -23,7 +23,7 @@ public abstract class SharedHandVirtualItemSystem : EntitySystem private static void HandleBeforeInteract( EntityUid uid, HandVirtualItemComponent component, - BeforeInteractEvent args) + BeforeRangedInteractEvent args) { // No interactions with a virtual item, please. args.Handled = true; diff --git a/Content.Shared/Interaction/BeforeInteract.cs b/Content.Shared/Interaction/BeforeInteract.cs index c2c5563511..156507fd87 100644 --- a/Content.Shared/Interaction/BeforeInteract.cs +++ b/Content.Shared/Interaction/BeforeInteract.cs @@ -8,7 +8,7 @@ namespace Content.Shared.Interaction /// Raised directed on the used object when clicking on another object before an interaction is handled. /// [PublicAPI] - public class BeforeInteractEvent : HandledEntityEventArgs + public class BeforeRangedInteractEvent : HandledEntityEventArgs { /// /// Entity that triggered the interaction. @@ -35,7 +35,7 @@ namespace Content.Shared.Interaction /// public bool CanReach { get; } - public BeforeInteractEvent( + public BeforeRangedInteractEvent( EntityUid user, EntityUid used, EntityUid? target, diff --git a/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs b/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs index a8186e9878..3f4e19d05c 100644 --- a/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs +++ b/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs @@ -2,13 +2,34 @@ namespace Content.Shared.Interaction.Events { - public class InteractionAttemptEvent : CancellableEntityEventArgs + /// + /// Event raised directed at a user to see if they can perform a generic interaction. + /// + public sealed class InteractionAttemptEvent : CancellableEntityEventArgs { - public InteractionAttemptEvent(EntityUid uid) + public InteractionAttemptEvent(EntityUid uid, EntityUid? target) { Uid = uid; + Target = target; } public EntityUid Uid { get; } + public EntityUid? Target { get; } + } + + /// + /// Event raised directed at the target entity of an interaction to see if the user is allowed to perform some + /// generic interaction. + /// + public sealed class GettingInteractedWithAttemptEvent : CancellableEntityEventArgs + { + public GettingInteractedWithAttemptEvent(EntityUid uid, EntityUid? target) + { + Uid = uid; + Target = target; + } + + public EntityUid Uid { get; } + public EntityUid? Target { get; } } } diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 66799a5e83..64ef8048b3 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -75,7 +75,7 @@ namespace Content.Shared.Interaction /// private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev) { - if (ev.Sender.AttachedEntity is not EntityUid user || !_actionBlockerSystem.CanInteract(user)) + if (ev.Sender.AttachedEntity is not EntityUid user || !_actionBlockerSystem.CanInteract(user, ev.Target)) { ev.Cancel(); return; @@ -108,6 +108,11 @@ namespace Content.Shared.Interaction return; } + // We won't bother to check that the target item is ACTUALLY in an inventory slot. UserInteraction() and + // InteractionActivate() should check that the item is accessible. So.. if a user wants to lie about an + // in-reach item being used in a slot... that should have no impact. This is functionally the same as if + // they had somehow directly clicked on that item. + if (msg.AltInteract) // Use 'UserInteraction' function - behaves as if the user alt-clicked the item in the world. UserInteraction(user.Value, coords, msg.ItemUid, msg.AltInteract); @@ -139,7 +144,14 @@ namespace Content.Shared.Interaction /// Whether to use default or alternative interactions (usually as a result of /// alt+clicking). If combat mode is enabled, the alternative action is to perform the default non-combat /// interaction. Having an item in the active hand also disables alternative interactions. - public async void UserInteraction(EntityUid user, EntityCoordinates coordinates, EntityUid? target, bool altInteract = false) + public void UserInteraction( + EntityUid user, + EntityCoordinates coordinates, + EntityUid? target, + bool altInteract = false, + bool checkCanInteract = true, + bool checkAccess = true, + bool checkCanUse = true) { if (target != null && Deleted(target.Value)) return; @@ -154,55 +166,71 @@ namespace Content.Shared.Interaction if (!ValidateInteractAndFace(user, coordinates)) return; - if (!_actionBlockerSystem.CanInteract(user)) + if (altInteract && target != null) + { + // Perform alternative interactions, using context menu verbs. + // These perform their own range, can-interact, and accessibility checks. + AltInteract(user, target.Value); + } + + if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target)) return; // Check if interacted entity is in the same container, the direct child, or direct parent of the user. - // This is bypassed IF the interaction happened through an item slot (e.g., backpack UI) - if (target != null && !ContainerSystem.IsInSameOrParentContainer(user, target.Value) && !CanAccessViaStorage(user, target.Value)) + // Also checks if the item is accessible via some storage UI (e.g., open backpack) + if (checkAccess + && target != null + && !ContainerSystem.IsInSameOrParentContainer(user, target.Value) + && !CanAccessViaStorage(user, target.Value)) return; - // Verify user has a hand, and find what object they are currently holding in their active hand - if (!TryComp(user, out SharedHandsComponent? hands)) + // Does the user have hands? + Hand? hand; + if (!TryComp(user, out SharedHandsComponent? hands) || !hands.TryGetActiveHand(out hand)) return; + // ^ In future, things like looking at a UI & opening doors (i.e., Activate interactions) shouldn't neccesarily require hands. + // But that would first involve some work with BUIs & making sure other activate-interactions check hands if they are required. + + // Check range // TODO: Replace with body interaction range when we get something like arm length or telekinesis or something. - var inRangeUnobstructed = user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true); - if (target == null || !inRangeUnobstructed) + var inRangeUnobstructed = !checkAccess || user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true); + + // empty-hand interactions + if (hand.HeldEntity == null) { - if (!hands.TryGetActiveHeldEntity(out var heldEntity) || !_actionBlockerSystem.CanUse(user)) - return; - - if (await InteractUsingRanged(user, heldEntity.Value, target, coordinates, inRangeUnobstructed)) - return; - - // Generate popup only if user actually tried to click on something. - if (!inRangeUnobstructed && target != null) - { - _popupSystem.PopupCursor(Loc.GetString("interaction-system-user-interaction-cannot-reach"), Filter.Entities(user)); - } + if (inRangeUnobstructed && target != null) + InteractHand(user, target.Value); return; } - // We are close to the nearby object. - if (altInteract) + // Can the user use the held entity? + if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user)) + return; + + if (inRangeUnobstructed && target != null) { - // Perform alternative interactions, using context menu verbs. - AltInteract(user, target.Value); - } - else if (!hands.TryGetActiveHeldEntity(out var heldEntity)) - { - // Since our hand is empty we will use InteractHand/Activate - InteractHand(user, target.Value, checkActionBlocker: false); - } - else if (heldEntity != target && _actionBlockerSystem.CanUse(user)) - { - await InteractUsing(user, heldEntity.Value, target.Value, coordinates, checkActionBlocker: false); + InteractUsing( + user, + hand.HeldEntity.Value, + target.Value, + coordinates, + checkCanInteract: false, + checkCanUse: false); + + return; } + + InteractUsingRanged( + user, + hand.HeldEntity.Value, + target, + coordinates, + inRangeUnobstructed); } - public virtual void InteractHand(EntityUid user, EntityUid target, bool checkActionBlocker = true) + public virtual void InteractHand(EntityUid user, EntityUid target) { // TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction. } @@ -213,11 +241,10 @@ namespace Content.Shared.Interaction // TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction. } - public virtual async Task InteractUsingRanged(EntityUid user, EntityUid used, EntityUid? target, + public virtual void InteractUsingRanged(EntityUid user, EntityUid used, EntityUid? target, EntityCoordinates clickLocation, bool inRangeUnobstructed) { // TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction. - return await Task.FromResult(true); } protected bool ValidateInteractAndFace(EntityUid user, EntityCoordinates coordinates) @@ -548,21 +575,21 @@ namespace Content.Shared.Interaction if (!inRange && popup) { - var message = Loc.GetString("shared-interaction-system-in-range-unobstructed-cannot-reach"); + var message = Loc.GetString("interaction-system-user-interaction-cannot-reach"); origin.PopupMessage(message); } return inRange; } - public bool InteractDoBefore( + public bool RangedInteractDoBefore( EntityUid user, EntityUid used, EntityUid? target, EntityCoordinates clickLocation, bool canReach) { - var ev = new BeforeInteractEvent(user, used, target, clickLocation, canReach); + var ev = new BeforeRangedInteractEvent(user, used, target, clickLocation, canReach); RaiseLocalEvent(used, ev, false); return ev.Handled; } @@ -572,12 +599,22 @@ namespace Content.Shared.Interaction /// Finds components with the InteractUsing interface and calls their function /// NOTE: Does not have an InRangeUnobstructed check /// - public async Task InteractUsing(EntityUid user, EntityUid used, EntityUid target, EntityCoordinates clickLocation, bool predicted = false, bool checkActionBlocker = true) + public async void InteractUsing( + EntityUid user, + EntityUid used, + EntityUid target, + EntityCoordinates clickLocation, + bool predicted = false, + bool checkCanInteract = true, + bool checkCanUse = true) { - if (checkActionBlocker && (!_actionBlockerSystem.CanInteract(user) || !_actionBlockerSystem.CanUse(user))) + if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target)) return; - if (InteractDoBefore(user, used, target, clickLocation, true)) + if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user)) + return; + + if (RangedInteractDoBefore(user, used, target, clickLocation, true)) return; // all interactions should only happen when in range / unobstructed, so no range check is needed @@ -596,13 +633,13 @@ namespace Content.Shared.Interaction return; } - await InteractDoAfter(user, used, target, clickLocation, true); + InteractDoAfter(user, used, target, clickLocation, canReach: true); } /// /// Used when clicking on an entity resulted in no other interaction. Used for low-priority interactions. /// - public async Task InteractDoAfter(EntityUid user, EntityUid used, EntityUid? target, EntityCoordinates clickLocation, bool canReach) + public async void InteractDoAfter(EntityUid user, EntityUid used, EntityUid? target, EntityCoordinates clickLocation, bool canReach) { if (target is {Valid: false}) target = null; @@ -610,7 +647,7 @@ namespace Content.Shared.Interaction var afterInteractEvent = new AfterInteractEvent(user, used, target, clickLocation, canReach); RaiseLocalEvent(used, afterInteractEvent, false); if (afterInteractEvent.Handled) - return true; + return; var afterInteractEventArgs = new AfterInteractEventArgs(user, clickLocation, target, canReach); var afterInteracts = AllComps(used).OrderByDescending(x => x.Priority).ToList(); @@ -618,46 +655,49 @@ namespace Content.Shared.Interaction foreach (var afterInteract in afterInteracts) { if (await afterInteract.AfterInteract(afterInteractEventArgs)) - return true; + return; } if (target == null) - return false; + return; var afterInteractUsingEvent = new AfterInteractUsingEvent(user, used, target, clickLocation, canReach); RaiseLocalEvent(target.Value, afterInteractUsingEvent, false); - return afterInteractEvent.Handled; } #region ActivateItemInWorld /// - /// Activates the IActivate behavior of an object - /// Verifies that the user is capable of doing the use interaction first + /// Raises events and activates the IActivate behavior of an object. /// - public void TryInteractionActivate(EntityUid? user, EntityUid? used) + /// + /// Does not check the can-use action blocker. In activations interacts can target entities outside of the users + /// hands. + /// + public bool InteractionActivate( + EntityUid user, + EntityUid used, + bool checkCanInteract = true, + bool checkUseDelay = true, + bool checkAccess = true) { - if (user == null || used == null) - return; + UseDelayComponent? delayComponent = null; + if (checkUseDelay + && TryComp(used, out delayComponent) + && delayComponent.ActiveDelay) + return false; - InteractionActivate(user.Value, used.Value); - } + if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used)) + return false; - protected void InteractionActivate(EntityUid user, EntityUid used) - { - if (TryComp(used, out UseDelayComponent? delayComponent) && delayComponent.ActiveDelay) - return; - - if (!_actionBlockerSystem.CanInteract(user) || !_actionBlockerSystem.CanUse(user)) - return; // all activates should only fire when in range / unobstructed - if (!InRangeUnobstructed(user, used, ignoreInsideBlocker: true, popup: true)) - return; + if (checkAccess && !InRangeUnobstructed(user, used, ignoreInsideBlocker: true, popup: true)) + return false; // Check if interacted entity is in the same container, the direct child, or direct parent of the user. // This is bypassed IF the interaction happened through an item slot (e.g., backpack UI) - if (!ContainerSystem.IsInSameOrParentContainer(user, used) && !CanAccessViaStorage(user, used)) - return; + if (checkAccess && !ContainerSystem.IsInSameOrParentContainer(user, used) && !CanAccessViaStorage(user, used)) + return false; var activateMsg = new ActivateInWorldEvent(user, used); RaiseLocalEvent(used, activateMsg); @@ -665,44 +705,47 @@ namespace Content.Shared.Interaction { BeginDelay(delayComponent); _adminLogSystem.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}"); - return; + return true; } if (!TryComp(used, out IActivate? activateComp)) - return; + return false; var activateEventArgs = new ActivateEventArgs(user, used); activateComp.Activate(activateEventArgs); BeginDelay(delayComponent); _adminLogSystem.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}"); // No way to check success. + return true; } #endregion #region Hands #region Use /// - /// Attempt to perform a use-interaction on an entity. If no interaction occurs, it will instead attempt to - /// activate the entity. - /// - public void TryUseInteraction(EntityUid user, EntityUid used) - { - if (_actionBlockerSystem.CanUse(user) && UseInteraction(user, used)) - return; - - // no use-interaction occurred. Attempt to activate the item instead. - InteractionActivate(user, used); - } - - /// - /// Activates the IUse behaviors of an entity without first checking - /// if the user is capable of doing the use interaction. + /// Raises UseInHandEvents and activates the IUse behaviors of an entity + /// Does not check accessibility or range, for obvious reasons /// /// True if the interaction was handled. False otherwise - public bool UseInteraction(EntityUid user, EntityUid used) + public bool UseInHandInteraction( + EntityUid user, + EntityUid used, + bool checkCanUse = true, + bool checkCanInteract = true, + bool checkUseDelay = true) { - if (TryComp(used, out UseDelayComponent? delayComponent) && delayComponent.ActiveDelay) + UseDelayComponent? delayComponent = null; + + if (checkUseDelay + && TryComp(used, out delayComponent) + && delayComponent.ActiveDelay) return true; // if the item is on cooldown, we consider this handled. + if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used)) + return false; + + if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user)) + return false; + var useMsg = new UseInHandEvent(user, used); RaiseLocalEvent(used, useMsg); if (useMsg.Handled) @@ -724,7 +767,8 @@ namespace Content.Shared.Interaction } } - return false; + // else, default to activating the item + return InteractionActivate(user, used, false, false, false); } protected virtual void BeginDelay(UseDelayComponent? component = null) @@ -854,7 +898,7 @@ namespace Content.Shared.Interaction /// Raised when a player attempts to activate an item in an inventory slot or hand slot /// [Serializable, NetSerializable] - public class InteractInventorySlotEvent : EntityEventArgs + public sealed class InteractInventorySlotEvent : EntityEventArgs { /// /// Entity that was interacted with. diff --git a/Content.Shared/Pulling/Systems/SharedPullingSystem.Actions.cs b/Content.Shared/Pulling/Systems/SharedPullingSystem.Actions.cs index 5e34801595..9406f58fca 100644 --- a/Content.Shared/Pulling/Systems/SharedPullingSystem.Actions.cs +++ b/Content.Shared/Pulling/Systems/SharedPullingSystem.Actions.cs @@ -24,7 +24,7 @@ namespace Content.Shared.Pulling return false; } - if (!_blocker.CanInteract(puller)) + if (!_blocker.CanInteract(puller, pulled)) { return false; } diff --git a/Content.Shared/Storage/SharedStorageComponent.cs b/Content.Shared/Storage/SharedStorageComponent.cs index 4275633e75..f9f5c4a4fe 100644 --- a/Content.Shared/Storage/SharedStorageComponent.cs +++ b/Content.Shared/Storage/SharedStorageComponent.cs @@ -34,7 +34,7 @@ namespace Content.Shared.Storage bool IDraggable.Drop(DragDropEvent eventArgs) { - if (!EntitySystem.Get().CanInteract(eventArgs.User)) + if (!EntitySystem.Get().CanInteract(eventArgs.User, eventArgs.Target)) { return false; } diff --git a/Content.Shared/Strip/Components/SharedStrippableComponent.cs b/Content.Shared/Strip/Components/SharedStrippableComponent.cs index 57146d81c5..1546e69885 100644 --- a/Content.Shared/Strip/Components/SharedStrippableComponent.cs +++ b/Content.Shared/Strip/Components/SharedStrippableComponent.cs @@ -15,7 +15,7 @@ namespace Content.Shared.Strip.Components { return by != Owner && IoCManager.Resolve().HasComponent(@by) - && EntitySystem.Get().CanInteract(@by); + && EntitySystem.Get().CanInteract(@by, Owner); } bool IDraggable.CanDrop(CanDropEvent args) diff --git a/Content.Shared/Tabletop/SharedTabletopSystem.cs b/Content.Shared/Tabletop/SharedTabletopSystem.cs index da267354de..e3e9807286 100644 --- a/Content.Shared/Tabletop/SharedTabletopSystem.cs +++ b/Content.Shared/Tabletop/SharedTabletopSystem.cs @@ -49,7 +49,7 @@ namespace Content.Shared.Tabletop return false; } - return playerEntity.InRangeUnobstructed(table.Value) && _actionBlockerSystem.CanInteract(playerEntity); + return playerEntity.InRangeUnobstructed(table.Value) && _actionBlockerSystem.CanInteract(playerEntity, table); } protected bool StunnedOrNoHands(EntityUid playerEntity) diff --git a/Content.Shared/Verbs/SharedVerbSystem.cs b/Content.Shared/Verbs/SharedVerbSystem.cs index caa590dda1..6950f1c576 100644 --- a/Content.Shared/Verbs/SharedVerbSystem.cs +++ b/Content.Shared/Verbs/SharedVerbSystem.cs @@ -76,10 +76,10 @@ namespace Content.Shared.Verbs // A large number of verbs need to check action blockers. Instead of repeatedly having each system individually // call ActionBlocker checks, just cache it for the verb request. - var canInteract = force || _actionBlockerSystem.CanInteract(user); + var canInteract = force || _actionBlockerSystem.CanInteract(user, target); EntityUid? @using = null; - if (TryComp(user, out SharedHandsComponent? hands) && (force || _actionBlockerSystem.CanUse(user))) + if (TryComp(user, out SharedHandsComponent? hands) && (force || _actionBlockerSystem.CanUseHeldEntity(user))) { hands.TryGetActiveHeldEntity(out @using); diff --git a/Content.Shared/Verbs/VerbEvents.cs b/Content.Shared/Verbs/VerbEvents.cs index a997fb59cb..ed7de92fff 100644 --- a/Content.Shared/Verbs/VerbEvents.cs +++ b/Content.Shared/Verbs/VerbEvents.cs @@ -118,7 +118,7 @@ namespace Content.Shared.Verbs /// The entity currently being held by the active hand. /// /// - /// This is only ever not null when is true and the user + /// This is only ever not null when is true and the user /// has hands. /// public readonly EntityUid? Using; diff --git a/Resources/Locale/en-US/buckle/components/buckle-component.ftl b/Resources/Locale/en-US/buckle/components/buckle-component.ftl index 673fcb3454..32fbaa999a 100644 --- a/Resources/Locale/en-US/buckle/components/buckle-component.ftl +++ b/Resources/Locale/en-US/buckle/components/buckle-component.ftl @@ -1,4 +1,3 @@ -buckle-component-cannot-do-that-message = You can't do that! buckle-component-no-hands-message = You don't have hands! buckle-component-already-buckled-message = You are already buckled in! buckle-component-other-already-buckled-message = {$owner} is already buckled in! diff --git a/Resources/Locale/en-US/components/base-computer-ui-component.ftl b/Resources/Locale/en-US/components/base-computer-ui-component.ftl index dce198278b..8111c16e43 100644 --- a/Resources/Locale/en-US/components/base-computer-ui-component.ftl +++ b/Resources/Locale/en-US/components/base-computer-ui-component.ftl @@ -1,3 +1 @@ -base-computer-ui-component-cannot-interact = You can't interact with a computer right now. -base-computer-ui-component-not-powered = The computer is not powered. - +base-computer-ui-component-not-powered = The computer is not powered. \ No newline at end of file diff --git a/Resources/Locale/en-US/disposal/unit/components/disposal-unit-component.ftl b/Resources/Locale/en-US/disposal/unit/components/disposal-unit-component.ftl index 485f25deef..8ca17cfced 100644 --- a/Resources/Locale/en-US/disposal/unit/components/disposal-unit-component.ftl +++ b/Resources/Locale/en-US/disposal/unit/components/disposal-unit-component.ftl @@ -8,8 +8,4 @@ ui-disposal-unit-label-status = Ready ui-disposal-unit-button-flush = Flush ui-disposal-unit-button-eject = Eject Contents -ui-disposal-unit-button-power = Power - -ui-disposal-unit-is-valid-interaction-cannot=interact = You can't do that! -ui-disposal-unit-is-valid-interaction-cannot-reach = You can't reach there! -ui-disposal-unit-is-valid-interaction-no-hands = You have no hands. \ No newline at end of file +ui-disposal-unit-button-power = Power \ No newline at end of file