Add Alt-click functionality (#4497)
* Fix ItemSlot Bug * Add Alt-use Key * Fix TransferAmount window bug * Alt-click functionality * Added AltInteract verbs * Add new verbs * verb icons * Changed Comments * Change Comments * Fix disposal verbs * Changed Get...() to Get...OrNull() * Changed alt-interact combat behaviour * Update verb icons * Inventory interact event * Add Alt+E secondary binding * Add alt-z keybinding * Rename AltUse -> AltActivateItemInWorld
This commit is contained in:
@@ -21,6 +21,7 @@ using Content.Shared.Inventory;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Rotatable;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
@@ -52,10 +53,13 @@ namespace Content.Server.Interaction
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeNetworkEvent<DragDropRequestEvent>(HandleDragDropRequestEvent);
|
||||
SubscribeNetworkEvent<InteractInventorySlotEvent>(HandleInteractInventorySlotEvent);
|
||||
|
||||
CommandBinds.Builder
|
||||
.Bind(EngineKeyFunctions.Use,
|
||||
new PointerInputCmdHandler(HandleUseInteraction))
|
||||
.Bind(ContentKeyFunctions.AltActivateItemInWorld,
|
||||
new PointerInputCmdHandler(HandleAltUseInteraction))
|
||||
.Bind(ContentKeyFunctions.WideAttack,
|
||||
new PointerInputCmdHandler(HandleWideAttack))
|
||||
.Bind(ContentKeyFunctions.ActivateItemInWorld,
|
||||
@@ -102,6 +106,34 @@ namespace Content.Server.Interaction
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Handles the event were a client uses an item in their inventory or in their hands, either by
|
||||
/// alt-clicking it or pressing 'E' while hovering over it.
|
||||
/// </summary>
|
||||
private void HandleInteractInventorySlotEvent(InteractInventorySlotEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (!EntityManager.TryGetEntity(msg.ItemUid, out var item))
|
||||
{
|
||||
Logger.WarningS("system.interaction",
|
||||
$"Client sent inventory interaction with an invalid target item. Session={args.SenderSession}");
|
||||
return;
|
||||
}
|
||||
|
||||
// client sanitization
|
||||
if (!ValidateClientInput(args.SenderSession, item.Transform.Coordinates, msg.ItemUid, out var userEntity))
|
||||
{
|
||||
Logger.InfoS("system.interaction", $"Inventory interaction validation failed. Session={args.SenderSession}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.AltInteract)
|
||||
// Use 'UserInteraction' function - behaves as if the user alt-clicked the item in the world.
|
||||
UserInteraction(userEntity, item.Transform.Coordinates, msg.ItemUid, msg.AltInteract);
|
||||
else
|
||||
// User used 'E'. We want to activate it, not simulate clicking on the item
|
||||
InteractionActivate(userEntity, item);
|
||||
}
|
||||
|
||||
#region Drag drop
|
||||
private void HandleDragDropRequestEvent(DragDropRequestEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
@@ -241,6 +273,20 @@ namespace Content.Server.Interaction
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool HandleAltUseInteraction(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
|
||||
{
|
||||
// client sanitization
|
||||
if (!ValidateClientInput(session, coords, uid, out var userEntity))
|
||||
{
|
||||
Logger.InfoS("system.interaction", $"Alt-use input validation failed");
|
||||
return true;
|
||||
}
|
||||
|
||||
UserInteraction(userEntity, coords, uid, altInteract : true );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool HandleTryPullObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
|
||||
{
|
||||
if (!ValidateClientInput(session, coords, uid, out var userEntity))
|
||||
@@ -264,12 +310,24 @@ namespace Content.Server.Interaction
|
||||
return pull.TogglePull(userEntity);
|
||||
}
|
||||
|
||||
public async void UserInteraction(IEntity user, EntityCoordinates coordinates, EntityUid clickedUid)
|
||||
/// <summary>
|
||||
/// Resolves user interactions with objects.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Checks Whether combat mode is enabled and whether the user can actually interact with the given entity.
|
||||
/// </remarks>
|
||||
/// <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(IEntity user, EntityCoordinates coordinates, EntityUid clickedUid, bool altInteract = false )
|
||||
{
|
||||
if (user.TryGetComponent(out CombatModeComponent? combatMode) && combatMode.IsInCombatMode)
|
||||
// TODO COMBAT Consider using alt-interact for advanced combat? maybe alt-interact disarms?
|
||||
if (!altInteract && user.TryGetComponent(out CombatModeComponent? combatMode) && combatMode.IsInCombatMode)
|
||||
{
|
||||
|
||||
DoAttack(user, coordinates, false, clickedUid);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (!ValidateInteractAndFace(user, coordinates))
|
||||
@@ -313,12 +371,22 @@ namespace Content.Server.Interaction
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are close to the nearby object and the object isn't contained in our active hand
|
||||
// InteractUsing/AfterInteract: We will either use the item on the nearby object
|
||||
if (item != null)
|
||||
// We are close to the nearby object.
|
||||
if (altInteract)
|
||||
// We are trying to use alternative interactions. Perform alternative interactions, using context
|
||||
// menu verbs.
|
||||
|
||||
// Verbs can be triggered with an item in the hand, but currently there are no verbs that depend on
|
||||
// the currently held item. Maybe this if statement should be changed to
|
||||
// (altInteract && (item == null || item == target)).
|
||||
// Note that item == target will happen when alt-clicking the item currently in your hands.
|
||||
AltInteract(user, target);
|
||||
else if (item != null && item != target)
|
||||
// We are performing a standard interaction with an item, and the target isn't the same as the item
|
||||
// currently in our hand. We will use the item in our hand on the nearby object via InteractUsing
|
||||
await InteractUsing(user, item, target, coordinates);
|
||||
// InteractHand/Activate: Since our hand is empty we will use InteractHand/Activate
|
||||
else
|
||||
else if (item == null)
|
||||
// Since our hand is empty we will use InteractHand/Activate
|
||||
InteractHand(user, target);
|
||||
}
|
||||
}
|
||||
@@ -432,6 +500,44 @@ namespace Content.Server.Interaction
|
||||
await InteractDoAfter(user, used, target, clickLocation, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Alternative interactions on an entity.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Uses the context menu verb list, and acts out the first verb marked as an alternative interaction. Note
|
||||
/// that this does not have any checks to see whether this interaction is valid, as these are all done in <see
|
||||
/// cref="UserInteraction(IEntity, EntityCoordinates, EntityUid, bool)"/>
|
||||
/// </remarks>
|
||||
public void AltInteract(IEntity user, IEntity target)
|
||||
{
|
||||
// TODO VERB SYSTEM when ECS-ing verbs and re-writing VerbUtility.GetVerbs, maybe sort verbs by some
|
||||
// priority property, such that which verbs appear first is more predictable?.
|
||||
|
||||
// Iterate through list of verbs that apply to target. We do not include global verbs here. If in the future
|
||||
// alt click should also support global verbs, this needs to be changed.
|
||||
foreach (var (component, verb) in VerbUtility.GetVerbs(target))
|
||||
{
|
||||
// Check that the verb marked as an alternative interaction?
|
||||
if (!verb.AlternativeInteraction)
|
||||
continue;
|
||||
|
||||
// Can the verb be acted out?
|
||||
if (!VerbUtility.VerbAccessChecks(user, target, verb))
|
||||
continue;
|
||||
|
||||
// Is the verb currently enabled?
|
||||
var verbData = verb.GetData(user, component);
|
||||
if (verbData.IsInvisible || verbData.IsDisabled)
|
||||
continue;
|
||||
|
||||
// Act out the verb. Note that, if there is more than one AlternativeInteraction verb, only the first
|
||||
// one is activated. The priority is effectively determined by the order in which VerbUtility.GetVerbs()
|
||||
// returns the verbs.
|
||||
verb.Activate(user, component);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses an empty hand on an entity
|
||||
/// Finds components with the InteractHand interface and calls their function
|
||||
@@ -470,11 +576,14 @@ namespace Content.Server.Interaction
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="used"></param>
|
||||
public void TryUseInteraction(IEntity user, IEntity used)
|
||||
public void TryUseInteraction(IEntity user, IEntity used, bool altInteract = false)
|
||||
{
|
||||
if (user != null && used != null && _actionBlockerSystem.CanUse(user))
|
||||
{
|
||||
UseInteraction(user, used);
|
||||
if (altInteract)
|
||||
AltInteract(user, used);
|
||||
else
|
||||
UseInteraction(user, used);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user