Reduce action blocker uses and add target entity to CanInteract (#6655)
This commit is contained in:
@@ -5,6 +5,7 @@ using Content.Client.Viewport;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.DragDrop;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Interaction.Helpers;
|
||||
using Content.Shared.Popups;
|
||||
using JetBrains.Annotations;
|
||||
@@ -428,11 +429,18 @@ namespace Content.Client.DragDrop
|
||||
/// <returns>null if the target doesn't support IDragDropOn</returns>
|
||||
private bool? ValidDragDrop(DragDropEvent eventArgs)
|
||||
{
|
||||
if (!_actionBlockerSystem.CanInteract(eventArgs.User))
|
||||
if (!_actionBlockerSystem.CanInteract(eventArgs.User, eventArgs.Target))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// CanInteract() doesn't support checking a second "target" entity.
|
||||
// Doing so manually:
|
||||
var ev = new GettingInteractedWithAttemptEvent(eventArgs.User, eventArgs.Dragged);
|
||||
RaiseLocalEvent(eventArgs.Dragged, ev);
|
||||
if (ev.Cancelled)
|
||||
return false;
|
||||
|
||||
var valid = CheckDragDropOn(eventArgs);
|
||||
|
||||
foreach (var comp in EntityManager.GetComponents<IDragDropOn>(eventArgs.Target))
|
||||
|
||||
@@ -172,11 +172,6 @@ namespace Content.Server.AME.Components
|
||||
if (playerEntity == default)
|
||||
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
|
||||
if (needsPower && !Powered)
|
||||
return false;
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Content.Server.Actions.Spells
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(caster)) return;
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(caster, null)) return;
|
||||
|
||||
// TODO: Nix when we get EntityPrototype serializers
|
||||
if (!IoCManager.Resolve<IPrototypeManager>().HasIndex<EntityPrototype>(ItemProto))
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Content.Server.Alert.Click
|
||||
{
|
||||
public void AlertClicked(EntityUid player)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(player))
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(player, null))
|
||||
return;
|
||||
|
||||
if (IoCManager.Resolve<IEntityManager>().TryGetComponent<SharedPullableComponent?>(player, out var playerPullable))
|
||||
|
||||
@@ -49,9 +49,6 @@ namespace Content.Server.Arcade.Components
|
||||
if(!Powered || !IoCManager.Resolve<IEntityManager>().TryGetComponent(eventArgs.User, out ActorComponent? actor))
|
||||
return;
|
||||
|
||||
if(!EntitySystem.Get<ActionBlockerSystem>().CanInteract(eventArgs.User))
|
||||
return;
|
||||
|
||||
UserInterface?.Toggle(actor.PlayerSession);
|
||||
if (UserInterface?.SessionHasOpen(actor.PlayerSession) == true)
|
||||
{
|
||||
@@ -130,13 +127,6 @@ namespace Content.Server.Arcade.Components
|
||||
case BlockGameMessages.BlockGamePlayerActionMessage playerActionMessage:
|
||||
if (obj.Session != _player) break;
|
||||
|
||||
// TODO: Should this check if the Owner can interact...?
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(Owner))
|
||||
{
|
||||
DeactivePlayer(obj.Session);
|
||||
break;
|
||||
}
|
||||
|
||||
if (playerActionMessage.PlayerAction == BlockGamePlayerAction.NewGame)
|
||||
{
|
||||
if(_game?.Started == true) _game = new BlockGame(this);
|
||||
|
||||
@@ -77,9 +77,6 @@ namespace Content.Server.Arcade.Components
|
||||
if (!Powered || !IoCManager.Resolve<IEntityManager>().TryGetComponent(eventArgs.User, out ActorComponent? actor))
|
||||
return;
|
||||
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(eventArgs.User))
|
||||
return;
|
||||
|
||||
_game ??= new SpaceVillainGame(this);
|
||||
|
||||
if (_entityManager.TryGetComponent<WiresComponent>(Owner, out var wiresComponent) && wiresComponent.IsPanelOpen)
|
||||
|
||||
@@ -207,11 +207,6 @@ namespace Content.Server.Atmos.Components
|
||||
|
||||
internal void ToggleInternals()
|
||||
{
|
||||
var user = GetInternalsComponent()?.Owner;
|
||||
|
||||
if (user == null || !EntitySystem.Get<ActionBlockerSystem>().CanUse(user.Value))
|
||||
return;
|
||||
|
||||
if (IsConnected)
|
||||
{
|
||||
DisconnectFromInternals();
|
||||
@@ -321,6 +316,9 @@ namespace Content.Server.Atmos.Components
|
||||
{
|
||||
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;
|
||||
// no change
|
||||
if (gasTankComponent.IsConnected == args.ToggledOn) return false;
|
||||
|
||||
@@ -181,7 +181,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if (!Resolve(uid, ref flammable))
|
||||
return;
|
||||
|
||||
if (!flammable.OnFire || !_actionBlockerSystem.CanInteract(flammable.Owner) || flammable.Resisting)
|
||||
if (!flammable.OnFire || !_actionBlockerSystem.CanInteract(flammable.Owner, null) || flammable.Resisting)
|
||||
return;
|
||||
|
||||
flammable.Resisting = true;
|
||||
|
||||
@@ -18,10 +18,8 @@ using Robust.Shared.Player;
|
||||
namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class GasValveSystem : EntitySystem
|
||||
public sealed class GasValveSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -51,11 +49,8 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
|
||||
private void OnActivate(EntityUid uid, GasValveComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (args.User.InRangeUnobstructed(args.Target) && _actionBlockerSystem.CanInteract(args.User))
|
||||
{
|
||||
Toggle(uid, component);
|
||||
SoundSystem.Play(Filter.Pvs(component.Owner), component.ValveSound.GetSound(), component.Owner, AudioHelpers.WithVariation(0.25f));
|
||||
}
|
||||
Toggle(uid, component);
|
||||
SoundSystem.Play(Filter.Pvs(component.Owner), component.ValveSound.GetSound(), component.Owner, AudioHelpers.WithVariation(0.25f));
|
||||
}
|
||||
|
||||
public void Set(EntityUid uid, GasValveComponent component, bool value)
|
||||
|
||||
@@ -25,11 +25,10 @@ using Robust.Shared.Players;
|
||||
namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class GasCanisterSystem : EntitySystem
|
||||
public sealed class GasCanisterSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly AdminLogSystem _adminLogSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
|
||||
@@ -422,7 +422,7 @@ namespace Content.Server.Botany.Components
|
||||
|
||||
public bool DoHarvest(EntityUid user)
|
||||
{
|
||||
if (Seed == null || _entMan.Deleted(user) || !EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
if (Seed == null || _entMan.Deleted(user))
|
||||
return false;
|
||||
|
||||
var botanySystem = EntitySystem.Get<BotanySystem>();
|
||||
@@ -657,7 +657,7 @@ namespace Content.Server.Botany.Components
|
||||
var user = eventArgs.User;
|
||||
var usingItem = eventArgs.Using;
|
||||
|
||||
if ((!_entMan.EntityExists(usingItem) ? EntityLifeStage.Deleted : _entMan.GetComponent<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;
|
||||
|
||||
var botanySystem = EntitySystem.Get<BotanySystem>();
|
||||
|
||||
@@ -138,12 +138,6 @@ namespace Content.Server.Buckle.Components
|
||||
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))
|
||||
{
|
||||
return false;
|
||||
@@ -295,13 +289,6 @@ namespace Content.Server.Buckle.Components
|
||||
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))
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -162,11 +162,6 @@ namespace Content.Server.Chemistry.Components
|
||||
if (playerEntity == default)
|
||||
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
|
||||
if (needsPower && !Powered)
|
||||
return false;
|
||||
|
||||
@@ -207,11 +207,6 @@ namespace Content.Server.Chemistry.Components
|
||||
if (playerEntity == null)
|
||||
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
|
||||
if (needsPower && !Powered)
|
||||
return false;
|
||||
|
||||
@@ -6,6 +6,7 @@ using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Part;
|
||||
using Content.Shared.Climbing;
|
||||
using Content.Shared.DragDrop;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Interaction.Helpers;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -70,7 +71,7 @@ namespace Content.Server.Climbing.Components
|
||||
/// <returns></returns>
|
||||
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");
|
||||
return false;
|
||||
@@ -110,7 +111,17 @@ namespace Content.Server.Climbing.Components
|
||||
/// <returns></returns>
|
||||
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");
|
||||
return false;
|
||||
|
||||
@@ -301,7 +301,7 @@ namespace Content.Server.Construction
|
||||
var pathFind = constructionGraph.Path(startNode.Name, targetNode.Name);
|
||||
|
||||
if (args.SenderSession.AttachedEntity is not {Valid: true} user ||
|
||||
!Get<ActionBlockerSystem>().CanInteract(user)) return;
|
||||
!Get<ActionBlockerSystem>().CanInteract(user, null)) return;
|
||||
|
||||
if (!EntityManager.TryGetComponent(user, out HandsComponent? hands)) return;
|
||||
|
||||
@@ -399,7 +399,7 @@ namespace Content.Server.Construction
|
||||
_beingBuilt[args.SenderSession].Remove(ev.Ack);
|
||||
}
|
||||
|
||||
if (!Get<ActionBlockerSystem>().CanInteract(user)
|
||||
if (!Get<ActionBlockerSystem>().CanInteract(user, null)
|
||||
|| !EntityManager.TryGetComponent(user, out HandsComponent? hands) || hands.GetActiveHandItem == null
|
||||
|| !user.InRangeUnobstructed(ev.Location, ignoreInsideBlocker:constructionPrototype.CanBuildInImpassable))
|
||||
{
|
||||
|
||||
@@ -149,7 +149,6 @@ namespace Content.Server.Cuffs.Components
|
||||
if (_cuffing) return true;
|
||||
|
||||
if (eventArgs.Target is not {Valid: true} target ||
|
||||
!EntitySystem.Get<ActionBlockerSystem>().CanUse(eventArgs.User) ||
|
||||
!_entities.TryGetComponent<CuffableComponent?>(eventArgs.Target.Value, out var cuffed))
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Content.Server.Cuffs
|
||||
else
|
||||
{
|
||||
// Check if the user can interact.
|
||||
if (!_actionBlockerSystem.CanInteract(args.User))
|
||||
if (!_actionBlockerSystem.CanInteract(args.User, args.Target))
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace Content.Server.Disposal.Tube.Components
|
||||
|
||||
var msg = (UiActionMessage) obj.Message;
|
||||
|
||||
if (!PlayerCanUseDisposalTagger(obj.Session))
|
||||
if (!Anchored)
|
||||
return;
|
||||
|
||||
//Check for correct message and ignore maleformed strings
|
||||
@@ -96,29 +96,6 @@ namespace Content.Server.Disposal.Tube.Components
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Gets component data to be used to update the user interface client-side.
|
||||
/// </summary>
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Content.Server.Disposal.Tube.Components
|
||||
{
|
||||
var msg = (UiActionMessage) obj.Message;
|
||||
|
||||
if (!PlayerCanUseDisposalTagger(obj.Session))
|
||||
if (!Anchored)
|
||||
return;
|
||||
|
||||
//Check for correct message and ignore maleformed strings
|
||||
@@ -78,28 +78,6 @@ namespace Content.Server.Disposal.Tube.Components
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Gets component data to be used to update the user interface client-side.
|
||||
/// </summary>
|
||||
|
||||
@@ -184,11 +184,6 @@ namespace Content.Server.Disposal.Unit.EntitySystems
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_actionBlockerSystem.CanInteract(player) || !_actionBlockerSystem.CanUse(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args.Button)
|
||||
{
|
||||
case SharedDisposalUnitComponent.UiButton.Eject:
|
||||
@@ -241,11 +236,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
if (IsValidInteraction(args))
|
||||
{
|
||||
component.Owner.GetUIOrNull(SharedDisposalUnitComponent.DisposalUnitUiKey.Key)?.Open(actor.PlayerSession);
|
||||
}
|
||||
component.Owner.GetUIOrNull(SharedDisposalUnitComponent.DisposalUnitUiKey.Key)?.Open(actor.PlayerSession);
|
||||
}
|
||||
|
||||
private void HandleAfterInteractUsing(EntityUid uid, DisposalUnitComponent component, AfterInteractUsingEvent args)
|
||||
@@ -439,30 +430,6 @@ namespace Content.Server.Disposal.Unit.EntitySystems
|
||||
return state == SharedDisposalUnitComponent.PressureState.Ready && component.RecentlyEjected.Count == 0;
|
||||
}
|
||||
|
||||
private bool IsValidInteraction(ITargetedInteractEventArgs eventArgs)
|
||||
{
|
||||
if (!Get<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)
|
||||
{
|
||||
if (!Resolve(unitId, ref unit))
|
||||
|
||||
@@ -19,7 +19,6 @@ namespace Content.Server.Extinguisher;
|
||||
|
||||
public class FireExtinguisherSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
|
||||
@@ -145,9 +144,6 @@ public class FireExtinguisherSystem : EntitySystem
|
||||
if (!Resolve(uid, ref extinguisher))
|
||||
return;
|
||||
|
||||
if (!_actionBlockerSystem.CanInteract(user) || !extinguisher.HasSafety)
|
||||
return;
|
||||
|
||||
extinguisher.Safety = !extinguisher.Safety;
|
||||
SoundSystem.Play(Filter.Pvs(uid), extinguisher.SafetySound.GetSound(), uid,
|
||||
AudioHelpers.WithVariation(0.125f).WithVolume(-4f));
|
||||
|
||||
@@ -100,10 +100,10 @@ namespace Content.Server.Interaction
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_actionBlockerSystem.CanInteract(userEntity.Value))
|
||||
if (Deleted(msg.Dropped) || Deleted(msg.Target))
|
||||
return;
|
||||
|
||||
if (Deleted(msg.Dropped) || Deleted(msg.Target))
|
||||
if (!_actionBlockerSystem.CanInteract(userEntity.Value, msg.Target))
|
||||
return;
|
||||
|
||||
var interactionArgs = new DragDropEvent(userEntity.Value, msg.DropLocation, msg.Dropped, msg.Target);
|
||||
@@ -232,13 +232,11 @@ namespace Content.Server.Interaction
|
||||
/// <summary>
|
||||
/// Uses an empty hand on an entity
|
||||
/// Finds components with the InteractHand interface and calls their function
|
||||
/// NOTE: Does not have an InRangeUnobstructed check
|
||||
/// NOTE: Does not have any range or can-interact checks. These should all have been done before this function is called.
|
||||
/// </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.
|
||||
if (checkActionBlocker && !_actionBlockerSystem.CanInteract(user))
|
||||
return;
|
||||
|
||||
// all interactions should only happen when in range / unobstructed, so no range check is needed
|
||||
var message = new InteractHandEvent(user, target);
|
||||
@@ -260,18 +258,26 @@ namespace Content.Server.Interaction
|
||||
}
|
||||
|
||||
// Else we run Activate.
|
||||
InteractionActivate(user, target);
|
||||
InteractionActivate(user, target,
|
||||
checkCanInteract: false,
|
||||
checkUseDelay: true,
|
||||
checkAccess: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </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.
|
||||
if (InteractDoBefore(user, used, target, clickLocation, inRangeUnobstructed))
|
||||
return true;
|
||||
if (RangedInteractDoBefore(user, used, target, clickLocation, inRangeUnobstructed))
|
||||
return;
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
@@ -279,10 +285,10 @@ namespace Content.Server.Interaction
|
||||
RaiseLocalEvent(target.Value, rangedMsg);
|
||||
|
||||
if (rangedMsg.Handled)
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
return await InteractDoAfter(user, used, target, clickLocation, inRangeUnobstructed);
|
||||
InteractDoAfter(user, used, target, clickLocation, inRangeUnobstructed);
|
||||
}
|
||||
|
||||
public override void DoAttack(EntityUid user, EntityCoordinates coordinates, bool wideAttack, EntityUid? target = null)
|
||||
|
||||
@@ -79,21 +79,8 @@ namespace Content.Server.Labels
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private bool CheckInteract(ICommonSession session)
|
||||
{
|
||||
if (session.AttachedEntity is not {Valid: true } uid
|
||||
|| !Get<ActionBlockerSystem>().CanInteract(uid)
|
||||
|| !Get<ActionBlockerSystem>().CanUse(uid))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnHandLabelerLabelChanged(EntityUid uid, HandLabelerComponent handLabeler, HandLabelerLabelChangedMessage args)
|
||||
{
|
||||
if (!CheckInteract(args.Session))
|
||||
return;
|
||||
|
||||
handLabeler.AssignedLabel = args.Label.Trim().Substring(0, Math.Min(handLabeler.MaxLabelChars, args.Label.Length));
|
||||
DirtyUI(uid, handLabeler);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Clothing.Components;
|
||||
using Content.Server.Light.EntitySystems;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Actions.Behaviors.Item;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
@@ -50,10 +51,11 @@ namespace Content.Server.Light.Components
|
||||
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public class ToggleLightAction : IToggleItemAction
|
||||
public sealed class ToggleLightAction : IToggleItemAction
|
||||
{
|
||||
public bool DoToggleAction(ToggleItemActionEventArgs args)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(args.Performer, args.Item)) return false;
|
||||
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent<HandheldLightComponent?>(args.Item, out var lightComponent)) return false;
|
||||
if (lightComponent.Activated == args.ToggledOn) return false;
|
||||
return EntitySystem.Get<HandheldLightSystem>().ToggleStatus(args.Performer, lightComponent);
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace Content.Server.Light.EntitySystems
|
||||
[UsedImplicitly]
|
||||
public sealed class HandheldLightSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||
|
||||
@@ -96,7 +95,6 @@ namespace Content.Server.Light.EntitySystems
|
||||
/// <returns>True if the light's status was toggled, false otherwise.</returns>
|
||||
public bool ToggleStatus(EntityUid user, HandheldLightComponent component)
|
||||
{
|
||||
if (!_blocker.CanUse(user)) return false;
|
||||
return component.Activated ? TurnOff(component) : TurnOn(user, component);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,8 @@ using Robust.Shared.Player;
|
||||
namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class LightReplacerSystem : EntitySystem
|
||||
public sealed class LightReplacerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||
[Dependency] private readonly PoweredLightSystem _poweredLight = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
|
||||
@@ -42,7 +41,6 @@ namespace Content.Server.Light.EntitySystems
|
||||
return;
|
||||
|
||||
// standard interaction checks
|
||||
if (!_blocker.CanUse(eventArgs.User)) return;
|
||||
if (!eventArgs.CanReach) return;
|
||||
|
||||
// behaviour will depends on target type
|
||||
@@ -64,9 +62,6 @@ namespace Content.Server.Light.EntitySystems
|
||||
if (eventArgs.Handled)
|
||||
return;
|
||||
|
||||
// standard interaction checks
|
||||
if (!_blocker.CanInteract(eventArgs.User)) return;
|
||||
|
||||
var usedUid = eventArgs.Used;
|
||||
|
||||
// want to insert a new light bulb?
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Content.Server.Medical
|
||||
|
||||
private void OnRelayMovement(EntityUid uid, MedicalScannerComponent component, RelayMovementEntityEvent args)
|
||||
{
|
||||
if (_blocker.CanInteract(args.Entity))
|
||||
if (_blocker.CanInteract(args.Entity, null))
|
||||
{
|
||||
if (_gameTiming.CurTime <
|
||||
component.LastInternalOpenAttempt + MedicalScannerComponent.InternalOpenAttemptDelay)
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace Content.Server.Medical.SuitSensors
|
||||
{
|
||||
public class SuitSensorSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IdCardSystem _idCardSystem = default!;
|
||||
@@ -142,7 +141,7 @@ namespace Content.Server.Medical.SuitSensors
|
||||
return;
|
||||
|
||||
// standard interaction checks
|
||||
if (!args.CanAccess || !args.CanInteract || !_actionBlockerSystem.CanDrop(args.User))
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
args.Verbs.UnionWith(new[]
|
||||
|
||||
@@ -117,12 +117,6 @@ namespace Content.Server.Nuke
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
// standard interactions check
|
||||
if (!args.InRangeUnobstructed())
|
||||
return;
|
||||
if (!_actionBlocker.CanInteract(args.User) || !_actionBlocker.CanUse(args.User))
|
||||
return;
|
||||
|
||||
if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
|
||||
return;
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
[Dependency] private readonly StomachSystem _stomachSystem = default!;
|
||||
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SharedAdminLogSystem _logSystem = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly SpillableSystem _spillableSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
@@ -111,10 +110,6 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
if (args.Handled || args.Target == null || !args.CanReach)
|
||||
return;
|
||||
|
||||
// CanInteract already checked CanInteract
|
||||
if (!_actionBlockerSystem.CanUse(args.User))
|
||||
return;
|
||||
|
||||
args.Handled = TryDrink(args.User, args.Target.Value, component);
|
||||
}
|
||||
|
||||
@@ -122,12 +117,6 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
{
|
||||
if (args.Handled) return;
|
||||
|
||||
if (!args.User.InRangeUnobstructed(uid, popup: true))
|
||||
{
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!component.Opened)
|
||||
{
|
||||
//Do the opening stuff like playing the sounds.
|
||||
@@ -137,10 +126,6 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
return;
|
||||
}
|
||||
|
||||
// CanUse already checked; trying to keep it consistent if we interact with ourselves.
|
||||
if (!_actionBlockerSystem.CanInteract(args.User))
|
||||
return;
|
||||
|
||||
args.Handled = TryDrink(args.User, args.User, component);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
[Dependency] private readonly UtensilSystem _utensilSystem = default!;
|
||||
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SharedAdminLogSystem _logSystem = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
@@ -79,9 +78,6 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
|
||||
public bool TryFeed(EntityUid user, EntityUid target, FoodComponent food)
|
||||
{
|
||||
if (!_actionBlockerSystem.CanInteract(user) || !_actionBlockerSystem.CanUse(user))
|
||||
return false;
|
||||
|
||||
// if currently being used to feed, cancel that action.
|
||||
if (food.CancelToken != null)
|
||||
{
|
||||
|
||||
@@ -150,13 +150,6 @@ namespace Content.Server.ParticleAccelerator.Components
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (obj.Session.AttachedEntity is not {Valid: true} attached ||
|
||||
!EntitySystem.Get<ActionBlockerSystem>().CanInteract(attached))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_wireInterfaceBlocked)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -37,9 +37,6 @@ namespace Content.Server.Plants.Systems
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
// standard interaction checks
|
||||
if (!_blocker.CanInteract(args.User)) return;
|
||||
|
||||
Rustle(uid, component);
|
||||
args.Handled = _stashSystem.TryHideItem(uid, args.User, args.Used);
|
||||
}
|
||||
@@ -49,9 +46,6 @@ namespace Content.Server.Plants.Systems
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
// standard interaction checks
|
||||
if (!_blocker.CanInteract(args.User)) return;
|
||||
|
||||
Rustle(uid, component);
|
||||
|
||||
var gotItem = _stashSystem.TryGetItem(uid, args.User);
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace Content.Server.Shuttles.EntitySystems
|
||||
{
|
||||
if (comp.Console == null) continue;
|
||||
|
||||
if (!_blocker.CanInteract((comp).Owner))
|
||||
if (!_blocker.CanInteract(comp.Owner, comp.Console.Owner))
|
||||
{
|
||||
toRemove.Add(comp);
|
||||
}
|
||||
@@ -189,8 +189,7 @@ namespace Content.Server.Shuttles.EntitySystems
|
||||
|
||||
public void AddPilot(EntityUid entity, ShuttleConsoleComponent component)
|
||||
{
|
||||
if (!_blocker.CanInteract(entity) ||
|
||||
!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent) ||
|
||||
if (!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent) ||
|
||||
component.SubscribedPilots.Contains(pilotComponent))
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -88,9 +88,6 @@ namespace Content.Server.Strip
|
||||
|
||||
bool Check()
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
return false;
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything"));
|
||||
@@ -153,9 +150,6 @@ namespace Content.Server.Strip
|
||||
|
||||
bool Check()
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
return false;
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything"));
|
||||
@@ -219,9 +213,6 @@ namespace Content.Server.Strip
|
||||
|
||||
bool Check()
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
return false;
|
||||
|
||||
if (!invSystem.HasSlot(Owner, slot))
|
||||
return false;
|
||||
|
||||
@@ -272,9 +263,6 @@ namespace Content.Server.Strip
|
||||
|
||||
bool Check()
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
return false;
|
||||
|
||||
if (!hands.HasHand(hand))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -73,9 +73,6 @@ namespace Content.Server.Stunnable
|
||||
|
||||
private void OnUseInHand(EntityUid uid, StunbatonComponent comp, UseInHandEvent args)
|
||||
{
|
||||
if (!Get<ActionBlockerSystem>().CanUse(args.User))
|
||||
return;
|
||||
|
||||
if (comp.Activated)
|
||||
{
|
||||
TurnOff(comp);
|
||||
|
||||
@@ -58,9 +58,7 @@ namespace Content.Server.Tabletop
|
||||
if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
|
||||
return;
|
||||
|
||||
// Check that the entity can interact with the game board.
|
||||
if(_actionBlockerSystem.CanInteract(args.User))
|
||||
OpenSessionFor(actor.PlayerSession, uid);
|
||||
OpenSessionFor(actor.PlayerSession, uid);
|
||||
}
|
||||
|
||||
private void OnGameShutdown(EntityUid uid, TabletopGameComponent component, ComponentShutdown args)
|
||||
|
||||
@@ -102,9 +102,6 @@ namespace Content.Server.Tools
|
||||
return false;
|
||||
}
|
||||
|
||||
if (user != null && !_actionBlockerSystem.CanInteract(user.Value))
|
||||
return false;
|
||||
|
||||
solution.RemoveReagent(welder.FuelReagent, welder.FuelLitCost);
|
||||
|
||||
welder.Lit = true;
|
||||
@@ -140,9 +137,6 @@ namespace Content.Server.Tools
|
||||
// Optional components.
|
||||
Resolve(uid, ref item, ref light, ref sprite);
|
||||
|
||||
if (user != null && !_actionBlockerSystem.CanInteract(user.Value))
|
||||
return false;
|
||||
|
||||
welder.Lit = false;
|
||||
|
||||
// TODO: Make all this use visualizers.
|
||||
|
||||
@@ -23,7 +23,6 @@ namespace Content.Server.Tools
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
@@ -214,7 +213,7 @@ namespace Content.Server.Tools
|
||||
if (!Resolve(tool, ref toolComponent))
|
||||
return false;
|
||||
|
||||
if (!toolComponent.Qualities.ContainsAll(toolQualitiesNeeded) || !_actionBlockerSystem.CanInteract(user))
|
||||
if (!toolComponent.Qualities.ContainsAll(toolQualitiesNeeded))
|
||||
return false;
|
||||
|
||||
var beforeAttempt = new ToolUseAttemptEvent(fuel, user);
|
||||
|
||||
@@ -89,10 +89,6 @@ namespace Content.Server.Traitor.Uplink
|
||||
if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
|
||||
return;
|
||||
|
||||
var actionBlocker = EntitySystem.Get<ActionBlockerSystem>();
|
||||
if (!actionBlocker.CanInteract(uid) || !actionBlocker.CanUse(uid))
|
||||
return;
|
||||
|
||||
ToggleUplinkUI(component, actor.PlayerSession);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace Content.Server.UserInterface
|
||||
[UsedImplicitly]
|
||||
internal sealed class ActivatableUISystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
@@ -82,12 +81,6 @@ namespace Content.Server.UserInterface
|
||||
|
||||
if (aui.AdminOnly && !_adminManager.IsAdmin(actor.PlayerSession)) return false;
|
||||
|
||||
if (!HasComp<GhostComponent>(user) && !_actionBlockerSystem.CanInteract(user))
|
||||
{
|
||||
user.PopupMessageCursor(Loc.GetString("base-computer-ui-component-cannot-interact"));
|
||||
return true;
|
||||
}
|
||||
|
||||
var ui = aui.UserInterface;
|
||||
if (ui == null) return false;
|
||||
|
||||
|
||||
@@ -44,9 +44,6 @@ namespace Content.Server.Weapon.Melee.EnergySword
|
||||
{
|
||||
if (args.Handled) return;
|
||||
|
||||
if (!_blockerSystem.CanUse(args.User))
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
if (comp.Activated)
|
||||
@@ -114,7 +111,7 @@ namespace Content.Server.Weapon.Melee.EnergySword
|
||||
{
|
||||
if (args.Handled) return;
|
||||
|
||||
if (comp.Hacked || !_blockerSystem.CanInteract(args.User))
|
||||
if (comp.Hacked)
|
||||
return;
|
||||
|
||||
if (!TryComp(args.Used, out ToolComponent? tool) || !tool.Qualities.ContainsAny("Pulsing")) return;
|
||||
|
||||
@@ -40,7 +40,7 @@ public sealed partial class GunSystem
|
||||
|
||||
if (!TryComp(user, out CombatModeComponent? combat) ||
|
||||
!combat.IsInCombatMode ||
|
||||
!_blocker.CanInteract(user)) return;
|
||||
!_blocker.CanInteract(user, gun.Owner)) return;
|
||||
|
||||
var fireAttempt = new GunFireAttemptEvent(user, gun);
|
||||
EntityManager.EventBus.RaiseLocalEvent(gun.Owner, fireAttempt);
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Content.Shared.ActionBlocker
|
||||
/// Utility methods to check if a specific entity is allowed to perform an action.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class ActionBlockerSystem : EntitySystem
|
||||
public sealed class ActionBlockerSystem : EntitySystem
|
||||
{
|
||||
public bool CanMove(EntityUid uid)
|
||||
{
|
||||
@@ -26,26 +26,54 @@ namespace Content.Shared.ActionBlocker
|
||||
return !ev.Cancelled;
|
||||
}
|
||||
|
||||
public bool CanInteract(EntityUid uid)
|
||||
/// <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);
|
||||
RaiseLocalEvent(uid, ev);
|
||||
var ev = new InteractionAttemptEvent(user, target);
|
||||
RaiseLocalEvent(user, ev);
|
||||
|
||||
if (ev.Cancelled)
|
||||
return false;
|
||||
|
||||
if (target == null)
|
||||
return true;
|
||||
|
||||
var targetEv = new GettingInteractedWithAttemptEvent(user, target);
|
||||
RaiseLocalEvent(target.Value, targetEv);
|
||||
|
||||
return !targetEv.Cancelled;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
public bool CanUse(EntityUid uid)
|
||||
public bool CanThrow(EntityUid user)
|
||||
{
|
||||
var ev = new UseAttemptEvent(uid);
|
||||
RaiseLocalEvent(uid, ev);
|
||||
|
||||
return !ev.Cancelled;
|
||||
}
|
||||
|
||||
public bool CanThrow(EntityUid uid)
|
||||
{
|
||||
var ev = new ThrowAttemptEvent(uid);
|
||||
RaiseLocalEvent(uid, ev);
|
||||
var ev = new ThrowAttemptEvent(user);
|
||||
RaiseLocalEvent(user, ev);
|
||||
|
||||
return !ev.Cancelled;
|
||||
}
|
||||
|
||||
@@ -278,6 +278,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
|
||||
/// <summary>
|
||||
/// Tries to insert item into a specific slot from an entity's hand.
|
||||
/// Does not check action blockers.
|
||||
/// </summary>
|
||||
/// <returns>False if failed to insert item</returns>
|
||||
public bool TryInsertFromHand(EntityUid uid, ItemSlot slot, EntityUid user, SharedHandsComponent? hands = null)
|
||||
@@ -293,7 +294,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
return false;
|
||||
|
||||
// hands.Drop(item) checks CanDrop action blocker
|
||||
if (!_actionBlockerSystem.CanInteract(user) && hands.Drop(heldItem))
|
||||
if (hands.Drop(heldItem))
|
||||
return false;
|
||||
|
||||
Insert(uid, slot, heldItem, user);
|
||||
|
||||
@@ -561,7 +561,7 @@ namespace Content.Shared.Hands.Components
|
||||
/// <summary>
|
||||
/// Attempts to interact with the item in a hand using the active held item.
|
||||
/// </summary>
|
||||
public async void InteractHandWithActiveHand(string handName)
|
||||
public void InteractHandWithActiveHand(string handName)
|
||||
{
|
||||
if (!TryGetActiveHeldEntity(out var activeHeldEntity))
|
||||
return;
|
||||
@@ -572,7 +572,7 @@ namespace Content.Shared.Hands.Components
|
||||
if (activeHeldEntity == heldEntity)
|
||||
return;
|
||||
|
||||
await EntitySystem.Get<SharedInteractionSystem>()
|
||||
EntitySystem.Get<SharedInteractionSystem>()
|
||||
.InteractUsing(Owner, activeHeldEntity.Value, heldEntity.Value, EntityCoordinates.Invalid);
|
||||
}
|
||||
|
||||
@@ -585,7 +585,7 @@ namespace Content.Shared.Hands.Components
|
||||
if (altInteract)
|
||||
sys.AltInteract(Owner, heldEntity.Value);
|
||||
else
|
||||
sys.TryUseInteraction(Owner, heldEntity.Value);
|
||||
sys.UseInHandInteraction(Owner, heldEntity.Value);
|
||||
}
|
||||
|
||||
public void ActivateHeldEntity(string handName)
|
||||
@@ -594,7 +594,7 @@ namespace Content.Shared.Hands.Components
|
||||
return;
|
||||
|
||||
EntitySystem.Get<SharedInteractionSystem>()
|
||||
.TryInteractionActivate(Owner, heldEntity);
|
||||
.InteractionActivate(Owner, heldEntity.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -12,7 +12,7 @@ public abstract class SharedHandVirtualItemSystem : EntitySystem
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<HandVirtualItemComponent, BeingEquippedAttemptEvent>(OnBeingEquippedAttempt);
|
||||
SubscribeLocalEvent<HandVirtualItemComponent, BeforeInteractEvent>(HandleBeforeInteract);
|
||||
SubscribeLocalEvent<HandVirtualItemComponent, BeforeRangedInteractEvent>(HandleBeforeInteract);
|
||||
}
|
||||
|
||||
private void OnBeingEquippedAttempt(EntityUid uid, HandVirtualItemComponent component, BeingEquippedAttemptEvent args)
|
||||
@@ -23,7 +23,7 @@ public abstract class SharedHandVirtualItemSystem : EntitySystem
|
||||
private static void HandleBeforeInteract(
|
||||
EntityUid uid,
|
||||
HandVirtualItemComponent component,
|
||||
BeforeInteractEvent args)
|
||||
BeforeRangedInteractEvent args)
|
||||
{
|
||||
// No interactions with a virtual item, please.
|
||||
args.Handled = true;
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Content.Shared.Interaction
|
||||
/// Raised directed on the used object when clicking on another object before an interaction is handled.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public class BeforeInteractEvent : HandledEntityEventArgs
|
||||
public class BeforeRangedInteractEvent : HandledEntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity that triggered the interaction.
|
||||
@@ -35,7 +35,7 @@ namespace Content.Shared.Interaction
|
||||
/// </summary>
|
||||
public bool CanReach { get; }
|
||||
|
||||
public BeforeInteractEvent(
|
||||
public BeforeRangedInteractEvent(
|
||||
EntityUid user,
|
||||
EntityUid used,
|
||||
EntityUid? target,
|
||||
|
||||
@@ -2,13 +2,34 @@
|
||||
|
||||
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;
|
||||
Target = target;
|
||||
}
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Content.Shared.Interaction
|
||||
/// </summary>
|
||||
private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev)
|
||||
{
|
||||
if (ev.Sender.AttachedEntity is not EntityUid user || !_actionBlockerSystem.CanInteract(user))
|
||||
if (ev.Sender.AttachedEntity is not EntityUid user || !_actionBlockerSystem.CanInteract(user, ev.Target))
|
||||
{
|
||||
ev.Cancel();
|
||||
return;
|
||||
@@ -108,6 +108,11 @@ namespace Content.Shared.Interaction
|
||||
return;
|
||||
}
|
||||
|
||||
// We won't bother to check that the target item is ACTUALLY in an inventory slot. UserInteraction() and
|
||||
// InteractionActivate() should check that the item is accessible. So.. if a user wants to lie about an
|
||||
// in-reach item being used in a slot... that should have no impact. This is functionally the same as if
|
||||
// they had somehow directly clicked on that item.
|
||||
|
||||
if (msg.AltInteract)
|
||||
// Use 'UserInteraction' function - behaves as if the user alt-clicked the item in the world.
|
||||
UserInteraction(user.Value, coords, msg.ItemUid, msg.AltInteract);
|
||||
@@ -139,7 +144,14 @@ namespace Content.Shared.Interaction
|
||||
/// <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
|
||||
/// 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))
|
||||
return;
|
||||
@@ -154,55 +166,71 @@ namespace Content.Shared.Interaction
|
||||
if (!ValidateInteractAndFace(user, coordinates))
|
||||
return;
|
||||
|
||||
if (!_actionBlockerSystem.CanInteract(user))
|
||||
if (altInteract && target != null)
|
||||
{
|
||||
// Perform alternative interactions, using context menu verbs.
|
||||
// These perform their own range, can-interact, and accessibility checks.
|
||||
AltInteract(user, target.Value);
|
||||
}
|
||||
|
||||
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target))
|
||||
return;
|
||||
|
||||
// Check if interacted entity is in the same container, the direct child, or direct parent of the user.
|
||||
// This is bypassed IF the interaction happened through an item slot (e.g., backpack UI)
|
||||
if (target != null && !ContainerSystem.IsInSameOrParentContainer(user, target.Value) && !CanAccessViaStorage(user, target.Value))
|
||||
// Also checks if the item is accessible via some storage UI (e.g., open backpack)
|
||||
if (checkAccess
|
||||
&& target != null
|
||||
&& !ContainerSystem.IsInSameOrParentContainer(user, target.Value)
|
||||
&& !CanAccessViaStorage(user, target.Value))
|
||||
return;
|
||||
|
||||
// Verify user has a hand, and find what object they are currently holding in their active hand
|
||||
if (!TryComp(user, out SharedHandsComponent? hands))
|
||||
// Does the user have hands?
|
||||
Hand? hand;
|
||||
if (!TryComp(user, out SharedHandsComponent? hands) || !hands.TryGetActiveHand(out hand))
|
||||
return;
|
||||
|
||||
// ^ In future, things like looking at a UI & opening doors (i.e., Activate interactions) shouldn't neccesarily require hands.
|
||||
// But that would first involve some work with BUIs & making sure other activate-interactions check hands if they are required.
|
||||
|
||||
// Check range
|
||||
// TODO: Replace with body interaction range when we get something like arm length or telekinesis or something.
|
||||
var inRangeUnobstructed = user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true);
|
||||
if (target == null || !inRangeUnobstructed)
|
||||
var inRangeUnobstructed = !checkAccess || user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true);
|
||||
|
||||
// empty-hand interactions
|
||||
if (hand.HeldEntity == null)
|
||||
{
|
||||
if (!hands.TryGetActiveHeldEntity(out var heldEntity) || !_actionBlockerSystem.CanUse(user))
|
||||
return;
|
||||
|
||||
if (await InteractUsingRanged(user, heldEntity.Value, target, coordinates, inRangeUnobstructed))
|
||||
return;
|
||||
|
||||
// Generate popup only if user actually tried to click on something.
|
||||
if (!inRangeUnobstructed && target != null)
|
||||
{
|
||||
_popupSystem.PopupCursor(Loc.GetString("interaction-system-user-interaction-cannot-reach"), Filter.Entities(user));
|
||||
}
|
||||
if (inRangeUnobstructed && target != null)
|
||||
InteractHand(user, target.Value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We are close to the nearby object.
|
||||
if (altInteract)
|
||||
// Can the user use the held entity?
|
||||
if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user))
|
||||
return;
|
||||
|
||||
if (inRangeUnobstructed && target != null)
|
||||
{
|
||||
// Perform alternative interactions, using context menu verbs.
|
||||
AltInteract(user, target.Value);
|
||||
}
|
||||
else if (!hands.TryGetActiveHeldEntity(out var heldEntity))
|
||||
{
|
||||
// Since our hand is empty we will use InteractHand/Activate
|
||||
InteractHand(user, target.Value, checkActionBlocker: false);
|
||||
}
|
||||
else if (heldEntity != target && _actionBlockerSystem.CanUse(user))
|
||||
{
|
||||
await InteractUsing(user, heldEntity.Value, target.Value, coordinates, checkActionBlocker: false);
|
||||
InteractUsing(
|
||||
user,
|
||||
hand.HeldEntity.Value,
|
||||
target.Value,
|
||||
coordinates,
|
||||
checkCanInteract: false,
|
||||
checkCanUse: false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
InteractUsingRanged(
|
||||
user,
|
||||
hand.HeldEntity.Value,
|
||||
target,
|
||||
coordinates,
|
||||
inRangeUnobstructed);
|
||||
}
|
||||
|
||||
public virtual void InteractHand(EntityUid user, EntityUid target, bool checkActionBlocker = true)
|
||||
public virtual void InteractHand(EntityUid user, EntityUid target)
|
||||
{
|
||||
// TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction.
|
||||
}
|
||||
@@ -213,11 +241,10 @@ namespace Content.Shared.Interaction
|
||||
// TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction.
|
||||
}
|
||||
|
||||
public virtual async Task<bool> InteractUsingRanged(EntityUid user, EntityUid used, EntityUid? target,
|
||||
public virtual void InteractUsingRanged(EntityUid user, EntityUid used, EntityUid? target,
|
||||
EntityCoordinates clickLocation, bool inRangeUnobstructed)
|
||||
{
|
||||
// TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction.
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
|
||||
protected bool ValidateInteractAndFace(EntityUid user, EntityCoordinates coordinates)
|
||||
@@ -548,21 +575,21 @@ namespace Content.Shared.Interaction
|
||||
|
||||
if (!inRange && popup)
|
||||
{
|
||||
var message = Loc.GetString("shared-interaction-system-in-range-unobstructed-cannot-reach");
|
||||
var message = Loc.GetString("interaction-system-user-interaction-cannot-reach");
|
||||
origin.PopupMessage(message);
|
||||
}
|
||||
|
||||
return inRange;
|
||||
}
|
||||
|
||||
public bool InteractDoBefore(
|
||||
public bool RangedInteractDoBefore(
|
||||
EntityUid user,
|
||||
EntityUid used,
|
||||
EntityUid? target,
|
||||
EntityCoordinates clickLocation,
|
||||
bool canReach)
|
||||
{
|
||||
var ev = new BeforeInteractEvent(user, used, target, clickLocation, canReach);
|
||||
var ev = new BeforeRangedInteractEvent(user, used, target, clickLocation, canReach);
|
||||
RaiseLocalEvent(used, ev, false);
|
||||
return ev.Handled;
|
||||
}
|
||||
@@ -572,12 +599,22 @@ namespace Content.Shared.Interaction
|
||||
/// Finds components with the InteractUsing interface and calls their function
|
||||
/// NOTE: Does not have an InRangeUnobstructed check
|
||||
/// </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;
|
||||
|
||||
if (InteractDoBefore(user, used, target, clickLocation, true))
|
||||
if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user))
|
||||
return;
|
||||
|
||||
if (RangedInteractDoBefore(user, used, target, clickLocation, true))
|
||||
return;
|
||||
|
||||
// all interactions should only happen when in range / unobstructed, so no range check is needed
|
||||
@@ -596,13 +633,13 @@ namespace Content.Shared.Interaction
|
||||
return;
|
||||
}
|
||||
|
||||
await InteractDoAfter(user, used, target, clickLocation, true);
|
||||
InteractDoAfter(user, used, target, clickLocation, canReach: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used when clicking on an entity resulted in no other interaction. Used for low-priority interactions.
|
||||
/// </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})
|
||||
target = null;
|
||||
@@ -610,7 +647,7 @@ namespace Content.Shared.Interaction
|
||||
var afterInteractEvent = new AfterInteractEvent(user, used, target, clickLocation, canReach);
|
||||
RaiseLocalEvent(used, afterInteractEvent, false);
|
||||
if (afterInteractEvent.Handled)
|
||||
return true;
|
||||
return;
|
||||
|
||||
var afterInteractEventArgs = new AfterInteractEventArgs(user, clickLocation, target, canReach);
|
||||
var afterInteracts = AllComps<IAfterInteract>(used).OrderByDescending(x => x.Priority).ToList();
|
||||
@@ -618,46 +655,49 @@ namespace Content.Shared.Interaction
|
||||
foreach (var afterInteract in afterInteracts)
|
||||
{
|
||||
if (await afterInteract.AfterInteract(afterInteractEventArgs))
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (target == null)
|
||||
return false;
|
||||
return;
|
||||
|
||||
var afterInteractUsingEvent = new AfterInteractUsingEvent(user, used, target, clickLocation, canReach);
|
||||
RaiseLocalEvent(target.Value, afterInteractUsingEvent, false);
|
||||
return afterInteractEvent.Handled;
|
||||
}
|
||||
|
||||
#region ActivateItemInWorld
|
||||
/// <summary>
|
||||
/// Activates the IActivate behavior of an object
|
||||
/// Verifies that the user is capable of doing the use interaction first
|
||||
/// Raises <see cref="ActivateInWorldEvent"/> events and activates the IActivate behavior of an object.
|
||||
/// </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)
|
||||
return;
|
||||
UseDelayComponent? delayComponent = null;
|
||||
if (checkUseDelay
|
||||
&& TryComp(used, out delayComponent)
|
||||
&& delayComponent.ActiveDelay)
|
||||
return false;
|
||||
|
||||
InteractionActivate(user.Value, used.Value);
|
||||
}
|
||||
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used))
|
||||
return false;
|
||||
|
||||
protected void InteractionActivate(EntityUid user, EntityUid used)
|
||||
{
|
||||
if (TryComp(used, out UseDelayComponent? delayComponent) && delayComponent.ActiveDelay)
|
||||
return;
|
||||
|
||||
if (!_actionBlockerSystem.CanInteract(user) || !_actionBlockerSystem.CanUse(user))
|
||||
return;
|
||||
|
||||
// all activates should only fire when in range / unobstructed
|
||||
if (!InRangeUnobstructed(user, used, ignoreInsideBlocker: true, popup: true))
|
||||
return;
|
||||
if (checkAccess && !InRangeUnobstructed(user, used, ignoreInsideBlocker: true, popup: true))
|
||||
return false;
|
||||
|
||||
// Check if interacted entity is in the same container, the direct child, or direct parent of the user.
|
||||
// This is bypassed IF the interaction happened through an item slot (e.g., backpack UI)
|
||||
if (!ContainerSystem.IsInSameOrParentContainer(user, used) && !CanAccessViaStorage(user, used))
|
||||
return;
|
||||
if (checkAccess && !ContainerSystem.IsInSameOrParentContainer(user, used) && !CanAccessViaStorage(user, used))
|
||||
return false;
|
||||
|
||||
var activateMsg = new ActivateInWorldEvent(user, used);
|
||||
RaiseLocalEvent(used, activateMsg);
|
||||
@@ -665,44 +705,47 @@ namespace Content.Shared.Interaction
|
||||
{
|
||||
BeginDelay(delayComponent);
|
||||
_adminLogSystem.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}");
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!TryComp(used, out IActivate? activateComp))
|
||||
return;
|
||||
return false;
|
||||
|
||||
var activateEventArgs = new ActivateEventArgs(user, used);
|
||||
activateComp.Activate(activateEventArgs);
|
||||
BeginDelay(delayComponent);
|
||||
_adminLogSystem.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}"); // No way to check success.
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Hands
|
||||
#region Use
|
||||
/// <summary>
|
||||
/// Attempt to perform a use-interaction on an entity. If no interaction occurs, it will instead attempt to
|
||||
/// activate the entity.
|
||||
/// </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.
|
||||
/// Raises UseInHandEvents and activates the IUse behaviors of an entity
|
||||
/// Does not check accessibility or range, for obvious reasons
|
||||
/// </summary>
|
||||
/// <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.
|
||||
|
||||
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used))
|
||||
return false;
|
||||
|
||||
if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user))
|
||||
return false;
|
||||
|
||||
var useMsg = new UseInHandEvent(user, used);
|
||||
RaiseLocalEvent(used, useMsg);
|
||||
if (useMsg.Handled)
|
||||
@@ -724,7 +767,8 @@ namespace Content.Shared.Interaction
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
// else, default to activating the item
|
||||
return InteractionActivate(user, used, false, false, false);
|
||||
}
|
||||
|
||||
protected virtual void BeginDelay(UseDelayComponent? component = null)
|
||||
@@ -854,7 +898,7 @@ namespace Content.Shared.Interaction
|
||||
/// Raised when a player attempts to activate an item in an inventory slot or hand slot
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class InteractInventorySlotEvent : EntityEventArgs
|
||||
public sealed class InteractInventorySlotEvent : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity that was interacted with.
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Content.Shared.Pulling
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_blocker.CanInteract(puller))
|
||||
if (!_blocker.CanInteract(puller, pulled))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Content.Shared.Storage
|
||||
|
||||
bool IDraggable.Drop(DragDropEvent eventArgs)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(eventArgs.User))
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(eventArgs.User, eventArgs.Target))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Content.Shared.Strip.Components
|
||||
{
|
||||
return by != Owner
|
||||
&& IoCManager.Resolve<IEntityManager>().HasComponent<SharedHandsComponent>(@by)
|
||||
&& EntitySystem.Get<ActionBlockerSystem>().CanInteract(@by);
|
||||
&& EntitySystem.Get<ActionBlockerSystem>().CanInteract(@by, Owner);
|
||||
}
|
||||
|
||||
bool IDraggable.CanDrop(CanDropEvent args)
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Content.Shared.Tabletop
|
||||
return false;
|
||||
}
|
||||
|
||||
return playerEntity.InRangeUnobstructed(table.Value) && _actionBlockerSystem.CanInteract(playerEntity);
|
||||
return playerEntity.InRangeUnobstructed(table.Value) && _actionBlockerSystem.CanInteract(playerEntity, table);
|
||||
}
|
||||
|
||||
protected bool StunnedOrNoHands(EntityUid playerEntity)
|
||||
|
||||
@@ -76,10 +76,10 @@ namespace Content.Shared.Verbs
|
||||
|
||||
// A large number of verbs need to check action blockers. Instead of repeatedly having each system individually
|
||||
// call ActionBlocker checks, just cache it for the verb request.
|
||||
var canInteract = force || _actionBlockerSystem.CanInteract(user);
|
||||
var canInteract = force || _actionBlockerSystem.CanInteract(user, target);
|
||||
|
||||
EntityUid? @using = null;
|
||||
if (TryComp(user, out SharedHandsComponent? hands) && (force || _actionBlockerSystem.CanUse(user)))
|
||||
if (TryComp(user, out SharedHandsComponent? hands) && (force || _actionBlockerSystem.CanUseHeldEntity(user)))
|
||||
{
|
||||
hands.TryGetActiveHeldEntity(out @using);
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace Content.Shared.Verbs
|
||||
/// The entity currently being held by the active hand.
|
||||
/// </summary>
|
||||
/// <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.
|
||||
/// </remarks>
|
||||
public readonly EntityUid? Using;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
buckle-component-cannot-do-that-message = You can't do that!
|
||||
buckle-component-no-hands-message = You don't have hands!
|
||||
buckle-component-already-buckled-message = You are already buckled in!
|
||||
buckle-component-other-already-buckled-message = {$owner} is already buckled in!
|
||||
|
||||
@@ -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.
|
||||
@@ -8,8 +8,4 @@ ui-disposal-unit-label-status = Ready
|
||||
|
||||
ui-disposal-unit-button-flush = Flush
|
||||
ui-disposal-unit-button-eject = Eject Contents
|
||||
ui-disposal-unit-button-power = Power
|
||||
|
||||
ui-disposal-unit-is-valid-interaction-cannot=interact = You can't do that!
|
||||
ui-disposal-unit-is-valid-interaction-cannot-reach = You can't reach there!
|
||||
ui-disposal-unit-is-valid-interaction-no-hands = You have no hands.
|
||||
ui-disposal-unit-button-power = Power
|
||||
Reference in New Issue
Block a user