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:
Leon Friedrich
2021-08-22 03:20:18 +10:00
committed by GitHub
parent ad5f7bb71b
commit 486dc6ca62
51 changed files with 748 additions and 53 deletions

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using Content.Client.Resources;
using Content.Client.Stylesheets;

View File

@@ -1,4 +1,4 @@
using Content.Shared.Chemistry;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
@@ -24,7 +24,7 @@ namespace Content.Client.Chemistry.UI
_window.Close();
}
};
_window.OnClose += Close;
_window.OpenCentered();
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Threading;
using Content.Client.Examine;
@@ -121,7 +121,7 @@ namespace Content.Client.ContextMenu.UI
if (firstEntity == null) return;
if (args.Function == EngineKeyFunctions.Use || args.Function == ContentKeyFunctions.TryPullObject || args.Function == ContentKeyFunctions.MovePulledObject)
if (args.Function == EngineKeyFunctions.Use || args.Function == ContentKeyFunctions.AltActivateItemInWorld || args.Function == ContentKeyFunctions.TryPullObject || args.Function == ContentKeyFunctions.MovePulledObject)
{
var inputSys = _systemManager.GetEntitySystem<InputSystem>();
@@ -221,7 +221,7 @@ namespace Content.Client.ContextMenu.UI
return;
}
if (args.Function == EngineKeyFunctions.Use || args.Function == ContentKeyFunctions.Point ||
if (args.Function == EngineKeyFunctions.Use || args.Function == ContentKeyFunctions.AltActivateItemInWorld || args.Function == ContentKeyFunctions.Point ||
args.Function == ContentKeyFunctions.TryPullObject || args.Function == ContentKeyFunctions.MovePulledObject)
{
var inputSys = _systemManager.GetEntitySystem<InputSystem>();

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Content.Client.Stylesheets;
using Content.Shared.Input;
@@ -85,7 +85,9 @@ namespace Content.Client.EscapeMenu.UI.Tabs
AddButton(EngineKeyFunctions.Use);
AddButton(ContentKeyFunctions.WideAttack);
AddButton(ContentKeyFunctions.ActivateItemInHand);
AddButton(ContentKeyFunctions.AltActivateItemInHand);
AddButton(ContentKeyFunctions.ActivateItemInWorld);
AddButton(ContentKeyFunctions.AltActivateItemInWorld);
AddButton(ContentKeyFunctions.Drop);
AddButton(ContentKeyFunctions.ExamineEntity);
AddButton(ContentKeyFunctions.SwapHands);

View File

@@ -31,9 +31,11 @@ namespace Content.Client.Input
human.AddFunction(ContentKeyFunctions.SwapHands);
human.AddFunction(ContentKeyFunctions.Drop);
human.AddFunction(ContentKeyFunctions.ActivateItemInHand);
human.AddFunction(ContentKeyFunctions.AltActivateItemInHand);
human.AddFunction(ContentKeyFunctions.OpenCharacterMenu);
human.AddFunction(ContentKeyFunctions.ActivateItemInWorld);
human.AddFunction(ContentKeyFunctions.ThrowItemInHand);
human.AddFunction(ContentKeyFunctions.AltActivateItemInWorld);
human.AddFunction(ContentKeyFunctions.TryPullObject);
human.AddFunction(ContentKeyFunctions.MovePulledObject);
human.AddFunction(ContentKeyFunctions.ReleasePulledObject);

View File

@@ -207,9 +207,8 @@ namespace Content.Client.Inventory
return;
if (!Owner.TryGetSlot(slot, out var item))
return;
if (_itemSlotManager.OnButtonPressed(args, item))
return;
if (!_itemSlotManager.OnButtonPressed(args, item))
base.HandleInventoryKeybind(args, slot);
}

View File

@@ -7,6 +7,7 @@ using Content.Client.Verbs;
using Content.Shared.Cooldown;
using Content.Shared.Hands.Components;
using Content.Shared.Input;
using Content.Shared.Interaction;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Input;
@@ -86,26 +87,11 @@ namespace Content.Client.Items.Managers
}
else if (args.Function == ContentKeyFunctions.ActivateItemInWorld)
{
var inputSys = _entitySystemManager.GetEntitySystem<InputSystem>();
var func = args.Function;
var funcId = _inputManager.NetworkBindMap.KeyFunctionID(args.Function);
var mousePosWorld = _eyeManager.ScreenToMap(args.PointerLocation);
var coordinates = _mapManager.TryFindGridAt(mousePosWorld, out var grid) ? grid.MapToGrid(mousePosWorld) :
EntityCoordinates.FromMap(_mapManager, mousePosWorld);
var message = new FullInputCmdMessage(_gameTiming.CurTick, _gameTiming.TickFraction, funcId, BoundKeyState.Down,
coordinates, args.PointerLocation, item.Uid);
// client side command handlers will always be sent the local player session.
var session = _playerManager.LocalPlayer?.Session;
if (session == null)
return false;
inputSys.HandleInputCommand(session, func, message);
_entityManager.EntityNetManager?.SendSystemNetworkMessage(new InteractInventorySlotEvent(item.Uid, altInteract: false));
}
else if (args.Function == ContentKeyFunctions.AltActivateItemInWorld)
{
_entityManager.EntityNetManager?.SendSystemNetworkMessage(new InteractInventorySlotEvent(item.Uid, altInteract: true));
}
else
{

View File

@@ -256,6 +256,8 @@ namespace Content.Server.Access.Components
[Verb]
public sealed class EjectPrivilegedIDVerb : Verb<IdCardConsoleComponent>
{
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, IdCardConsoleComponent component, VerbData data)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
@@ -281,6 +283,8 @@ namespace Content.Server.Access.Components
public sealed class EjectTargetIDVerb : Verb<IdCardConsoleComponent>
{
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, IdCardConsoleComponent component, VerbData data)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))

View File

@@ -43,6 +43,7 @@ namespace Content.Server.Administration.Verbs
}
data.Text = Loc.GetString("admin-add-reagent-verb-get-data-text");
data.IconTexture = "/Textures/Interface/VerbIcons/spill.svg.192dpi.png";
data.CategoryData = VerbCategories.Debug;
data.Visibility = VerbVisibility.Invisible;

View File

@@ -75,6 +75,11 @@ namespace Content.Server.Cabinet
[Verb]
public sealed class ToggleItemCabinetVerb : Verb<ItemCabinetComponent>
{
// Unlike lockers, you cannot open/close cabinets by clicking on them, as this usually removes their item
// instead. So open/close is the alt-interact verb
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, ItemCabinetComponent component, VerbData data)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))

View File

@@ -426,6 +426,8 @@ namespace Content.Server.Chemistry.Components
[Verb]
public sealed class EjectBeakerVerb : Verb<ChemMasterComponent>
{
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, ChemMasterComponent component, VerbData data)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
@@ -436,6 +438,7 @@ namespace Content.Server.Chemistry.Components
data.Text = Loc.GetString("eject-beaker-verb-get-data-text");
data.Visibility = component.HasBeaker ? VerbVisibility.Visible : VerbVisibility.Invisible;
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
}
protected override void Activate(IEntity user, ChemMasterComponent component)

View File

@@ -368,6 +368,8 @@ namespace Content.Server.Chemistry.Components
[Verb]
public sealed class EjectBeakerVerb : Verb<ReagentDispenserComponent>
{
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, ReagentDispenserComponent component, VerbData data)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
@@ -378,6 +380,7 @@ namespace Content.Server.Chemistry.Components
data.Text = Loc.GetString("eject-beaker-verb-get-data-text");
data.Visibility = component.HasBeaker ? VerbVisibility.Visible : VerbVisibility.Invisible;
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
}
protected override void Activate(IEntity user, ReagentDispenserComponent component)

View File

@@ -282,6 +282,8 @@ namespace Content.Server.Chemistry.Components
[Verb]
public sealed class CustomTransferVerb : Verb<SolutionTransferComponent>
{
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, SolutionTransferComponent component, VerbData data)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user) || !component.CanChangeTransferAmount)

View File

@@ -1,4 +1,4 @@
using System;
using System;
using Content.Server.DoAfter;
using Content.Server.Notification;
using Content.Shared.ActionBlocker;
@@ -242,6 +242,8 @@ namespace Content.Server.Climbing.Components
[Verb]
private sealed class ClimbVerb : Verb<ClimbableComponent>
{
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, ClimbableComponent component, VerbData data)
{
if (!component.CanVault(user, component.Owner, out var _))

View File

@@ -207,7 +207,7 @@ namespace Content.Server.Disposal.Unit.Components
}
data.Visibility = VerbVisibility.Visible;
data.Text = Loc.GetString("self-insert-verb-get-data-text");
data.Text = Loc.GetString("disposal-self-insert-verb-get-data-text");
}
protected override void Activate(IEntity user, DisposalUnitComponent component)
@@ -230,8 +230,8 @@ namespace Content.Server.Disposal.Unit.Components
}
data.Visibility = VerbVisibility.Visible;
data.Text = Loc.GetString("flush-verb-get-data-text");
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
data.Text = Loc.GetString("disposal-flush-verb-get-data-text");
data.IconTexture = "/Textures/Interface/VerbIcons/delete_transparent.svg.192dpi.png";
}
protected override void Activate(IEntity user, DisposalUnitComponent component)
@@ -240,6 +240,37 @@ namespace Content.Server.Disposal.Unit.Components
}
}
[Verb]
private sealed class EjectVerb : Verb<DisposalUnitComponent>
{
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, DisposalUnitComponent component, VerbData data)
{
data.Visibility = VerbVisibility.Invisible;
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user) ||
component.ContainedEntities.Contains(user))
{
return;
}
// Only show verb if actually containing any entities.
if (component.ContainedEntities.Count > 0)
data.Visibility = VerbVisibility.Visible;
else
data.Visibility = VerbVisibility.Invisible;
data.Text = Loc.GetString("disposal-eject-verb-get-data-text");
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
}
protected override void Activate(IEntity user, DisposalUnitComponent component)
{
EntitySystem.Get<DisposalUnitSystem>().TryEjectContents(component);
}
}
void IDestroyAct.OnDestroy(DestructionEventArgs eventArgs)
{
EntitySystem.Get<DisposalUnitSystem>().TryEjectContents(this);

View File

@@ -82,10 +82,10 @@ namespace Content.Server.Hands.Components
.TryInteractionActivate(Owner, heldEntity);
}
protected override void DoUse(IEntity heldEntity)
protected override void DoUse(IEntity heldEntity, bool altInteract = false)
{
_entitySystemManager.GetEntitySystem<InteractionSystem>()
.TryUseInteraction(Owner, heldEntity);
.TryUseInteraction(Owner, heldEntity, altInteract);
}
protected override void HandlePickupAnimation(IEntity entity)

View File

@@ -50,6 +50,7 @@ namespace Content.Server.Hands
CommandBinds.Builder
.Bind(ContentKeyFunctions.ActivateItemInHand, InputCmdHandler.FromDelegate(HandleActivateItem))
.Bind(ContentKeyFunctions.AltActivateItemInHand, InputCmdHandler.FromDelegate(HandleAltActivateItem))
.Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem))
.Bind(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack))
.Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt))
@@ -220,6 +221,14 @@ namespace Content.Server.Hands
hands.UseActiveHeldEntity();
}
private void HandleAltActivateItem(ICommonSession? session)
{
if (!TryGetHandsComp(session, out var hands))
return;
hands.UseActiveHeldEntity(altInteract: true);
}
private bool HandleThrowItem(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
{
if (session is not IPlayerSession playerSession)

View File

@@ -32,6 +32,7 @@ namespace Content.Server.Interaction
data.Visibility = VerbVisibility.Visible;
data.Text = Loc.GetString("in-range-unoccluded-verb-get-data-text");
data.IconTexture = "/Textures/Interface/VerbIcons/information.svg.192dpi.png";
data.CategoryData = VerbCategories.Debug;
}

View File

@@ -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,10 +576,13 @@ 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))
{
if (altInteract)
AltInteract(user, used);
else
UseInteraction(user, used);
}
}

View File

@@ -44,6 +44,7 @@ namespace Content.Server.Items
}
data.Text = Loc.GetString("pick-up-verb-get-data-text");
data.IconTexture = "/Textures/Interface/VerbIcons/pickup.svg.192dpi.png";
}
protected override void Activate(IEntity user, ItemComponent component)

View File

@@ -26,6 +26,8 @@ namespace Content.Server.Storage.Components
[Verb]
private sealed class ToggleLockVerb : Verb<LockComponent>
{
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, LockComponent component, VerbData data)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user) ||

View File

@@ -228,6 +228,8 @@ namespace Content.Server.Medical.Components
[Verb]
public sealed class EjectVerb : Verb<MedicalScannerComponent>
{
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, MedicalScannerComponent component, VerbData data)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
@@ -238,6 +240,7 @@ namespace Content.Server.Medical.Components
data.Text = Loc.GetString("medical-scanner-eject-verb-get-data-text");
data.Visibility = component.IsOccupied ? VerbVisibility.Visible : VerbVisibility.Invisible;
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
}
protected override void Activate(IEntity user, MedicalScannerComponent component)

View File

@@ -375,6 +375,8 @@ namespace Content.Server.PDA
[Verb]
public sealed class EjectIDVerb : Verb<PDAComponent>
{
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, PDAComponent component, VerbData data)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))

View File

@@ -162,6 +162,8 @@ namespace Content.Server.Power.Components
[Verb]
private sealed class EjectVerb : Verb<BaseCharger>
{
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, BaseCharger component, VerbData data)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))

View File

@@ -179,6 +179,8 @@ namespace Content.Server.PowerCell.Components
[Verb]
public sealed class EjectCellVerb : Verb<PowerCellSlotComponent>
{
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, PowerCellSlotComponent component, VerbData data)
{
if (!component.ShowVerb || !EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))

View File

@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Content.Server.DoAfter;
using Content.Server.Hands.Components;
using Content.Server.Items;
using Content.Shared.ActionBlocker;
using Content.Shared.Acts;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
@@ -14,6 +15,7 @@ using Content.Shared.Notification.Managers;
using Content.Shared.Placeable;
using Content.Shared.Sound;
using Content.Shared.Storage;
using Content.Shared.Verbs;
using Content.Shared.Whitelist;
using Robust.Server.GameObjects;
using Robust.Server.Player;
@@ -22,6 +24,7 @@ using Robust.Shared.Containers;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Network;
@@ -629,5 +632,46 @@ namespace Content.Server.Storage.Components
{
SoundSystem.Play(Filter.Pvs(Owner), StorageSoundCollection.GetSound(), Owner, AudioParams.Default);
}
[Verb]
private sealed class ToggleOpenVerb : Verb<ServerStorageComponent>
{
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, ServerStorageComponent component, VerbData data)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
{
data.Visibility = VerbVisibility.Invisible;
return;
}
// Get the session for the user
var session = user.GetComponentOrNull<ActorComponent>()?.PlayerSession;
if (session == null)
{
data.Visibility = VerbVisibility.Invisible;
return;
}
// Does this player currently have the storage UI open?
if (component.SubscribedSessions.Contains(session))
{
data.Text = Loc.GetString("toggle-open-verb-close");
data.IconTexture = "/Textures/Interface/VerbIcons/close.svg.192dpi.png";
} else
{
data.Text = Loc.GetString("toggle-open-verb-open");
data.IconTexture = "/Textures/Interface/VerbIcons/open.svg.192dpi.png";
}
}
/// <inheritdoc />
protected override void Activate(IEntity user, ServerStorageComponent component)
{
// "Open" actually closes the UI if it is already open.
component.OpenStorageUI(user);
}
}
}
}

View File

@@ -290,6 +290,8 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
[Verb]
public sealed class EjectCellVerb : Verb<ServerBatteryBarrelComponent>
{
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, ServerBatteryBarrelComponent component, VerbData data)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user) || !component._powerCellRemovable)

View File

@@ -429,6 +429,8 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
[Verb]
private sealed class EjectMagazineVerb : Verb<ServerMagazineBarrelComponent>
{
public override bool AlternativeInteraction => true;
protected override void GetData(IEntity user, ServerMagazineBarrelComponent component, VerbData data)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))

View File

@@ -674,12 +674,12 @@ namespace Content.Shared.Hands.Components
DoInteraction(activeHeldEntity, heldEntity);
}
public void UseActiveHeldEntity()
public void UseActiveHeldEntity(bool altInteract = false)
{
if (!TryGetActiveHeldEntity(out var heldEntity))
return;
DoUse(heldEntity);
DoUse(heldEntity, altInteract);
}
public void ActivateHeldEntity(string handName)
@@ -783,7 +783,7 @@ namespace Content.Shared.Hands.Components
protected virtual void DoInteraction(IEntity activeHeldEntity, IEntity heldEntity) { }
protected virtual void DoUse(IEntity heldEntity) { }
protected virtual void DoUse(IEntity heldEntity, bool altInteract = false) { }
protected virtual void DoActivate(IEntity heldEntity) { }

View File

@@ -7,7 +7,9 @@ namespace Content.Shared.Input
{
public static readonly BoundKeyFunction WideAttack = "WideAttack";
public static readonly BoundKeyFunction ActivateItemInHand = "ActivateItemInHand";
public static readonly BoundKeyFunction ActivateItemInWorld = "ActivateItemInWorld"; // default action on world entity
public static readonly BoundKeyFunction AltActivateItemInHand = "AltActivateItemInHand";
public static readonly BoundKeyFunction ActivateItemInWorld = "ActivateItemInWorld";
public static readonly BoundKeyFunction AltActivateItemInWorld = "AltActivateItemInWorld";
public static readonly BoundKeyFunction Drop = "Drop";
public static readonly BoundKeyFunction ExamineEntity = "ExamineEntity";
public static readonly BoundKeyFunction FocusChat = "FocusChatInputWindow";

View File

@@ -1,3 +1,4 @@
using System;
using System.Linq;
using Content.Shared.Notification;
using Content.Shared.Notification.Managers;
@@ -9,6 +10,7 @@ using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Serialization;
namespace Content.Shared.Interaction
{
@@ -338,4 +340,27 @@ namespace Content.Shared.Interaction
return inRange;
}
}
/// <summary>
/// Raised when a player attempts to activate an item in an inventory slot or hand slot
/// </summary>
[Serializable, NetSerializable]
public class InteractInventorySlotEvent : EntityEventArgs
{
/// <summary>
/// Entity that was interacted with.
/// </summary>
public EntityUid ItemUid { get; }
/// <summary>
/// Whether the interaction used the alt-modifier to trigger alternative interactions.
/// </summary>
public bool AltInteract { get; }
public InteractInventorySlotEvent(EntityUid itemUid, bool altInteract = false)
{
ItemUid = itemUid;
AltInteract = altInteract;
}
}
}

View File

@@ -1,4 +1,4 @@

namespace Content.Shared.Verbs
{
public abstract class VerbBase
@@ -15,5 +15,10 @@ namespace Content.Shared.Verbs
/// OR the user can be the entity's container
/// </summary>
public virtual bool BlockedByContainers => true;
/// <summary>
/// If true, this verb can be activated by alt-clicking on the entity.
/// </summary>
public virtual bool AlternativeInteraction => false;
}
}

View File

@@ -10,7 +10,8 @@ namespace Content.Shared.Verbs
("Debug", "/Textures/Interface/VerbIcons/debug.svg.192dpi.png");
public static readonly VerbCategoryData Rotate = ("Rotate", null);
public static readonly VerbCategoryData Construction = ("Construction", null);
public static readonly VerbCategoryData Construction =
("Construction", "/Textures/Interface/hammer_scaled.svg.192dpi.png");
public static readonly VerbCategoryData SetTransferAmount =
("Set Transfer Amount", "/Textures/Interface/VerbIcons/spill.svg.192dpi.png");
}

View File

@@ -16,6 +16,9 @@ namespace Content.Shared.Verbs
/// <summary>
/// The text that the user sees on the verb button.
/// </summary>
/// <remarks>
/// This string is automatically passed through Loc.GetString().
/// </remarks>
public string Text { get; set; } = string.Empty;
/// <summary>

View File

@@ -16,8 +16,12 @@ disposal-mailing-unit-is-valid-interaction-no-hands = You have no hands.
## SelfInsertVerb
self-insert-verb-get-data-text = Jump inside
disposal-self-insert-verb-get-data-text = Jump inside
## FlushVerb
flush-verb-get-data-text = Flush
disposal-flush-verb-get-data-text = Flush
## EjectVerb
disposal-eject-verb-get-data-text = Eject

View File

@@ -72,7 +72,9 @@ ui-options-function-walk = Walk
ui-options-function-use = Use
ui-options-function-wide-attack = Wide attack
ui-options-function-activate-item-in-hand = Activate item in hand
ui-options-function-alt-activate-item-in-hand = Alternative activate item in hand
ui-options-function-activate-item-in-world = Activate item in world
ui-options-function-alt-activate-item-in-world = Alternative activate item in world
ui-options-function-drop = Drop item
ui-options-function-examine-entity = Examine
ui-options-function-swap-hands = Swap hands

View File

@@ -0,0 +1,5 @@
## ToggleOpenVerb
toggle-open-verb-open = Open
toggle-open-verb-close = Close

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1"
viewBox="0 0 32 32"
id="svg4"
sodipodi:docname="climb.svg"
width="32"
height="32"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
inkscape:export-filename="C:\Users\ElectroSR\Documents\GitHub\space-station-14\Resources\Textures\Interface\VerbIcons\pickup.svg.192dpi.png"
inkscape:export-xdpi="192"
inkscape:export-ydpi="192"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8">
<symbol
id="UpArrow">
<title
id="title4024">Up Arrow</title>
<path
d="M 36,9 58,31 58,46 42,30 42,62 30,62 30,30 14,46 14,31 z"
style="stroke:none"
id="path4026" />
</symbol>
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1284"
inkscape:window-height="1081"
id="namedview6"
showgrid="false"
inkscape:pagecheckerboard="true"
inkscape:zoom="22.627417"
inkscape:cx="8.4631843"
inkscape:cy="18.760427"
inkscape:window-x="578"
inkscape:window-y="502"
inkscape:window-maximized="0"
inkscape:current-layer="svg4" />
<use
xlink:href="#UpArrow"
style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:0, 11.00000008;stroke-dashoffset:0;fill-opacity:1"
id="use12482"
x="0"
y="0"
width="100%"
height="100%"
transform="matrix(0.52591705,0,0,0.52591705,-3.0032019,-3.4294343)" />
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg6"
sodipodi:docname="delete_white.svg"
width="32"
height="32"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
inkscape:export-filename="C:\Users\ElectroSR\Documents\GitHub\space-station-14\Resources\Textures\Interface\VerbIcons\delete_transparent.svg.192dpi.png"
inkscape:export-xdpi="192"
inkscape:export-ydpi="192"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10">
<color-profile
name="Agfa-:-Swop-Standard"
xlink:href="../../../../../../../../../WINDOWS/system32/spool/drivers/color/RSWOP.icm"
id="color-profile1466" />
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1271"
inkscape:window-height="1272"
id="namedview8"
showgrid="true"
inkscape:pagecheckerboard="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:zoom="16"
inkscape:cx="11.21875"
inkscape:cy="13.0625"
inkscape:window-x="341"
inkscape:window-y="107"
inkscape:window-maximized="0"
inkscape:current-layer="svg6">
<inkscape:grid
type="xygrid"
id="grid837"
empspacing="4" />
<sodipodi:guide
position="24.91123,3.1839064"
orientation="-0.99756278,0.069774599"
id="guide848" />
<sodipodi:guide
position="6.8635256,3.1839064"
orientation="-0.99830447,-0.058208125"
id="guide850" />
</sodipodi:namedview>
<g
id="g846"
transform="translate(0.99999985,0.4620656)"
style="fill:#ffffff;fill-opacity:1">
<path
id="path2"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.225696"
d="M 11.875975,2.7218408 11.26413,4.2466048 H 3.8056135 L 3.3542233,7.7523717 H 26.645777 L 26.194387,4.2466048 H 18.73587 l -0.611845,-1.524764 z" />
<path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.225696"
d="M 20.609312,9 V 25.675027 H 18.976634 V 9 H 15.704055 V 25.675027 H 14.071152 V 9 H 10.573781 V 25.675027 H 8.9413298 V 9 H 4.7350507 L 5.8742488,28.537934 H 23.898367 L 25.264949,9 Z"
id="path841"
sodipodi:nodetypes="ccccccccccccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 B

View File

@@ -0,0 +1,3 @@
#variant of delete.svg, with transparent background
sample:
filter: true

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1"
viewBox="0 0 32 32"
id="svg4"
sodipodi:docname="information.svg"
width="32"
height="32"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
inkscape:export-filename="C:\Users\ElectroSR\Documents\GitHub\space-station-14\Resources\Textures\Interface\VerbIcons\information.svg.192dpi.png"
inkscape:export-xdpi="192"
inkscape:export-ydpi="192"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8">
<symbol
id="Information">
<title
id="title622">Information</title>
<circle
cx="36"
cy="36"
r="24"
style="fill:none;stroke-width:6"
id="circle624" />
<path
d="m 27,32 c 0,-5 1,-11 9,-11 4,0 8,3 8,7 0,4 -2,6 -4,7 -2,1 -4,2 -4,5 0,3 0,6 0,6"
style="fill:none;stroke-width:6"
id="path626" />
<rect
x="33"
y="48"
width="6"
height="6"
style="stroke:none"
id="rect628" />
</symbol>
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1271"
inkscape:window-height="1081"
id="namedview6"
showgrid="false"
inkscape:pagecheckerboard="true"
inkscape:zoom="35.2"
inkscape:cx="13.508523"
inkscape:cy="14.701705"
inkscape:window-x="1119"
inkscape:window-y="319"
inkscape:window-maximized="0"
inkscape:current-layer="svg4" />
<use
xlink:href="#Information"
style="fill:#ffffff;stroke:#ffffff;stroke-opacity:1"
id="use4000"
x="0"
y="0"
width="100%"
height="100%"
transform="matrix(0.48863637,0,0,0.48863637,-1.5909093,-1.5909093)"
inkscape:export-filename="C:\Users\ElectroSR\Documents\GitHub\space-station-14\Resources\Textures\Interface\VerbIcons\information.svg.192dpi.png"
inkscape:export-xdpi="192"
inkscape:export-ydpi="192" />
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,2 @@
sample:
filter: true

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1"
viewBox="0 0 32 32"
id="svg4"
sodipodi:docname="pickup.svg"
width="32"
height="32"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
inkscape:export-filename="C:\Users\Pieter-Jan Briers\Projects\space-station-14\Resources\Textures\Interface\VerbIcons\anchor.svg.96dpi.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8">
<symbol
id="UpArrow">
<title
id="title4024">Up Arrow</title>
<path
d="M 36,9 58,31 58,46 42,30 42,62 30,62 30,30 14,46 14,31 z"
style="stroke:none"
id="path4026" />
</symbol>
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1417"
id="namedview6"
showgrid="false"
inkscape:pagecheckerboard="true"
inkscape:zoom="22.627417"
inkscape:cx="8.4852814"
inkscape:cy="18.760427"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg4" />
<use
xlink:href="#UpArrow"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:0, 11;stroke-dashoffset:0"
id="use12482"
x="0"
y="0"
width="100%"
height="100%"
transform="matrix(0.46866551,0,0,0.46866551,-0.87195836,-0.6376256)"
inkscape:export-filename="C:\Users\ElectroSR\Documents\GitHub\space-station-14\Resources\Textures\Interface\VerbIcons\pickup.svg.192dpi.png"
inkscape:export-xdpi="192"
inkscape:export-ydpi="192" />
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

View File

@@ -0,0 +1,2 @@
sample:
filter: true

View File

@@ -0,0 +1,3 @@
#this is just a copy of hammer.svg, but scaled and adjusted slightly so that it can be used as a right-click context menu icon.
sample:
filter: true

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1"
viewBox="0 0 32 32"
id="svg4"
sodipodi:docname="hammer_scaled.svg"
width="32"
height="32"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
inkscape:export-filename="C:\Users\ElectroSR\Documents\GitHub\space-station-14\Resources\Textures\Interface\hammer_scaled.svg.192dpi.png"
inkscape:export-xdpi="192"
inkscape:export-ydpi="192"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8">
<symbol
id="UpArrow">
<title
id="title4024">Up Arrow</title>
<path
d="M 36,9 58,31 58,46 42,30 42,62 30,62 30,30 14,46 14,31 z"
style="stroke:none"
id="path4026" />
</symbol>
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1417"
id="namedview6"
showgrid="false"
inkscape:pagecheckerboard="true"
inkscape:zoom="22.627417"
inkscape:cx="5.8778251"
inkscape:cy="12.926796"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg4" />
<g
transform="matrix(3.1774603,0,0,3.1774603,246.59289,376.58527)"
id="layer1">
<path
id="path34"
d="m -70.236934,-117.55773 -0.861483,0.0297 c -0.04048,0.001 -0.07832,0.0195 -0.105569,0.0503 l -0.102658,0.11563 -0.112713,-0.10822 c -0.03016,-0.0288 -0.06958,-0.044 -0.109802,-0.0429 l -1.723231,0.0593 c -0.345546,0.0119 -0.924454,0.0318 -1.479814,0.65811 -0.555096,0.62585 -0.529432,1.18144 -0.51144,1.58708 0.0029,0.064 0.0426,0.12052 0.100806,0.14295 0.05821,0.0227 0.123296,0.007 0.164571,-0.04 0.827352,-0.93271 1.309423,-0.67016 1.599935,-0.39139 0.241565,0.23159 0.245269,0.55176 0.259557,0.73109 h 1.480872 c 0.0463,-0.33388 0.127,-0.82923 0.259028,-0.9535 l 0.154516,0.14827 c 0.03016,0.0288 0.06959,0.044 0.109802,0.0428 l 1.077119,-0.0371 c 0.04022,-0.002 0.07832,-0.0195 0.105569,-0.0503 l 0.0034,-0.004 c 0.02752,-0.033 0.04022,-0.0765 0.03519,-0.11983 l -0.184679,-1.67522 c -0.0093,-0.0833 -0.07858,-0.14548 -0.159015,-0.14294 z"
style="clip-rule:evenodd;fill:#ffffff;fill-opacity:0.941176;fill-rule:evenodd;stroke-width:0.264583" />
<path
id="path36"
d="m -73.125919,-110.25648 c -0.004,0.22704 0.07726,0.43934 0.242094,0.61087 0.313531,0.32631 0.832379,0.31758 1.157287,-0.0195 0.165365,-0.17174 0.256381,-0.40722 0.249767,-0.64571 l -0.163777,-4.08366 h -1.485371 z"
style="clip-rule:evenodd;fill:#ffffff;fill-opacity:0.941176;fill-rule:evenodd;stroke-width:0.264583" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 B

View File

@@ -109,6 +109,10 @@ binds:
- function: ActivateItemInHand
type: State
key: Z
- function: AltActivateItemInHand
type: State
key: Z
mod1: Alt
- function: OpenCharacterMenu
type: State
key: C
@@ -128,6 +132,15 @@ binds:
- function: ActivateItemInWorld
type: State
key: E
- function: AltActivateItemInWorld
type: State
key: MouseLeft
canFocus: true
mod1: Alt
- function: AltActivateItemInWorld # secondary binding
type: State
key: E
mod1: Alt
- function: ThrowItemInHand
type: State
key: Q