Reduce action blocker uses and add target entity to CanInteract (#6655)

This commit is contained in:
Leon Friedrich
2022-02-15 17:06:52 +13:00
committed by GitHub
parent 334568dad2
commit ad9ddf1552
60 changed files with 286 additions and 402 deletions

View File

@@ -5,6 +5,7 @@ using Content.Client.Viewport;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.DragDrop; using Content.Shared.DragDrop;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Interaction.Helpers; using Content.Shared.Interaction.Helpers;
using Content.Shared.Popups; using Content.Shared.Popups;
using JetBrains.Annotations; using JetBrains.Annotations;
@@ -428,11 +429,18 @@ namespace Content.Client.DragDrop
/// <returns>null if the target doesn't support IDragDropOn</returns> /// <returns>null if the target doesn't support IDragDropOn</returns>
private bool? ValidDragDrop(DragDropEvent eventArgs) private bool? ValidDragDrop(DragDropEvent eventArgs)
{ {
if (!_actionBlockerSystem.CanInteract(eventArgs.User)) if (!_actionBlockerSystem.CanInteract(eventArgs.User, eventArgs.Target))
{ {
return false; 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); var valid = CheckDragDropOn(eventArgs);
foreach (var comp in EntityManager.GetComponents<IDragDropOn>(eventArgs.Target)) foreach (var comp in EntityManager.GetComponents<IDragDropOn>(eventArgs.Target))

View File

@@ -172,11 +172,6 @@ namespace Content.Server.AME.Components
if (playerEntity == default) if (playerEntity == default)
return false; return false;
var actionBlocker = EntitySystem.Get<ActionBlockerSystem>();
//Check if player can interact in their current state
if (!actionBlocker.CanInteract(playerEntity) || !actionBlocker.CanUse(playerEntity))
return false;
//Check if device is powered //Check if device is powered
if (needsPower && !Powered) if (needsPower && !Powered)
return false; return false;

View File

@@ -45,7 +45,7 @@ namespace Content.Server.Actions.Spells
return; return;
} }
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(caster)) return; if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(caster, null)) return;
// TODO: Nix when we get EntityPrototype serializers // TODO: Nix when we get EntityPrototype serializers
if (!IoCManager.Resolve<IPrototypeManager>().HasIndex<EntityPrototype>(ItemProto)) if (!IoCManager.Resolve<IPrototypeManager>().HasIndex<EntityPrototype>(ItemProto))

View File

@@ -18,7 +18,7 @@ namespace Content.Server.Alert.Click
{ {
public void AlertClicked(EntityUid player) public void AlertClicked(EntityUid player)
{ {
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(player)) if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(player, null))
return; return;
if (IoCManager.Resolve<IEntityManager>().TryGetComponent<SharedPullableComponent?>(player, out var playerPullable)) if (IoCManager.Resolve<IEntityManager>().TryGetComponent<SharedPullableComponent?>(player, out var playerPullable))

View File

@@ -49,9 +49,6 @@ namespace Content.Server.Arcade.Components
if(!Powered || !IoCManager.Resolve<IEntityManager>().TryGetComponent(eventArgs.User, out ActorComponent? actor)) if(!Powered || !IoCManager.Resolve<IEntityManager>().TryGetComponent(eventArgs.User, out ActorComponent? actor))
return; return;
if(!EntitySystem.Get<ActionBlockerSystem>().CanInteract(eventArgs.User))
return;
UserInterface?.Toggle(actor.PlayerSession); UserInterface?.Toggle(actor.PlayerSession);
if (UserInterface?.SessionHasOpen(actor.PlayerSession) == true) if (UserInterface?.SessionHasOpen(actor.PlayerSession) == true)
{ {
@@ -130,13 +127,6 @@ namespace Content.Server.Arcade.Components
case BlockGameMessages.BlockGamePlayerActionMessage playerActionMessage: case BlockGameMessages.BlockGamePlayerActionMessage playerActionMessage:
if (obj.Session != _player) break; if (obj.Session != _player) break;
// TODO: Should this check if the Owner can interact...?
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(Owner))
{
DeactivePlayer(obj.Session);
break;
}
if (playerActionMessage.PlayerAction == BlockGamePlayerAction.NewGame) if (playerActionMessage.PlayerAction == BlockGamePlayerAction.NewGame)
{ {
if(_game?.Started == true) _game = new BlockGame(this); if(_game?.Started == true) _game = new BlockGame(this);

View File

@@ -77,9 +77,6 @@ namespace Content.Server.Arcade.Components
if (!Powered || !IoCManager.Resolve<IEntityManager>().TryGetComponent(eventArgs.User, out ActorComponent? actor)) if (!Powered || !IoCManager.Resolve<IEntityManager>().TryGetComponent(eventArgs.User, out ActorComponent? actor))
return; return;
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(eventArgs.User))
return;
_game ??= new SpaceVillainGame(this); _game ??= new SpaceVillainGame(this);
if (_entityManager.TryGetComponent<WiresComponent>(Owner, out var wiresComponent) && wiresComponent.IsPanelOpen) if (_entityManager.TryGetComponent<WiresComponent>(Owner, out var wiresComponent) && wiresComponent.IsPanelOpen)

View File

@@ -207,11 +207,6 @@ namespace Content.Server.Atmos.Components
internal void ToggleInternals() internal void ToggleInternals()
{ {
var user = GetInternalsComponent()?.Owner;
if (user == null || !EntitySystem.Get<ActionBlockerSystem>().CanUse(user.Value))
return;
if (IsConnected) if (IsConnected)
{ {
DisconnectFromInternals(); DisconnectFromInternals();
@@ -321,6 +316,9 @@ namespace Content.Server.Atmos.Components
{ {
public bool DoToggleAction(ToggleItemActionEventArgs args) public bool DoToggleAction(ToggleItemActionEventArgs args)
{ {
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(args.Performer, args.Item))
return false;
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent<GasTankComponent?>(args.Item, out var gasTankComponent)) return false; if (!IoCManager.Resolve<IEntityManager>().TryGetComponent<GasTankComponent?>(args.Item, out var gasTankComponent)) return false;
// no change // no change
if (gasTankComponent.IsConnected == args.ToggledOn) return false; if (gasTankComponent.IsConnected == args.ToggledOn) return false;

View File

@@ -181,7 +181,7 @@ namespace Content.Server.Atmos.EntitySystems
if (!Resolve(uid, ref flammable)) if (!Resolve(uid, ref flammable))
return; return;
if (!flammable.OnFire || !_actionBlockerSystem.CanInteract(flammable.Owner) || flammable.Resisting) if (!flammable.OnFire || !_actionBlockerSystem.CanInteract(flammable.Owner, null) || flammable.Resisting)
return; return;
flammable.Resisting = true; flammable.Resisting = true;

View File

@@ -18,10 +18,8 @@ using Robust.Shared.Player;
namespace Content.Server.Atmos.Piping.Binary.EntitySystems namespace Content.Server.Atmos.Piping.Binary.EntitySystems
{ {
[UsedImplicitly] [UsedImplicitly]
public class GasValveSystem : EntitySystem public sealed class GasValveSystem : EntitySystem
{ {
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -51,11 +49,8 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
private void OnActivate(EntityUid uid, GasValveComponent component, ActivateInWorldEvent args) 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) public void Set(EntityUid uid, GasValveComponent component, bool value)

View File

@@ -25,11 +25,10 @@ using Robust.Shared.Players;
namespace Content.Server.Atmos.Piping.Unary.EntitySystems namespace Content.Server.Atmos.Piping.Unary.EntitySystems
{ {
[UsedImplicitly] [UsedImplicitly]
public class GasCanisterSystem : EntitySystem public sealed class GasCanisterSystem : EntitySystem
{ {
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly AdminLogSystem _adminLogSystem = default!; [Dependency] private readonly AdminLogSystem _adminLogSystem = default!;
public override void Initialize() public override void Initialize()

View File

@@ -422,7 +422,7 @@ namespace Content.Server.Botany.Components
public bool DoHarvest(EntityUid user) public bool DoHarvest(EntityUid user)
{ {
if (Seed == null || _entMan.Deleted(user) || !EntitySystem.Get<ActionBlockerSystem>().CanInteract(user)) if (Seed == null || _entMan.Deleted(user))
return false; return false;
var botanySystem = EntitySystem.Get<BotanySystem>(); var botanySystem = EntitySystem.Get<BotanySystem>();
@@ -657,7 +657,7 @@ namespace Content.Server.Botany.Components
var user = eventArgs.User; var user = eventArgs.User;
var usingItem = eventArgs.Using; var usingItem = eventArgs.Using;
if ((!_entMan.EntityExists(usingItem) ? EntityLifeStage.Deleted : _entMan.GetComponent<MetaDataComponent>(usingItem).EntityLifeStage) >= EntityLifeStage.Deleted || !EntitySystem.Get<ActionBlockerSystem>().CanInteract(user)) if ((!_entMan.EntityExists(usingItem) ? EntityLifeStage.Deleted : _entMan.GetComponent<MetaDataComponent>(usingItem).EntityLifeStage) >= EntityLifeStage.Deleted)
return false; return false;
var botanySystem = EntitySystem.Get<BotanySystem>(); var botanySystem = EntitySystem.Get<BotanySystem>();

View File

@@ -138,12 +138,6 @@ namespace Content.Server.Buckle.Components
return false; return false;
} }
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
{
popupSystem.PopupEntity(Loc.GetString("buckle-component-cannot-do-that-message"), user, Filter.Entities(user));
return false;
}
if (!_entMan.TryGetComponent(to, out strap)) if (!_entMan.TryGetComponent(to, out strap))
{ {
return false; return false;
@@ -295,13 +289,6 @@ namespace Content.Server.Buckle.Components
return false; return false;
} }
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
{
var popupSystem = EntitySystem.Get<SharedPopupSystem>();
popupSystem.PopupEntity(Loc.GetString("buckle-component-cannot-do-that-message"), user, Filter.Entities(user));
return false;
}
if (!user.InRangeUnobstructed(oldBuckledTo.Owner, Range, popup: true)) if (!user.InRangeUnobstructed(oldBuckledTo.Owner, Range, popup: true))
{ {
return false; return false;

View File

@@ -162,11 +162,6 @@ namespace Content.Server.Chemistry.Components
if (playerEntity == default) if (playerEntity == default)
return false; return false;
var actionBlocker = EntitySystem.Get<ActionBlockerSystem>();
//Check if player can interact in their current state
if (!actionBlocker.CanInteract(playerEntity) || !actionBlocker.CanUse(playerEntity))
return false;
//Check if device is powered //Check if device is powered
if (needsPower && !Powered) if (needsPower && !Powered)
return false; return false;

View File

@@ -207,11 +207,6 @@ namespace Content.Server.Chemistry.Components
if (playerEntity == null) if (playerEntity == null)
return false; return false;
var actionBlocker = EntitySystem.Get<ActionBlockerSystem>();
//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 //Check if device is powered
if (needsPower && !Powered) if (needsPower && !Powered)
return false; return false;

View File

@@ -6,6 +6,7 @@ using Content.Shared.Body.Components;
using Content.Shared.Body.Part; using Content.Shared.Body.Part;
using Content.Shared.Climbing; using Content.Shared.Climbing;
using Content.Shared.DragDrop; using Content.Shared.DragDrop;
using Content.Shared.Interaction.Events;
using Content.Shared.Interaction.Helpers; using Content.Shared.Interaction.Helpers;
using Content.Shared.Popups; using Content.Shared.Popups;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -70,7 +71,7 @@ namespace Content.Server.Climbing.Components
/// <returns></returns> /// <returns></returns>
private bool CanVault(EntityUid user, EntityUid target, out string reason) private bool CanVault(EntityUid user, EntityUid target, out string reason)
{ {
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user)) if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user, target))
{ {
reason = Loc.GetString("comp-climbable-cant-interact"); reason = Loc.GetString("comp-climbable-cant-interact");
return false; return false;
@@ -110,7 +111,17 @@ namespace Content.Server.Climbing.Components
/// <returns></returns> /// <returns></returns>
private bool CanVault(EntityUid user, EntityUid dragged, EntityUid target, out string reason) private bool CanVault(EntityUid user, EntityUid dragged, EntityUid target, out string reason)
{ {
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user)) if (!EntitySystem.Get<ActionBlockerSystem>().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"); reason = Loc.GetString("comp-climbable-cant-interact");
return false; return false;

View File

@@ -301,7 +301,7 @@ namespace Content.Server.Construction
var pathFind = constructionGraph.Path(startNode.Name, targetNode.Name); var pathFind = constructionGraph.Path(startNode.Name, targetNode.Name);
if (args.SenderSession.AttachedEntity is not {Valid: true} user || if (args.SenderSession.AttachedEntity is not {Valid: true} user ||
!Get<ActionBlockerSystem>().CanInteract(user)) return; !Get<ActionBlockerSystem>().CanInteract(user, null)) return;
if (!EntityManager.TryGetComponent(user, out HandsComponent? hands)) return; if (!EntityManager.TryGetComponent(user, out HandsComponent? hands)) return;
@@ -399,7 +399,7 @@ namespace Content.Server.Construction
_beingBuilt[args.SenderSession].Remove(ev.Ack); _beingBuilt[args.SenderSession].Remove(ev.Ack);
} }
if (!Get<ActionBlockerSystem>().CanInteract(user) if (!Get<ActionBlockerSystem>().CanInteract(user, null)
|| !EntityManager.TryGetComponent(user, out HandsComponent? hands) || hands.GetActiveHandItem == null || !EntityManager.TryGetComponent(user, out HandsComponent? hands) || hands.GetActiveHandItem == null
|| !user.InRangeUnobstructed(ev.Location, ignoreInsideBlocker:constructionPrototype.CanBuildInImpassable)) || !user.InRangeUnobstructed(ev.Location, ignoreInsideBlocker:constructionPrototype.CanBuildInImpassable))
{ {

View File

@@ -149,7 +149,6 @@ namespace Content.Server.Cuffs.Components
if (_cuffing) return true; if (_cuffing) return true;
if (eventArgs.Target is not {Valid: true} target || if (eventArgs.Target is not {Valid: true} target ||
!EntitySystem.Get<ActionBlockerSystem>().CanUse(eventArgs.User) ||
!_entities.TryGetComponent<CuffableComponent?>(eventArgs.Target.Value, out var cuffed)) !_entities.TryGetComponent<CuffableComponent?>(eventArgs.Target.Value, out var cuffed))
{ {
return false; return false;

View File

@@ -83,7 +83,7 @@ namespace Content.Server.Cuffs
else else
{ {
// Check if the user can interact. // Check if the user can interact.
if (!_actionBlockerSystem.CanInteract(args.User)) if (!_actionBlockerSystem.CanInteract(args.User, args.Target))
{ {
args.Cancel(); args.Cancel();
} }

View File

@@ -81,7 +81,7 @@ namespace Content.Server.Disposal.Tube.Components
var msg = (UiActionMessage) obj.Message; var msg = (UiActionMessage) obj.Message;
if (!PlayerCanUseDisposalTagger(obj.Session)) if (!Anchored)
return; return;
//Check for correct message and ignore maleformed strings //Check for correct message and ignore maleformed strings
@@ -96,29 +96,6 @@ namespace Content.Server.Disposal.Tube.Components
} }
} }
/// <summary>
/// Checks whether the player entity is able to use the configuration interface of the pipe tagger.
/// </summary>
/// <param name="IPlayerSession">The player session.</param>
/// <returns>Returns true if the entity can use the configuration interface, and false if it cannot.</returns>
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<ActionBlockerSystem>();
var groupController = IoCManager.Resolve<IConGroupController>();
//Check if player can interact in their current state
if (!groupController.CanAdminMenu(session) && (!actionBlocker.CanInteract(attached) || !actionBlocker.CanUse(attached)))
return false;
return true;
}
/// <summary> /// <summary>
/// Gets component data to be used to update the user interface client-side. /// Gets component data to be used to update the user interface client-side.
/// </summary> /// </summary>

View File

@@ -67,7 +67,7 @@ namespace Content.Server.Disposal.Tube.Components
{ {
var msg = (UiActionMessage) obj.Message; var msg = (UiActionMessage) obj.Message;
if (!PlayerCanUseDisposalTagger(obj.Session)) if (!Anchored)
return; return;
//Check for correct message and ignore maleformed strings //Check for correct message and ignore maleformed strings
@@ -78,28 +78,6 @@ namespace Content.Server.Disposal.Tube.Components
} }
} }
/// <summary>
/// Checks whether the player entity is able to use the configuration interface of the pipe tagger.
/// </summary>
/// <param name="IPlayerSession">The player entity.</param>
/// <returns>Returns true if the entity can use the configuration interface, and false if it cannot.</returns>
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<ActionBlockerSystem>();
var groupController = IoCManager.Resolve<IConGroupController>();
//Check if player can interact in their current state
if (!groupController.CanAdminMenu(session) && (!actionBlocker.CanInteract(attached) || !actionBlocker.CanUse(attached)))
return false;
return true;
}
/// <summary> /// <summary>
/// Gets component data to be used to update the user interface client-side. /// Gets component data to be used to update the user interface client-side.
/// </summary> /// </summary>

View File

@@ -184,11 +184,6 @@ namespace Content.Server.Disposal.Unit.EntitySystems
return; return;
} }
if (!_actionBlockerSystem.CanInteract(player) || !_actionBlockerSystem.CanUse(player))
{
return;
}
switch (args.Button) switch (args.Button)
{ {
case SharedDisposalUnitComponent.UiButton.Eject: case SharedDisposalUnitComponent.UiButton.Eject:
@@ -241,11 +236,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
} }
args.Handled = true; args.Handled = true;
component.Owner.GetUIOrNull(SharedDisposalUnitComponent.DisposalUnitUiKey.Key)?.Open(actor.PlayerSession);
if (IsValidInteraction(args))
{
component.Owner.GetUIOrNull(SharedDisposalUnitComponent.DisposalUnitUiKey.Key)?.Open(actor.PlayerSession);
}
} }
private void HandleAfterInteractUsing(EntityUid uid, DisposalUnitComponent component, AfterInteractUsingEvent args) 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; return state == SharedDisposalUnitComponent.PressureState.Ready && component.RecentlyEjected.Count == 0;
} }
private bool IsValidInteraction(ITargetedInteractEventArgs eventArgs)
{
if (!Get<ActionBlockerSystem>().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<HandsComponent>(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) public bool TryInsert(EntityUid unitId, EntityUid toInsertId, EntityUid userId, DisposalUnitComponent? unit = null)
{ {
if (!Resolve(unitId, ref unit)) if (!Resolve(unitId, ref unit))

View File

@@ -19,7 +19,6 @@ namespace Content.Server.Extinguisher;
public class FireExtinguisherSystem : EntitySystem public class FireExtinguisherSystem : EntitySystem
{ {
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
@@ -145,9 +144,6 @@ public class FireExtinguisherSystem : EntitySystem
if (!Resolve(uid, ref extinguisher)) if (!Resolve(uid, ref extinguisher))
return; return;
if (!_actionBlockerSystem.CanInteract(user) || !extinguisher.HasSafety)
return;
extinguisher.Safety = !extinguisher.Safety; extinguisher.Safety = !extinguisher.Safety;
SoundSystem.Play(Filter.Pvs(uid), extinguisher.SafetySound.GetSound(), uid, SoundSystem.Play(Filter.Pvs(uid), extinguisher.SafetySound.GetSound(), uid,
AudioHelpers.WithVariation(0.125f).WithVolume(-4f)); AudioHelpers.WithVariation(0.125f).WithVolume(-4f));

View File

@@ -100,10 +100,10 @@ namespace Content.Server.Interaction
return; return;
} }
if (!_actionBlockerSystem.CanInteract(userEntity.Value)) if (Deleted(msg.Dropped) || Deleted(msg.Target))
return; return;
if (Deleted(msg.Dropped) || Deleted(msg.Target)) if (!_actionBlockerSystem.CanInteract(userEntity.Value, msg.Target))
return; return;
var interactionArgs = new DragDropEvent(userEntity.Value, msg.DropLocation, msg.Dropped, msg.Target); var interactionArgs = new DragDropEvent(userEntity.Value, msg.DropLocation, msg.Dropped, msg.Target);
@@ -232,13 +232,11 @@ namespace Content.Server.Interaction
/// <summary> /// <summary>
/// Uses an empty hand on an entity /// Uses an empty hand on an entity
/// Finds components with the InteractHand interface and calls their function /// 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.
/// </summary> /// </summary>
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. // 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 // all interactions should only happen when in range / unobstructed, so no range check is needed
var message = new InteractHandEvent(user, target); var message = new InteractHandEvent(user, target);
@@ -260,18 +258,26 @@ namespace Content.Server.Interaction
} }
// Else we run Activate. // Else we run Activate.
InteractionActivate(user, target); InteractionActivate(user, target,
checkCanInteract: false,
checkUseDelay: true,
checkAccess: false);
} }
/// <summary> /// <summary>
/// Will have two behaviors, either "uses" the used entity at range on the target entity if it is capable of accepting that action /// 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 /// Or it will use the used entity itself on the position clicked, regardless of what was there
/// </summary> /// </summary>
public override async Task<bool> 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. // TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction.
if (InteractDoBefore(user, used, target, clickLocation, inRangeUnobstructed)) if (RangedInteractDoBefore(user, used, target, clickLocation, inRangeUnobstructed))
return true; return;
if (target != null) if (target != null)
{ {
@@ -279,10 +285,10 @@ namespace Content.Server.Interaction
RaiseLocalEvent(target.Value, rangedMsg); RaiseLocalEvent(target.Value, rangedMsg);
if (rangedMsg.Handled) 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) public override void DoAttack(EntityUid user, EntityCoordinates coordinates, bool wideAttack, EntityUid? target = null)

View File

@@ -79,21 +79,8 @@ namespace Content.Server.Labels
args.Handled = true; args.Handled = true;
} }
private bool CheckInteract(ICommonSession session)
{
if (session.AttachedEntity is not {Valid: true } uid
|| !Get<ActionBlockerSystem>().CanInteract(uid)
|| !Get<ActionBlockerSystem>().CanUse(uid))
return false;
return true;
}
private void OnHandLabelerLabelChanged(EntityUid uid, HandLabelerComponent handLabeler, HandLabelerLabelChangedMessage args) 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)); handLabeler.AssignedLabel = args.Label.Trim().Substring(0, Math.Min(handLabeler.MaxLabelChars, args.Label.Length));
DirtyUI(uid, handLabeler); DirtyUI(uid, handLabeler);
} }

View File

@@ -1,6 +1,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.Clothing.Components; using Content.Server.Clothing.Components;
using Content.Server.Light.EntitySystems; using Content.Server.Light.EntitySystems;
using Content.Shared.ActionBlocker;
using Content.Shared.Actions.Behaviors.Item; using Content.Shared.Actions.Behaviors.Item;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Interaction; using Content.Shared.Interaction;
@@ -50,10 +51,11 @@ namespace Content.Server.Light.Components
[UsedImplicitly] [UsedImplicitly]
[DataDefinition] [DataDefinition]
public class ToggleLightAction : IToggleItemAction public sealed class ToggleLightAction : IToggleItemAction
{ {
public bool DoToggleAction(ToggleItemActionEventArgs args) public bool DoToggleAction(ToggleItemActionEventArgs args)
{ {
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(args.Performer, args.Item)) return false;
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent<HandheldLightComponent?>(args.Item, out var lightComponent)) return false; if (!IoCManager.Resolve<IEntityManager>().TryGetComponent<HandheldLightComponent?>(args.Item, out var lightComponent)) return false;
if (lightComponent.Activated == args.ToggledOn) return false; if (lightComponent.Activated == args.ToggledOn) return false;
return EntitySystem.Get<HandheldLightSystem>().ToggleStatus(args.Performer, lightComponent); return EntitySystem.Get<HandheldLightSystem>().ToggleStatus(args.Performer, lightComponent);

View File

@@ -28,7 +28,6 @@ namespace Content.Server.Light.EntitySystems
[UsedImplicitly] [UsedImplicitly]
public sealed class HandheldLightSystem : EntitySystem public sealed class HandheldLightSystem : EntitySystem
{ {
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
[Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly PowerCellSystem _powerCell = default!; [Dependency] private readonly PowerCellSystem _powerCell = default!;
@@ -96,7 +95,6 @@ namespace Content.Server.Light.EntitySystems
/// <returns>True if the light's status was toggled, false otherwise.</returns> /// <returns>True if the light's status was toggled, false otherwise.</returns>
public bool ToggleStatus(EntityUid user, HandheldLightComponent component) public bool ToggleStatus(EntityUid user, HandheldLightComponent component)
{ {
if (!_blocker.CanUse(user)) return false;
return component.Activated ? TurnOff(component) : TurnOn(user, component); return component.Activated ? TurnOff(component) : TurnOn(user, component);
} }

View File

@@ -16,9 +16,8 @@ using Robust.Shared.Player;
namespace Content.Server.Light.EntitySystems namespace Content.Server.Light.EntitySystems
{ {
[UsedImplicitly] [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 PoweredLightSystem _poweredLight = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
@@ -42,7 +41,6 @@ namespace Content.Server.Light.EntitySystems
return; return;
// standard interaction checks // standard interaction checks
if (!_blocker.CanUse(eventArgs.User)) return;
if (!eventArgs.CanReach) return; if (!eventArgs.CanReach) return;
// behaviour will depends on target type // behaviour will depends on target type
@@ -64,9 +62,6 @@ namespace Content.Server.Light.EntitySystems
if (eventArgs.Handled) if (eventArgs.Handled)
return; return;
// standard interaction checks
if (!_blocker.CanInteract(eventArgs.User)) return;
var usedUid = eventArgs.Used; var usedUid = eventArgs.Used;
// want to insert a new light bulb? // want to insert a new light bulb?

View File

@@ -75,7 +75,7 @@ namespace Content.Server.Medical
private void OnRelayMovement(EntityUid uid, MedicalScannerComponent component, RelayMovementEntityEvent args) private void OnRelayMovement(EntityUid uid, MedicalScannerComponent component, RelayMovementEntityEvent args)
{ {
if (_blocker.CanInteract(args.Entity)) if (_blocker.CanInteract(args.Entity, null))
{ {
if (_gameTiming.CurTime < if (_gameTiming.CurTime <
component.LastInternalOpenAttempt + MedicalScannerComponent.InternalOpenAttemptDelay) component.LastInternalOpenAttempt + MedicalScannerComponent.InternalOpenAttemptDelay)

View File

@@ -24,7 +24,6 @@ namespace Content.Server.Medical.SuitSensors
{ {
public class SuitSensorSystem : EntitySystem public class SuitSensorSystem : EntitySystem
{ {
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IdCardSystem _idCardSystem = default!; [Dependency] private readonly IdCardSystem _idCardSystem = default!;
@@ -142,7 +141,7 @@ namespace Content.Server.Medical.SuitSensors
return; return;
// standard interaction checks // standard interaction checks
if (!args.CanAccess || !args.CanInteract || !_actionBlockerSystem.CanDrop(args.User)) if (!args.CanAccess || !args.CanInteract)
return; return;
args.Verbs.UnionWith(new[] args.Verbs.UnionWith(new[]

View File

@@ -117,12 +117,6 @@ namespace Content.Server.Nuke
if (args.Handled) if (args.Handled)
return; 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)) if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
return; return;

View File

@@ -40,7 +40,6 @@ namespace Content.Server.Nutrition.EntitySystems
[Dependency] private readonly StomachSystem _stomachSystem = default!; [Dependency] private readonly StomachSystem _stomachSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedAdminLogSystem _logSystem = default!; [Dependency] private readonly SharedAdminLogSystem _logSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly SpillableSystem _spillableSystem = default!; [Dependency] private readonly SpillableSystem _spillableSystem = default!;
public override void Initialize() public override void Initialize()
@@ -111,10 +110,6 @@ namespace Content.Server.Nutrition.EntitySystems
if (args.Handled || args.Target == null || !args.CanReach) if (args.Handled || args.Target == null || !args.CanReach)
return; return;
// CanInteract already checked CanInteract
if (!_actionBlockerSystem.CanUse(args.User))
return;
args.Handled = TryDrink(args.User, args.Target.Value, component); args.Handled = TryDrink(args.User, args.Target.Value, component);
} }
@@ -122,12 +117,6 @@ namespace Content.Server.Nutrition.EntitySystems
{ {
if (args.Handled) return; if (args.Handled) return;
if (!args.User.InRangeUnobstructed(uid, popup: true))
{
args.Handled = true;
return;
}
if (!component.Opened) if (!component.Opened)
{ {
//Do the opening stuff like playing the sounds. //Do the opening stuff like playing the sounds.
@@ -137,10 +126,6 @@ namespace Content.Server.Nutrition.EntitySystems
return; 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); args.Handled = TryDrink(args.User, args.User, component);
} }

View File

@@ -40,7 +40,6 @@ namespace Content.Server.Nutrition.EntitySystems
[Dependency] private readonly UtensilSystem _utensilSystem = default!; [Dependency] private readonly UtensilSystem _utensilSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedAdminLogSystem _logSystem = default!; [Dependency] private readonly SharedAdminLogSystem _logSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!;
public override void Initialize() public override void Initialize()
@@ -79,9 +78,6 @@ namespace Content.Server.Nutrition.EntitySystems
public bool TryFeed(EntityUid user, EntityUid target, FoodComponent food) 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 currently being used to feed, cancel that action.
if (food.CancelToken != null) if (food.CancelToken != null)
{ {

View File

@@ -150,13 +150,6 @@ namespace Content.Server.ParticleAccelerator.Components
return; return;
} }
if (obj.Session.AttachedEntity is not {Valid: true} attached ||
!EntitySystem.Get<ActionBlockerSystem>().CanInteract(attached))
{
return;
}
if (_wireInterfaceBlocked) if (_wireInterfaceBlocked)
{ {
return; return;

View File

@@ -37,9 +37,6 @@ namespace Content.Server.Plants.Systems
if (args.Handled) if (args.Handled)
return; return;
// standard interaction checks
if (!_blocker.CanInteract(args.User)) return;
Rustle(uid, component); Rustle(uid, component);
args.Handled = _stashSystem.TryHideItem(uid, args.User, args.Used); args.Handled = _stashSystem.TryHideItem(uid, args.User, args.Used);
} }
@@ -49,9 +46,6 @@ namespace Content.Server.Plants.Systems
if (args.Handled) if (args.Handled)
return; return;
// standard interaction checks
if (!_blocker.CanInteract(args.User)) return;
Rustle(uid, component); Rustle(uid, component);
var gotItem = _stashSystem.TryGetItem(uid, args.User); var gotItem = _stashSystem.TryGetItem(uid, args.User);

View File

@@ -96,7 +96,7 @@ namespace Content.Server.Shuttles.EntitySystems
{ {
if (comp.Console == null) continue; if (comp.Console == null) continue;
if (!_blocker.CanInteract((comp).Owner)) if (!_blocker.CanInteract(comp.Owner, comp.Console.Owner))
{ {
toRemove.Add(comp); toRemove.Add(comp);
} }
@@ -189,8 +189,7 @@ namespace Content.Server.Shuttles.EntitySystems
public void AddPilot(EntityUid entity, ShuttleConsoleComponent component) public void AddPilot(EntityUid entity, ShuttleConsoleComponent component)
{ {
if (!_blocker.CanInteract(entity) || if (!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent) ||
!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent) ||
component.SubscribedPilots.Contains(pilotComponent)) component.SubscribedPilots.Contains(pilotComponent))
{ {
return; return;

View File

@@ -88,9 +88,6 @@ namespace Content.Server.Strip
bool Check() bool Check()
{ {
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
return false;
if (item == null) if (item == null)
{ {
user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything")); user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything"));
@@ -153,9 +150,6 @@ namespace Content.Server.Strip
bool Check() bool Check()
{ {
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
return false;
if (item == null) if (item == null)
{ {
user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything")); user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything"));
@@ -219,9 +213,6 @@ namespace Content.Server.Strip
bool Check() bool Check()
{ {
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
return false;
if (!invSystem.HasSlot(Owner, slot)) if (!invSystem.HasSlot(Owner, slot))
return false; return false;
@@ -272,9 +263,6 @@ namespace Content.Server.Strip
bool Check() bool Check()
{ {
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
return false;
if (!hands.HasHand(hand)) if (!hands.HasHand(hand))
return false; return false;

View File

@@ -73,9 +73,6 @@ namespace Content.Server.Stunnable
private void OnUseInHand(EntityUid uid, StunbatonComponent comp, UseInHandEvent args) private void OnUseInHand(EntityUid uid, StunbatonComponent comp, UseInHandEvent args)
{ {
if (!Get<ActionBlockerSystem>().CanUse(args.User))
return;
if (comp.Activated) if (comp.Activated)
{ {
TurnOff(comp); TurnOff(comp);

View File

@@ -58,9 +58,7 @@ namespace Content.Server.Tabletop
if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
return; return;
// Check that the entity can interact with the game board. OpenSessionFor(actor.PlayerSession, uid);
if(_actionBlockerSystem.CanInteract(args.User))
OpenSessionFor(actor.PlayerSession, uid);
} }
private void OnGameShutdown(EntityUid uid, TabletopGameComponent component, ComponentShutdown args) private void OnGameShutdown(EntityUid uid, TabletopGameComponent component, ComponentShutdown args)

View File

@@ -102,9 +102,6 @@ namespace Content.Server.Tools
return false; return false;
} }
if (user != null && !_actionBlockerSystem.CanInteract(user.Value))
return false;
solution.RemoveReagent(welder.FuelReagent, welder.FuelLitCost); solution.RemoveReagent(welder.FuelReagent, welder.FuelLitCost);
welder.Lit = true; welder.Lit = true;
@@ -140,9 +137,6 @@ namespace Content.Server.Tools
// Optional components. // Optional components.
Resolve(uid, ref item, ref light, ref sprite); Resolve(uid, ref item, ref light, ref sprite);
if (user != null && !_actionBlockerSystem.CanInteract(user.Value))
return false;
welder.Lit = false; welder.Lit = false;
// TODO: Make all this use visualizers. // TODO: Make all this use visualizers.

View File

@@ -23,7 +23,6 @@ namespace Content.Server.Tools
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
@@ -214,7 +213,7 @@ namespace Content.Server.Tools
if (!Resolve(tool, ref toolComponent)) if (!Resolve(tool, ref toolComponent))
return false; return false;
if (!toolComponent.Qualities.ContainsAll(toolQualitiesNeeded) || !_actionBlockerSystem.CanInteract(user)) if (!toolComponent.Qualities.ContainsAll(toolQualitiesNeeded))
return false; return false;
var beforeAttempt = new ToolUseAttemptEvent(fuel, user); var beforeAttempt = new ToolUseAttemptEvent(fuel, user);

View File

@@ -89,10 +89,6 @@ namespace Content.Server.Traitor.Uplink
if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
return; return;
var actionBlocker = EntitySystem.Get<ActionBlockerSystem>();
if (!actionBlocker.CanInteract(uid) || !actionBlocker.CanUse(uid))
return;
ToggleUplinkUI(component, actor.PlayerSession); ToggleUplinkUI(component, actor.PlayerSession);
args.Handled = true; args.Handled = true;
} }

View File

@@ -18,7 +18,6 @@ namespace Content.Server.UserInterface
[UsedImplicitly] [UsedImplicitly]
internal sealed class ActivatableUISystem : EntitySystem internal sealed class ActivatableUISystem : EntitySystem
{ {
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly IAdminManager _adminManager = default!;
public override void Initialize() public override void Initialize()
@@ -82,12 +81,6 @@ namespace Content.Server.UserInterface
if (aui.AdminOnly && !_adminManager.IsAdmin(actor.PlayerSession)) return false; if (aui.AdminOnly && !_adminManager.IsAdmin(actor.PlayerSession)) return false;
if (!HasComp<GhostComponent>(user) && !_actionBlockerSystem.CanInteract(user))
{
user.PopupMessageCursor(Loc.GetString("base-computer-ui-component-cannot-interact"));
return true;
}
var ui = aui.UserInterface; var ui = aui.UserInterface;
if (ui == null) return false; if (ui == null) return false;

View File

@@ -44,9 +44,6 @@ namespace Content.Server.Weapon.Melee.EnergySword
{ {
if (args.Handled) return; if (args.Handled) return;
if (!_blockerSystem.CanUse(args.User))
return;
args.Handled = true; args.Handled = true;
if (comp.Activated) if (comp.Activated)
@@ -114,7 +111,7 @@ namespace Content.Server.Weapon.Melee.EnergySword
{ {
if (args.Handled) return; if (args.Handled) return;
if (comp.Hacked || !_blockerSystem.CanInteract(args.User)) if (comp.Hacked)
return; return;
if (!TryComp(args.Used, out ToolComponent? tool) || !tool.Qualities.ContainsAny("Pulsing")) return; if (!TryComp(args.Used, out ToolComponent? tool) || !tool.Qualities.ContainsAny("Pulsing")) return;

View File

@@ -40,7 +40,7 @@ public sealed partial class GunSystem
if (!TryComp(user, out CombatModeComponent? combat) || if (!TryComp(user, out CombatModeComponent? combat) ||
!combat.IsInCombatMode || !combat.IsInCombatMode ||
!_blocker.CanInteract(user)) return; !_blocker.CanInteract(user, gun.Owner)) return;
var fireAttempt = new GunFireAttemptEvent(user, gun); var fireAttempt = new GunFireAttemptEvent(user, gun);
EntityManager.EventBus.RaiseLocalEvent(gun.Owner, fireAttempt); EntityManager.EventBus.RaiseLocalEvent(gun.Owner, fireAttempt);

View File

@@ -16,7 +16,7 @@ namespace Content.Shared.ActionBlocker
/// Utility methods to check if a specific entity is allowed to perform an action. /// Utility methods to check if a specific entity is allowed to perform an action.
/// </summary> /// </summary>
[UsedImplicitly] [UsedImplicitly]
public class ActionBlockerSystem : EntitySystem public sealed class ActionBlockerSystem : EntitySystem
{ {
public bool CanMove(EntityUid uid) public bool CanMove(EntityUid uid)
{ {
@@ -26,26 +26,54 @@ namespace Content.Shared.ActionBlocker
return !ev.Cancelled; return !ev.Cancelled;
} }
public bool CanInteract(EntityUid uid) /// <summary>
/// Raises an event directed at both the user and the target entity to check whether a user is capable of
/// interacting with this entity.
/// </summary>
/// <remarks>
/// 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 <see cref="SharedInteractionSystem"/>. 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 <see cref="CanPickup(EntityUid)"/>
/// </remarks>
/// <returns></returns>
public bool CanInteract(EntityUid user, EntityUid? target)
{ {
var ev = new InteractionAttemptEvent(uid); var ev = new InteractionAttemptEvent(user, target);
RaiseLocalEvent(uid, ev); 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;
}
/// <summary>
/// Can a user utilize the entity that they are currently holding in their hands.
/// </summary>>
/// <remarks>
/// This event is automatically checked by <see cref="SharedInteractionSystem"/> 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.
/// </remarks>
public bool CanUseHeldEntity(EntityUid user)
{
var ev = new UseAttemptEvent(user);
RaiseLocalEvent(user, ev);
return !ev.Cancelled; return !ev.Cancelled;
} }
public bool CanUse(EntityUid uid) public bool CanThrow(EntityUid user)
{ {
var ev = new UseAttemptEvent(uid); var ev = new ThrowAttemptEvent(user);
RaiseLocalEvent(uid, ev); RaiseLocalEvent(user, ev);
return !ev.Cancelled;
}
public bool CanThrow(EntityUid uid)
{
var ev = new ThrowAttemptEvent(uid);
RaiseLocalEvent(uid, ev);
return !ev.Cancelled; return !ev.Cancelled;
} }

View File

@@ -278,6 +278,7 @@ namespace Content.Shared.Containers.ItemSlots
/// <summary> /// <summary>
/// Tries to insert item into a specific slot from an entity's hand. /// Tries to insert item into a specific slot from an entity's hand.
/// Does not check action blockers.
/// </summary> /// </summary>
/// <returns>False if failed to insert item</returns> /// <returns>False if failed to insert item</returns>
public bool TryInsertFromHand(EntityUid uid, ItemSlot slot, EntityUid user, SharedHandsComponent? hands = null) public bool TryInsertFromHand(EntityUid uid, ItemSlot slot, EntityUid user, SharedHandsComponent? hands = null)
@@ -293,7 +294,7 @@ namespace Content.Shared.Containers.ItemSlots
return false; return false;
// hands.Drop(item) checks CanDrop action blocker // hands.Drop(item) checks CanDrop action blocker
if (!_actionBlockerSystem.CanInteract(user) && hands.Drop(heldItem)) if (hands.Drop(heldItem))
return false; return false;
Insert(uid, slot, heldItem, user); Insert(uid, slot, heldItem, user);

View File

@@ -561,7 +561,7 @@ namespace Content.Shared.Hands.Components
/// <summary> /// <summary>
/// Attempts to interact with the item in a hand using the active held item. /// Attempts to interact with the item in a hand using the active held item.
/// </summary> /// </summary>
public async void InteractHandWithActiveHand(string handName) public void InteractHandWithActiveHand(string handName)
{ {
if (!TryGetActiveHeldEntity(out var activeHeldEntity)) if (!TryGetActiveHeldEntity(out var activeHeldEntity))
return; return;
@@ -572,7 +572,7 @@ namespace Content.Shared.Hands.Components
if (activeHeldEntity == heldEntity) if (activeHeldEntity == heldEntity)
return; return;
await EntitySystem.Get<SharedInteractionSystem>() EntitySystem.Get<SharedInteractionSystem>()
.InteractUsing(Owner, activeHeldEntity.Value, heldEntity.Value, EntityCoordinates.Invalid); .InteractUsing(Owner, activeHeldEntity.Value, heldEntity.Value, EntityCoordinates.Invalid);
} }
@@ -585,7 +585,7 @@ namespace Content.Shared.Hands.Components
if (altInteract) if (altInteract)
sys.AltInteract(Owner, heldEntity.Value); sys.AltInteract(Owner, heldEntity.Value);
else else
sys.TryUseInteraction(Owner, heldEntity.Value); sys.UseInHandInteraction(Owner, heldEntity.Value);
} }
public void ActivateHeldEntity(string handName) public void ActivateHeldEntity(string handName)
@@ -594,7 +594,7 @@ namespace Content.Shared.Hands.Components
return; return;
EntitySystem.Get<SharedInteractionSystem>() EntitySystem.Get<SharedInteractionSystem>()
.TryInteractionActivate(Owner, heldEntity); .InteractionActivate(Owner, heldEntity.Value);
} }
/// <summary> /// <summary>

View File

@@ -12,7 +12,7 @@ public abstract class SharedHandVirtualItemSystem : EntitySystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<HandVirtualItemComponent, BeingEquippedAttemptEvent>(OnBeingEquippedAttempt); SubscribeLocalEvent<HandVirtualItemComponent, BeingEquippedAttemptEvent>(OnBeingEquippedAttempt);
SubscribeLocalEvent<HandVirtualItemComponent, BeforeInteractEvent>(HandleBeforeInteract); SubscribeLocalEvent<HandVirtualItemComponent, BeforeRangedInteractEvent>(HandleBeforeInteract);
} }
private void OnBeingEquippedAttempt(EntityUid uid, HandVirtualItemComponent component, BeingEquippedAttemptEvent args) private void OnBeingEquippedAttempt(EntityUid uid, HandVirtualItemComponent component, BeingEquippedAttemptEvent args)
@@ -23,7 +23,7 @@ public abstract class SharedHandVirtualItemSystem : EntitySystem
private static void HandleBeforeInteract( private static void HandleBeforeInteract(
EntityUid uid, EntityUid uid,
HandVirtualItemComponent component, HandVirtualItemComponent component,
BeforeInteractEvent args) BeforeRangedInteractEvent args)
{ {
// No interactions with a virtual item, please. // No interactions with a virtual item, please.
args.Handled = true; args.Handled = true;

View File

@@ -8,7 +8,7 @@ namespace Content.Shared.Interaction
/// Raised directed on the used object when clicking on another object before an interaction is handled. /// Raised directed on the used object when clicking on another object before an interaction is handled.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public class BeforeInteractEvent : HandledEntityEventArgs public class BeforeRangedInteractEvent : HandledEntityEventArgs
{ {
/// <summary> /// <summary>
/// Entity that triggered the interaction. /// Entity that triggered the interaction.
@@ -35,7 +35,7 @@ namespace Content.Shared.Interaction
/// </summary> /// </summary>
public bool CanReach { get; } public bool CanReach { get; }
public BeforeInteractEvent( public BeforeRangedInteractEvent(
EntityUid user, EntityUid user,
EntityUid used, EntityUid used,
EntityUid? target, EntityUid? target,

View File

@@ -2,13 +2,34 @@
namespace Content.Shared.Interaction.Events namespace Content.Shared.Interaction.Events
{ {
public class InteractionAttemptEvent : CancellableEntityEventArgs /// <summary>
/// Event raised directed at a user to see if they can perform a generic interaction.
/// </summary>
public sealed class InteractionAttemptEvent : CancellableEntityEventArgs
{ {
public InteractionAttemptEvent(EntityUid uid) public InteractionAttemptEvent(EntityUid uid, EntityUid? target)
{ {
Uid = uid; Uid = uid;
Target = target;
} }
public EntityUid Uid { get; } public EntityUid Uid { get; }
public EntityUid? Target { get; }
}
/// <summary>
/// Event raised directed at the target entity of an interaction to see if the user is allowed to perform some
/// generic interaction.
/// </summary>
public sealed class GettingInteractedWithAttemptEvent : CancellableEntityEventArgs
{
public GettingInteractedWithAttemptEvent(EntityUid uid, EntityUid? target)
{
Uid = uid;
Target = target;
}
public EntityUid Uid { get; }
public EntityUid? Target { get; }
} }
} }

View File

@@ -75,7 +75,7 @@ namespace Content.Shared.Interaction
/// </summary> /// </summary>
private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev) 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(); ev.Cancel();
return; return;
@@ -108,6 +108,11 @@ namespace Content.Shared.Interaction
return; 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) if (msg.AltInteract)
// Use 'UserInteraction' function - behaves as if the user alt-clicked the item in the world. // Use 'UserInteraction' function - behaves as if the user alt-clicked the item in the world.
UserInteraction(user.Value, coords, msg.ItemUid, msg.AltInteract); UserInteraction(user.Value, coords, msg.ItemUid, msg.AltInteract);
@@ -139,7 +144,14 @@ namespace Content.Shared.Interaction
/// <param name="altInteract">Whether to use default or alternative interactions (usually as a result of /// <param name="altInteract">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 /// 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.</param> /// interaction. Having an item in the active hand also disables alternative interactions.</param>
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)) if (target != null && Deleted(target.Value))
return; return;
@@ -154,55 +166,71 @@ namespace Content.Shared.Interaction
if (!ValidateInteractAndFace(user, coordinates)) if (!ValidateInteractAndFace(user, coordinates))
return; 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; return;
// Check if interacted entity is in the same container, the direct child, or direct parent of the user. // 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) // Also checks if the item is accessible via some storage UI (e.g., open backpack)
if (target != null && !ContainerSystem.IsInSameOrParentContainer(user, target.Value) && !CanAccessViaStorage(user, target.Value)) if (checkAccess
&& target != null
&& !ContainerSystem.IsInSameOrParentContainer(user, target.Value)
&& !CanAccessViaStorage(user, target.Value))
return; return;
// Verify user has a hand, and find what object they are currently holding in their active hand // Does the user have hands?
if (!TryComp(user, out SharedHandsComponent? hands)) Hand? hand;
if (!TryComp(user, out SharedHandsComponent? hands) || !hands.TryGetActiveHand(out hand))
return; 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. // TODO: Replace with body interaction range when we get something like arm length or telekinesis or something.
var inRangeUnobstructed = user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true); var inRangeUnobstructed = !checkAccess || user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true);
if (target == null || !inRangeUnobstructed)
// empty-hand interactions
if (hand.HeldEntity == null)
{ {
if (!hands.TryGetActiveHeldEntity(out var heldEntity) || !_actionBlockerSystem.CanUse(user)) if (inRangeUnobstructed && target != null)
return; InteractHand(user, target.Value);
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));
}
return; return;
} }
// We are close to the nearby object. // Can the user use the held entity?
if (altInteract) if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user))
return;
if (inRangeUnobstructed && target != null)
{ {
// Perform alternative interactions, using context menu verbs. InteractUsing(
AltInteract(user, target.Value); user,
} hand.HeldEntity.Value,
else if (!hands.TryGetActiveHeldEntity(out var heldEntity)) target.Value,
{ coordinates,
// Since our hand is empty we will use InteractHand/Activate checkCanInteract: false,
InteractHand(user, target.Value, checkActionBlocker: false); checkCanUse: false);
}
else if (heldEntity != target && _actionBlockerSystem.CanUse(user)) return;
{
await InteractUsing(user, heldEntity.Value, target.Value, coordinates, checkActionBlocker: false);
} }
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. // 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. // TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction.
} }
public virtual async Task<bool> InteractUsingRanged(EntityUid user, EntityUid used, EntityUid? target, public virtual void InteractUsingRanged(EntityUid user, EntityUid used, EntityUid? target,
EntityCoordinates clickLocation, bool inRangeUnobstructed) EntityCoordinates clickLocation, bool inRangeUnobstructed)
{ {
// TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction. // 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) protected bool ValidateInteractAndFace(EntityUid user, EntityCoordinates coordinates)
@@ -548,21 +575,21 @@ namespace Content.Shared.Interaction
if (!inRange && popup) 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); origin.PopupMessage(message);
} }
return inRange; return inRange;
} }
public bool InteractDoBefore( public bool RangedInteractDoBefore(
EntityUid user, EntityUid user,
EntityUid used, EntityUid used,
EntityUid? target, EntityUid? target,
EntityCoordinates clickLocation, EntityCoordinates clickLocation,
bool canReach) bool canReach)
{ {
var ev = new BeforeInteractEvent(user, used, target, clickLocation, canReach); var ev = new BeforeRangedInteractEvent(user, used, target, clickLocation, canReach);
RaiseLocalEvent(used, ev, false); RaiseLocalEvent(used, ev, false);
return ev.Handled; return ev.Handled;
} }
@@ -572,12 +599,22 @@ namespace Content.Shared.Interaction
/// Finds components with the InteractUsing interface and calls their function /// Finds components with the InteractUsing interface and calls their function
/// NOTE: Does not have an InRangeUnobstructed check /// NOTE: Does not have an InRangeUnobstructed check
/// </summary> /// </summary>
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; return;
if (InteractDoBefore(user, used, target, clickLocation, true)) if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user))
return;
if (RangedInteractDoBefore(user, used, target, clickLocation, true))
return; return;
// all interactions should only happen when in range / unobstructed, so no range check is needed // all interactions should only happen when in range / unobstructed, so no range check is needed
@@ -596,13 +633,13 @@ namespace Content.Shared.Interaction
return; return;
} }
await InteractDoAfter(user, used, target, clickLocation, true); InteractDoAfter(user, used, target, clickLocation, canReach: true);
} }
/// <summary> /// <summary>
/// Used when clicking on an entity resulted in no other interaction. Used for low-priority interactions. /// Used when clicking on an entity resulted in no other interaction. Used for low-priority interactions.
/// </summary> /// </summary>
public async Task<bool> 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}) if (target is {Valid: false})
target = null; target = null;
@@ -610,7 +647,7 @@ namespace Content.Shared.Interaction
var afterInteractEvent = new AfterInteractEvent(user, used, target, clickLocation, canReach); var afterInteractEvent = new AfterInteractEvent(user, used, target, clickLocation, canReach);
RaiseLocalEvent(used, afterInteractEvent, false); RaiseLocalEvent(used, afterInteractEvent, false);
if (afterInteractEvent.Handled) if (afterInteractEvent.Handled)
return true; return;
var afterInteractEventArgs = new AfterInteractEventArgs(user, clickLocation, target, canReach); var afterInteractEventArgs = new AfterInteractEventArgs(user, clickLocation, target, canReach);
var afterInteracts = AllComps<IAfterInteract>(used).OrderByDescending(x => x.Priority).ToList(); var afterInteracts = AllComps<IAfterInteract>(used).OrderByDescending(x => x.Priority).ToList();
@@ -618,46 +655,49 @@ namespace Content.Shared.Interaction
foreach (var afterInteract in afterInteracts) foreach (var afterInteract in afterInteracts)
{ {
if (await afterInteract.AfterInteract(afterInteractEventArgs)) if (await afterInteract.AfterInteract(afterInteractEventArgs))
return true; return;
} }
if (target == null) if (target == null)
return false; return;
var afterInteractUsingEvent = new AfterInteractUsingEvent(user, used, target, clickLocation, canReach); var afterInteractUsingEvent = new AfterInteractUsingEvent(user, used, target, clickLocation, canReach);
RaiseLocalEvent(target.Value, afterInteractUsingEvent, false); RaiseLocalEvent(target.Value, afterInteractUsingEvent, false);
return afterInteractEvent.Handled;
} }
#region ActivateItemInWorld #region ActivateItemInWorld
/// <summary> /// <summary>
/// Activates the IActivate behavior of an object /// Raises <see cref="ActivateInWorldEvent"/> events and activates the IActivate behavior of an object.
/// Verifies that the user is capable of doing the use interaction first
/// </summary> /// </summary>
public void TryInteractionActivate(EntityUid? user, EntityUid? used) /// <remarks>
/// Does not check the can-use action blocker. In activations interacts can target entities outside of the users
/// hands.
/// </remarks>
public bool InteractionActivate(
EntityUid user,
EntityUid used,
bool checkCanInteract = true,
bool checkUseDelay = true,
bool checkAccess = true)
{ {
if (user == null || used == null) UseDelayComponent? delayComponent = null;
return; 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 // all activates should only fire when in range / unobstructed
if (!InRangeUnobstructed(user, used, ignoreInsideBlocker: true, popup: true)) if (checkAccess && !InRangeUnobstructed(user, used, ignoreInsideBlocker: true, popup: true))
return; return false;
// Check if interacted entity is in the same container, the direct child, or direct parent of the user. // 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) // This is bypassed IF the interaction happened through an item slot (e.g., backpack UI)
if (!ContainerSystem.IsInSameOrParentContainer(user, used) && !CanAccessViaStorage(user, used)) if (checkAccess && !ContainerSystem.IsInSameOrParentContainer(user, used) && !CanAccessViaStorage(user, used))
return; return false;
var activateMsg = new ActivateInWorldEvent(user, used); var activateMsg = new ActivateInWorldEvent(user, used);
RaiseLocalEvent(used, activateMsg); RaiseLocalEvent(used, activateMsg);
@@ -665,44 +705,47 @@ namespace Content.Shared.Interaction
{ {
BeginDelay(delayComponent); BeginDelay(delayComponent);
_adminLogSystem.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}"); _adminLogSystem.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}");
return; return true;
} }
if (!TryComp(used, out IActivate? activateComp)) if (!TryComp(used, out IActivate? activateComp))
return; return false;
var activateEventArgs = new ActivateEventArgs(user, used); var activateEventArgs = new ActivateEventArgs(user, used);
activateComp.Activate(activateEventArgs); activateComp.Activate(activateEventArgs);
BeginDelay(delayComponent); BeginDelay(delayComponent);
_adminLogSystem.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}"); // No way to check success. _adminLogSystem.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}"); // No way to check success.
return true;
} }
#endregion #endregion
#region Hands #region Hands
#region Use #region Use
/// <summary> /// <summary>
/// Attempt to perform a use-interaction on an entity. If no interaction occurs, it will instead attempt to /// Raises UseInHandEvents and activates the IUse behaviors of an entity
/// activate the entity. /// Does not check accessibility or range, for obvious reasons
/// </summary>
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);
}
/// <summary>
/// Activates the IUse behaviors of an entity without first checking
/// if the user is capable of doing the use interaction.
/// </summary> /// </summary>
/// <returns>True if the interaction was handled. False otherwise</returns> /// <returns>True if the interaction was handled. False otherwise</returns>
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. 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); var useMsg = new UseInHandEvent(user, used);
RaiseLocalEvent(used, useMsg); RaiseLocalEvent(used, useMsg);
if (useMsg.Handled) 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) 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 /// Raised when a player attempts to activate an item in an inventory slot or hand slot
/// </summary> /// </summary>
[Serializable, NetSerializable] [Serializable, NetSerializable]
public class InteractInventorySlotEvent : EntityEventArgs public sealed class InteractInventorySlotEvent : EntityEventArgs
{ {
/// <summary> /// <summary>
/// Entity that was interacted with. /// Entity that was interacted with.

View File

@@ -24,7 +24,7 @@ namespace Content.Shared.Pulling
return false; return false;
} }
if (!_blocker.CanInteract(puller)) if (!_blocker.CanInteract(puller, pulled))
{ {
return false; return false;
} }

View File

@@ -34,7 +34,7 @@ namespace Content.Shared.Storage
bool IDraggable.Drop(DragDropEvent eventArgs) bool IDraggable.Drop(DragDropEvent eventArgs)
{ {
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(eventArgs.User)) if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(eventArgs.User, eventArgs.Target))
{ {
return false; return false;
} }

View File

@@ -15,7 +15,7 @@ namespace Content.Shared.Strip.Components
{ {
return by != Owner return by != Owner
&& IoCManager.Resolve<IEntityManager>().HasComponent<SharedHandsComponent>(@by) && IoCManager.Resolve<IEntityManager>().HasComponent<SharedHandsComponent>(@by)
&& EntitySystem.Get<ActionBlockerSystem>().CanInteract(@by); && EntitySystem.Get<ActionBlockerSystem>().CanInteract(@by, Owner);
} }
bool IDraggable.CanDrop(CanDropEvent args) bool IDraggable.CanDrop(CanDropEvent args)

View File

@@ -49,7 +49,7 @@ namespace Content.Shared.Tabletop
return false; return false;
} }
return playerEntity.InRangeUnobstructed(table.Value) && _actionBlockerSystem.CanInteract(playerEntity); return playerEntity.InRangeUnobstructed(table.Value) && _actionBlockerSystem.CanInteract(playerEntity, table);
} }
protected bool StunnedOrNoHands(EntityUid playerEntity) protected bool StunnedOrNoHands(EntityUid playerEntity)

View File

@@ -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 // 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. // 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; 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); hands.TryGetActiveHeldEntity(out @using);

View File

@@ -118,7 +118,7 @@ namespace Content.Shared.Verbs
/// The entity currently being held by the active hand. /// The entity currently being held by the active hand.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This is only ever not null when <see cref="ActionBlockerSystem.CanUse(EntityUid)"/> is true and the user /// This is only ever not null when <see cref="ActionBlockerSystem.CanUseHeldEntity(EntityUid)"/> is true and the user
/// has hands. /// has hands.
/// </remarks> /// </remarks>
public readonly EntityUid? Using; public readonly EntityUid? Using;

View File

@@ -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-no-hands-message = You don't have hands!
buckle-component-already-buckled-message = You are already buckled in! buckle-component-already-buckled-message = You are already buckled in!
buckle-component-other-already-buckled-message = {$owner} is already buckled in! buckle-component-other-already-buckled-message = {$owner} is already buckled in!

View File

@@ -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.

View File

@@ -8,8 +8,4 @@ ui-disposal-unit-label-status = Ready
ui-disposal-unit-button-flush = Flush ui-disposal-unit-button-flush = Flush
ui-disposal-unit-button-eject = Eject Contents ui-disposal-unit-button-eject = Eject Contents
ui-disposal-unit-button-power = Power 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.