diff --git a/Content.Server/Chemistry/Components/InjectorComponent.cs b/Content.Server/Chemistry/Components/InjectorComponent.cs index eb33144e45..532bf4537b 100644 --- a/Content.Server/Chemistry/Components/InjectorComponent.cs +++ b/Content.Server/Chemistry/Components/InjectorComponent.cs @@ -138,10 +138,7 @@ namespace Content.Server.Chemistry.Components return true; } - if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) - return false; - - if (!EntitySystem.Get().CanInteract(eventArgs.User)) + if (!eventArgs.CanReach) return false; var solutionsSys = EntitySystem.Get(); diff --git a/Content.Server/Chemistry/Components/SolutionTransferComponent.cs b/Content.Server/Chemistry/Components/SolutionTransferComponent.cs index ee7bbe2182..8741f74d71 100644 --- a/Content.Server/Chemistry/Components/SolutionTransferComponent.cs +++ b/Content.Server/Chemistry/Components/SolutionTransferComponent.cs @@ -115,7 +115,7 @@ namespace Content.Server.Chemistry.Components { var solutionsSys = EntitySystem.Get(); - if (!eventArgs.InRangeUnobstructed() || eventArgs.Target == null) + if (!eventArgs.CanReach || eventArgs.Target == null) return false; if (!_entities.HasComponent(Owner)) diff --git a/Content.Server/Construction/AnchorableSystem.cs b/Content.Server/Construction/AnchorableSystem.cs index a6a2e3d020..b62c26f4e0 100644 --- a/Content.Server/Construction/AnchorableSystem.cs +++ b/Content.Server/Construction/AnchorableSystem.cs @@ -33,7 +33,8 @@ namespace Content.Server.Construction if (!EntityManager.TryGetComponent(args.Used, out ToolComponent? usedTool)) return; - args.Handled = await TryToggleAnchor(uid, args.User, args.Used, anchorable, usingTool:usedTool); + args.Handled = true; + await TryToggleAnchor(uid, args.User, args.Used, anchorable, usingTool:usedTool); } /// diff --git a/Content.Server/Crayon/CrayonComponent.cs b/Content.Server/Crayon/CrayonComponent.cs index 8a55b70cd0..2559261f80 100644 --- a/Content.Server/Crayon/CrayonComponent.cs +++ b/Content.Server/Crayon/CrayonComponent.cs @@ -108,7 +108,7 @@ namespace Content.Server.Crayon async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { - if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: false, popup: true, + if (!eventArgs.User.InRangeUnobstructed(eventArgs.ClickLocation, ignoreInsideBlocker: false, popup: true, collisionMask: Shared.Physics.CollisionGroup.MobImpassable)) { return true; diff --git a/Content.Server/Cuffs/Components/HandcuffComponent.cs b/Content.Server/Cuffs/Components/HandcuffComponent.cs index eedbf35fa8..dbc07472e9 100644 --- a/Content.Server/Cuffs/Components/HandcuffComponent.cs +++ b/Content.Server/Cuffs/Components/HandcuffComponent.cs @@ -179,7 +179,7 @@ namespace Content.Server.Cuffs.Components return true; } - if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true)) + if (!eventArgs.CanReach) { eventArgs.User.PopupMessage(Loc.GetString("handcuff-component-too-far-away-error")); return true; diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index df5833d2ca..0fc72fe65b 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -63,8 +63,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems // Interactions SubscribeLocalEvent(HandleActivate); - SubscribeLocalEvent(HandleInteractHand); - SubscribeLocalEvent(HandleInteractUsing); + SubscribeLocalEvent(HandleAfterInteractUsing); SubscribeLocalEvent(HandleDragDropOn); SubscribeLocalEvent(HandleDestruction); @@ -223,19 +222,11 @@ namespace Content.Server.Disposal.Unit.EntitySystems } } - private void HandleInteractHand(EntityUid uid, DisposalUnitComponent component, InteractHandEvent args) + private void HandleAfterInteractUsing(EntityUid uid, DisposalUnitComponent component, AfterInteractUsingEvent args) { - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) return; + if (args.Handled || !args.CanReach) + return; - // Duplicated code here, not sure how else to get actor inside to make UserInterface happy. - - if (!IsValidInteraction(args)) return; - component.Owner.GetUIOrNull(SharedDisposalUnitComponent.DisposalUnitUiKey.Key)?.Open(actor.PlayerSession); - args.Handled = true; - } - - private void HandleInteractUsing(EntityUid uid, DisposalUnitComponent component, InteractUsingEvent args) - { if (!EntityManager.TryGetComponent(args.User, out HandsComponent? hands)) { return; diff --git a/Content.Server/Fluids/Components/MopComponent.cs b/Content.Server/Fluids/Components/MopComponent.cs index b5314af1c0..d601aa60ac 100644 --- a/Content.Server/Fluids/Components/MopComponent.cs +++ b/Content.Server/Fluids/Components/MopComponent.cs @@ -103,14 +103,13 @@ namespace Content.Server.Fluids.Components var solutionSystem = EntitySystem.Get(); var spillableSystem = EntitySystem.Get(); - if (!solutionSystem.TryGetSolution(Owner, SolutionName, out var contents ) || - Mopping || - !eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) + if (!eventArgs.CanReach || + !solutionSystem.TryGetSolution(Owner, SolutionName, out var contents ) || + Mopping) { return false; } - if (eventArgs.Target is not {Valid: true} target) { // Drop the liquid on the mop on to the ground diff --git a/Content.Server/Guardian/GuardianSystem.cs b/Content.Server/Guardian/GuardianSystem.cs index d92dff0604..0a01eedf48 100644 --- a/Content.Server/Guardian/GuardianSystem.cs +++ b/Content.Server/Guardian/GuardianSystem.cs @@ -118,7 +118,7 @@ namespace Content.Server.Guardian private void OnCreatorInteract(EntityUid uid, GuardianCreatorComponent component, AfterInteractEvent args) { - if (args.Handled || args.Target == null) return; + if (args.Handled || args.Target == null || !args.CanReach) return; args.Handled = true; UseCreator(args.User, args.Target.Value, component); } diff --git a/Content.Server/Interaction/InteractionSystem.cs b/Content.Server/Interaction/InteractionSystem.cs index 3292a21440..dbb6cb80cc 100644 --- a/Content.Server/Interaction/InteractionSystem.cs +++ b/Content.Server/Interaction/InteractionSystem.cs @@ -234,10 +234,10 @@ namespace Content.Server.Interaction /// Finds components with the InteractHand interface and calls their function /// NOTE: Does not have an InRangeUnobstructed check /// - public override void InteractHand(EntityUid user, EntityUid target) + public override void InteractHand(EntityUid user, EntityUid target, bool checkActionBlocker = true) { // TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction. - if (!_actionBlockerSystem.CanInteract(user)) + if (checkActionBlocker && !_actionBlockerSystem.CanInteract(user)) return; // all interactions should only happen when in range / unobstructed, so no range check is needed @@ -270,7 +270,7 @@ namespace Content.Server.Interaction public override async Task 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, inRangeUnobstructed ? target : null, clickLocation, false)) + if (InteractDoBefore(user, used, target, clickLocation, inRangeUnobstructed)) return true; if (target != null) @@ -282,7 +282,7 @@ namespace Content.Server.Interaction return true; } - return await InteractDoAfter(user, used, inRangeUnobstructed ? target : null, clickLocation, false); + return await 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 0c2a18ff1a..0288bbad03 100644 --- a/Content.Server/Labels/Label/HandLabelerSystem.cs +++ b/Content.Server/Labels/Label/HandLabelerSystem.cs @@ -34,7 +34,7 @@ namespace Content.Server.Labels private void AfterInteractOn(EntityUid uid, HandLabelerComponent handLabeler, AfterInteractEvent args) { - if (args.Target is not {Valid: true} target || !handLabeler.Whitelist.IsValid(target)) + if (args.Target is not {Valid: true} target || !handLabeler.Whitelist.IsValid(target) || !args.CanReach) return; AddLabelTo(uid, handLabeler, target, out string? result); diff --git a/Content.Server/Medical/Components/HealingComponent.cs b/Content.Server/Medical/Components/HealingComponent.cs index 8ab242b3e6..3450a003fd 100644 --- a/Content.Server/Medical/Components/HealingComponent.cs +++ b/Content.Server/Medical/Components/HealingComponent.cs @@ -35,7 +35,7 @@ namespace Content.Server.Medical.Components async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { - if (eventArgs.Target == null) + if (eventArgs.Target == null || !eventArgs.CanReach) { return false; } @@ -54,12 +54,6 @@ namespace Content.Server.Medical.Components return true; } - if (eventArgs.User != eventArgs.Target && - !eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) - { - return true; - } - if (_entMan.TryGetComponent(Owner, out var stack) && !EntitySystem.Get().Use(Owner, 1, stack)) { return true; diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs index bbfdd7bd69..a4b8d70408 100644 --- a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs @@ -130,30 +130,15 @@ namespace Content.Server.Nutrition.EntitySystems private void AfterInteract(EntityUid uid, DrinkComponent component, AfterInteractEvent args) { - if (args.Handled || args.Target == null) + if (args.Handled || args.Target == null || !args.CanReach) return; - if (!_actionBlockerSystem.CanInteract(args.User) || !_actionBlockerSystem.CanUse(args.User)) - return; - - if (!args.User.InRangeUnobstructed(uid, popup: true)) - { - args.Handled = true; - return; - } - if (args.User == args.Target) { args.Handled = TryUseDrink(uid, args.User); return; } - if (!args.User.InRangeUnobstructed(args.Target.Value, popup: true)) - { - args.Handled = true; - return; - } - if (args.User == args.Target) args.Handled = TryUseDrink(uid, args.User, component); else @@ -164,15 +149,6 @@ namespace Content.Server.Nutrition.EntitySystems { if (args.Handled) return; - if (!_actionBlockerSystem.CanInteract(args.User) || !_actionBlockerSystem.CanUse(args.User)) - return; - - if (!args.User.InRangeUnobstructed(uid, popup: true)) - { - args.Handled = true; - return; - } - if (!component.Opened) { //Do the opening stuff like playing the sounds. diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index 9e4f0e5d60..2b5ea54673 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -77,15 +77,6 @@ namespace Content.Server.Nutrition.EntitySystems if (ev.Handled) return; - if (!_actionBlockerSystem.CanInteract(ev.User) || !_actionBlockerSystem.CanUse(ev.User)) - return; - - if (!ev.User.InRangeUnobstructed(uid, popup: true)) - { - ev.Handled = true; - return; - } - ev.Handled = TryUseFood(uid, ev.User); } @@ -94,30 +85,15 @@ namespace Content.Server.Nutrition.EntitySystems /// private void OnFeedFood(EntityUid uid, FoodComponent foodComponent, AfterInteractEvent args) { - if (args.Handled || args.Target == null) + if (args.Handled || args.Target == null || !args.CanReach) return; - if (!_actionBlockerSystem.CanInteract(args.User) || !_actionBlockerSystem.CanUse(args.User)) - return; - - if (!args.User.InRangeUnobstructed(uid, popup: true)) - { - args.Handled = true; - return; - } - if (args.User == args.Target) { args.Handled = TryUseFood(uid, args.User); return; } - if (!args.User.InRangeUnobstructed(args.Target.Value, popup: true)) - { - args.Handled = true; - return; - } - args.Handled = TryForceFeed(uid, args.User, args.Target.Value); } diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Cigar.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Cigar.cs index 0af7869a11..e88fa6343e 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Cigar.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Cigar.cs @@ -56,6 +56,7 @@ namespace Content.Server.Nutrition.EntitySystems { var targetEntity = args.Target; if (targetEntity == null || + !args.CanReach || !EntityManager.TryGetComponent(uid, out SmokableComponent? smokable) || smokable.State == SmokableState.Lit) return; diff --git a/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs b/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs index 7ab9b73f48..8ab7cc6dfa 100644 --- a/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs @@ -32,7 +32,7 @@ namespace Content.Server.Nutrition.EntitySystems /// private void OnAfterInteract(EntityUid uid, UtensilComponent component, AfterInteractEvent ev) { - if (ev.Target == null) + if (ev.Target == null || !ev.CanReach) return; if (TryUseUtensil(ev.User, ev.Target.Value, component)) diff --git a/Content.Server/Power/EntitySystems/CableSystem.Placer.cs b/Content.Server/Power/EntitySystems/CableSystem.Placer.cs index eec06141ff..e9f2260d5a 100644 --- a/Content.Server/Power/EntitySystems/CableSystem.Placer.cs +++ b/Content.Server/Power/EntitySystems/CableSystem.Placer.cs @@ -16,13 +16,10 @@ public sealed partial class CableSystem private void OnCablePlacerAfterInteract(EntityUid uid, CablePlacerComponent component, AfterInteractEvent args) { - if (args.Handled) return; + if (args.Handled || !args.CanReach) return; if (component.CablePrototypeId == null) return; - if (!args.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) - return; - if(!_mapManager.TryGetGrid(args.ClickLocation.GetGridId(EntityManager), out var grid)) return; diff --git a/Content.Server/RCD/Systems/RCDSystem.cs b/Content.Server/RCD/Systems/RCDSystem.cs index d81db4b99c..dd416299d8 100644 --- a/Content.Server/RCD/Systems/RCDSystem.cs +++ b/Content.Server/RCD/Systems/RCDSystem.cs @@ -25,6 +25,7 @@ namespace Content.Server.RCD.Systems [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; private readonly RcdMode[] _modes = (RcdMode[]) Enum.GetValues(typeof(RcdMode)); @@ -60,7 +61,7 @@ namespace Content.Server.RCD.Systems private async void OnAfterInteract(EntityUid uid, RCDComponent rcd, AfterInteractEvent args) { - if (args.Handled) + if (args.Handled || !args.CanReach) return; // FIXME: Make this work properly. Right now it relies on the click location being on a grid, which is bad. @@ -159,7 +160,8 @@ namespace Content.Server.RCD.Systems } var coordinates = mapGrid.ToCoordinates(tile.GridIndices); - if (coordinates == EntityCoordinates.Invalid || !eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) + if (coordinates == EntityCoordinates.Invalid || + !_interactionSystem.InRangeUnobstructed(eventArgs.User, coordinates, ignoreInsideBlocker: true, popup: true)) { return false; } diff --git a/Content.Server/Storage/Components/ServerStorageComponent.cs b/Content.Server/Storage/Components/ServerStorageComponent.cs index 6975d2ba36..b40c7eb428 100644 --- a/Content.Server/Storage/Components/ServerStorageComponent.cs +++ b/Content.Server/Storage/Components/ServerStorageComponent.cs @@ -558,7 +558,7 @@ namespace Content.Server.Storage.Components /// async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { - if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return false; + if (!eventArgs.CanReach) return false; // Pick up all entities in a radius around the clicked location. // The last half of the if is because carpets exist and this is terrible diff --git a/Content.Server/Tiles/FloorTileItemComponent.cs b/Content.Server/Tiles/FloorTileItemComponent.cs index b240c1be19..ac86f75f97 100644 --- a/Content.Server/Tiles/FloorTileItemComponent.cs +++ b/Content.Server/Tiles/FloorTileItemComponent.cs @@ -56,7 +56,7 @@ namespace Content.Server.Tiles async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { - if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) + if (!eventArgs.CanReach) return true; if (!_entMan.TryGetComponent(Owner, out StackComponent? stack)) diff --git a/Content.Server/Tools/ToolSystem.TilePrying.cs b/Content.Server/Tools/ToolSystem.TilePrying.cs index 076cbab53f..83509d7e9e 100644 --- a/Content.Server/Tools/ToolSystem.TilePrying.cs +++ b/Content.Server/Tools/ToolSystem.TilePrying.cs @@ -25,7 +25,7 @@ public sealed partial class ToolSystem private void OnTilePryingAfterInteract(EntityUid uid, TilePryingComponent component, AfterInteractEvent args) { - if (args.Handled) return; + if (args.Handled || !args.CanReach) return; if (TryPryTile(args.User, component, args.ClickLocation)) args.Handled = true; diff --git a/Content.Server/Traitor/Uplink/Telecrystal/TelecrystalSystem.cs b/Content.Server/Traitor/Uplink/Telecrystal/TelecrystalSystem.cs index e887c567e0..26e06b6915 100644 --- a/Content.Server/Traitor/Uplink/Telecrystal/TelecrystalSystem.cs +++ b/Content.Server/Traitor/Uplink/Telecrystal/TelecrystalSystem.cs @@ -23,7 +23,7 @@ namespace Content.Server.Traitor.Uplink.Telecrystal private void OnAfterInteract(EntityUid uid, TelecrystalComponent component, AfterInteractEvent args) { - if (args.Handled) + if (args.Handled || !args.CanReach) return; if (args.Target == null || !EntityManager.TryGetComponent(args.Target.Value, out UplinkComponent? uplink)) diff --git a/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs index ad44e1146b..8961154554 100644 --- a/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs @@ -215,7 +215,7 @@ namespace Content.Server.Weapon.Melee /// private void OnAfterInteract(EntityUid owner, MeleeWeaponComponent comp, AfterInteractEvent args) { - if (!args.CanReach) + if (args.Handled || !args.CanReach) return; var curTime = _gameTiming.CurTime; diff --git a/Content.Server/Weapon/Ranged/GunSystem.SpeedLoader.cs b/Content.Server/Weapon/Ranged/GunSystem.SpeedLoader.cs index ce19a83953..b71fefbf0b 100644 --- a/Content.Server/Weapon/Ranged/GunSystem.SpeedLoader.cs +++ b/Content.Server/Weapon/Ranged/GunSystem.SpeedLoader.cs @@ -90,7 +90,7 @@ public sealed partial class GunSystem private void OnSpeedLoaderAfterInteract(EntityUid uid, SpeedLoaderComponent component, AfterInteractEvent args) { - if (args.Handled) return; + if (args.Handled || !args.CanReach) return; if (args.Target == null) { diff --git a/Content.Shared/Interaction/BeforeInteract.cs b/Content.Shared/Interaction/BeforeInteract.cs index ed0bcb1c41..c2c5563511 100644 --- a/Content.Shared/Interaction/BeforeInteract.cs +++ b/Content.Shared/Interaction/BeforeInteract.cs @@ -31,8 +31,7 @@ namespace Content.Shared.Interaction public EntityCoordinates ClickLocation { get; } /// - /// Is the click location close enough to reach by the player? This does not check for obstructions, just that the target is within - /// reach radius around the user. + /// Is the click location in range and unobstructed? /// public bool CanReach { get; } diff --git a/Content.Shared/Interaction/Helpers/SharedUnobstructedExtensions.cs b/Content.Shared/Interaction/Helpers/SharedUnobstructedExtensions.cs index ae640e2538..b226e49aaf 100644 --- a/Content.Shared/Interaction/Helpers/SharedUnobstructedExtensions.cs +++ b/Content.Shared/Interaction/Helpers/SharedUnobstructedExtensions.cs @@ -403,24 +403,6 @@ namespace Content.Shared.Interaction.Helpers { return SharedInteractionSystem.InRangeUnobstructed(args.User, args.Target, range, collisionMask, predicate, ignoreInsideBlocker, popup); } - - public static bool InRangeUnobstructed( - this AfterInteractEventArgs args, - float range = InteractionRange, - CollisionGroup collisionMask = CollisionGroup.Impassable, - Ignored? predicate = null, - bool ignoreInsideBlocker = false, - bool popup = false) - { - var user = args.User; - var target = args.Target; - - if (target == null) - return SharedInteractionSystem.InRangeUnobstructed(user, args.ClickLocation, range, collisionMask, predicate, ignoreInsideBlocker, popup); - else - return SharedInteractionSystem.InRangeUnobstructed(user, target.Value, range, collisionMask, predicate, ignoreInsideBlocker, popup); - - } #endregion #region EntityEventArgs @@ -444,23 +426,6 @@ namespace Content.Shared.Interaction.Helpers return true; } - - public static bool InRangeUnobstructed( - this AfterInteractEvent args, - float range = InteractionRange, - CollisionGroup collisionMask = CollisionGroup.Impassable, - Ignored? predicate = null, - bool ignoreInsideBlocker = false, - bool popup = false) - { - var user = args.User; - var target = args.Target; - - if (target == null) - return SharedInteractionSystem.InRangeUnobstructed(user, args.ClickLocation, range, collisionMask, predicate, ignoreInsideBlocker, popup); - else - return SharedInteractionSystem.InRangeUnobstructed(user, target.Value, range, collisionMask, predicate, ignoreInsideBlocker, popup); - } #endregion } } diff --git a/Content.Shared/Interaction/IAfterInteract.cs b/Content.Shared/Interaction/IAfterInteract.cs index 6d85d41f6c..c578d60666 100644 --- a/Content.Shared/Interaction/IAfterInteract.cs +++ b/Content.Shared/Interaction/IAfterInteract.cs @@ -45,11 +45,8 @@ namespace Content.Shared.Interaction } } - /// - /// Raised directed on the used object when clicking on another object and no attack event was handled. - /// [PublicAPI] - public class AfterInteractEvent : HandledEntityEventArgs + public abstract class InteractEvent : HandledEntityEventArgs { /// /// Entity that triggered the interaction. @@ -62,7 +59,7 @@ namespace Content.Shared.Interaction public EntityUid Used { get; } /// - /// Entity that was interacted on. This can be null if the attack did not click on an entity. + /// Entity that was interacted on. This can be null if there was no target (e.g., clicking on tiles). /// public EntityUid? Target { get; } @@ -72,12 +69,11 @@ namespace Content.Shared.Interaction public EntityCoordinates ClickLocation { get; } /// - /// Is the click location close enough to reach by the player? This does not check for obstructions, just that the target is within - /// reach radius around the user. + /// Is the click location in range without obstructions? /// public bool CanReach { get; } - public AfterInteractEvent(EntityUid user, EntityUid used, EntityUid? target, + public InteractEvent(EntityUid user, EntityUid used, EntityUid? target, EntityCoordinates clickLocation, bool canReach) { User = user; @@ -87,4 +83,26 @@ namespace Content.Shared.Interaction CanReach = canReach; } } + + /// + /// Raised directed on the used object when clicking on another object and no standard interaction occurred. + /// Used for low-priority interactions facilitated by the used entity. + /// + public sealed class AfterInteractEvent : InteractEvent + { + public AfterInteractEvent(EntityUid user, EntityUid used, EntityUid? target, + EntityCoordinates clickLocation, bool canReach) : base(user, used, target, clickLocation, canReach) + { } + } + + /// + /// Raised directed on the target when clicking on another object and no standard interaction occurred. Used for + /// low-priority interactions facilitated by the target entity. + /// + public sealed class AfterInteractUsingEvent : InteractEvent + { + public AfterInteractUsingEvent(EntityUid user, EntityUid used, EntityUid? target, + EntityCoordinates clickLocation, bool canReach) : base(user, used, target, clickLocation, canReach) + { } + } } diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 7f5269b739..783d7a7c03 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -26,6 +26,7 @@ using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Players; using Robust.Shared.Serialization; +using Robust.Shared.Player; #pragma warning disable 618 @@ -42,6 +43,7 @@ namespace Content.Shared.Interaction [Dependency] private readonly SharedVerbSystem _verbSystem = default!; [Dependency] private readonly SharedAdminLogSystem _adminLogSystem = default!; [Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] protected readonly SharedContainerSystem ContainerSystem = default!; public const float InteractionRange = 2; @@ -166,14 +168,16 @@ namespace Content.Shared.Interaction var inRangeUnobstructed = user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true); if (target == null || !inRangeUnobstructed) { - if (!hands.TryGetActiveHeldEntity(out var heldEntity)) + if (!hands.TryGetActiveHeldEntity(out var heldEntity) || !_actionBlockerSystem.CanUse(user)) return; - if (!await InteractUsingRanged(user, heldEntity.Value, target, coordinates, inRangeUnobstructed) && - !inRangeUnobstructed) + 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) { - var message = Loc.GetString("interaction-system-user-interaction-cannot-reach"); - user.PopupMessage(message); + _popupSystem.PopupCursor(Loc.GetString("interaction-system-user-interaction-cannot-reach"), Filter.Entities(user)); } return; @@ -188,15 +192,15 @@ namespace Content.Shared.Interaction else if (!hands.TryGetActiveHeldEntity(out var heldEntity)) { // Since our hand is empty we will use InteractHand/Activate - InteractHand(user, target.Value); + InteractHand(user, target.Value, checkActionBlocker: false); } - else if (heldEntity != target) + else if (heldEntity != target && _actionBlockerSystem.CanUse(user)) { - await InteractUsing(user, heldEntity.Value, target.Value, coordinates); + await InteractUsing(user, heldEntity.Value, target.Value, coordinates, checkActionBlocker: false); } } - public virtual void InteractHand(EntityUid user, EntityUid target) + public virtual void InteractHand(EntityUid user, EntityUid target, bool checkActionBlocker = true) { // TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction. } @@ -561,9 +565,9 @@ 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) + public async Task InteractUsing(EntityUid user, EntityUid used, EntityUid target, EntityCoordinates clickLocation, bool predicted = false, bool checkActionBlocker = true) { - if (!_actionBlockerSystem.CanInteract(user)) + if (checkActionBlocker && (!_actionBlockerSystem.CanInteract(user) || !_actionBlockerSystem.CanUse(user))) return; if (InteractDoBefore(user, used, target, clickLocation, true)) @@ -585,12 +589,11 @@ namespace Content.Shared.Interaction return; } - // If we aren't directly interacting with the nearby object, lets see if our item has an after interact we can do await InteractDoAfter(user, used, target, clickLocation, true); } /// - /// We didn't click on any entity, try doing an AfterInteract on the click location + /// 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) { @@ -611,7 +614,12 @@ namespace Content.Shared.Interaction return true; } - return false; + if (target == null) + return false; + + var afterInteractUsingEvent = new AfterInteractUsingEvent(user, used, target, clickLocation, canReach); + RaiseLocalEvent(target.Value, afterInteractUsingEvent, false); + return afterInteractEvent.Handled; } #region ActivateItemInWorld diff --git a/Content.Shared/Placeable/PlaceableSurfaceSystem.cs b/Content.Shared/Placeable/PlaceableSurfaceSystem.cs index aa1b174ccb..36127e59d8 100644 --- a/Content.Shared/Placeable/PlaceableSurfaceSystem.cs +++ b/Content.Shared/Placeable/PlaceableSurfaceSystem.cs @@ -13,7 +13,7 @@ namespace Content.Shared.Placeable { base.Initialize(); - SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnAfterInteractUsing); SubscribeLocalEvent(OnHandleState); } @@ -44,9 +44,9 @@ namespace Content.Shared.Placeable surface.Dirty(); } - private void OnInteractUsing(EntityUid uid, PlaceableSurfaceComponent surface, InteractUsingEvent args) + private void OnAfterInteractUsing(EntityUid uid, PlaceableSurfaceComponent surface, AfterInteractUsingEvent args) { - if (args.Handled) + if (args.Handled || !args.CanReach) return; if (!surface.IsPlaceable) @@ -55,14 +55,11 @@ namespace Content.Shared.Placeable if(!EntityManager.TryGetComponent(args.User, out var handComponent)) return; - if (!args.ClickLocation.IsValid(EntityManager)) - return; - if(!handComponent.TryDropEntity(args.Used, EntityManager.GetComponent(surface.Owner).Coordinates)) return; if (surface.PlaceCentered) - EntityManager.GetComponent(args.Used).LocalPosition = EntityManager.GetComponent(args.Target).LocalPosition + surface.PositionOffset; + EntityManager.GetComponent(args.Used).LocalPosition = EntityManager.GetComponent(uid).LocalPosition + surface.PositionOffset; else EntityManager.GetComponent(args.Used).Coordinates = args.ClickLocation;