HandsSystem Refactor (#38438)
* checkpoint * pt 2 * pt... i forgot * pt 4 * patch * More test fixes * optimization!!! * the REAL hand system * fix RetractableItemActionSystem.cs oversight * the review * test * remove test usage of body prototype * Update Content.IntegrationTests/Tests/Interaction/InteractionTest.cs Co-authored-by: Tayrtahn <tayrtahn@gmail.com> * hellcode * hellcode 2 * Minor cleanup * test * Chasing the last of the bugs * changes --------- Co-authored-by: Tayrtahn <tayrtahn@gmail.com>
This commit is contained in:
@@ -3,7 +3,7 @@ using Content.Shared.Access.Components;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.PDA;
|
||||
@@ -21,6 +21,7 @@ public abstract class SharedIdCardSystem : EntitySystem
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedAccessSystem _access = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
@@ -83,8 +84,7 @@ public abstract class SharedIdCardSystem : EntitySystem
|
||||
public bool TryFindIdCard(EntityUid uid, out Entity<IdCardComponent> idCard)
|
||||
{
|
||||
// check held item?
|
||||
if (TryComp(uid, out HandsComponent? hands) &&
|
||||
hands.ActiveHandEntity is EntityUid heldItem &&
|
||||
if (_hands.GetActiveItem(uid) is { } heldItem &&
|
||||
TryGetIdCard(heldItem, out idCard))
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -97,7 +97,7 @@ public sealed partial class BlockingSystem : EntitySystem
|
||||
if (!handQuery.TryGetComponent(args.Performer, out var hands))
|
||||
return;
|
||||
|
||||
var shields = _handsSystem.EnumerateHeld(args.Performer, hands).ToArray();
|
||||
var shields = _handsSystem.EnumerateHeld((args.Performer, hands)).ToArray();
|
||||
|
||||
foreach (var shield in shields)
|
||||
{
|
||||
@@ -277,7 +277,7 @@ public sealed partial class BlockingSystem : EntitySystem
|
||||
if (!handQuery.TryGetComponent(user, out var hands))
|
||||
return;
|
||||
|
||||
var shields = _handsSystem.EnumerateHeld(user, hands).ToArray();
|
||||
var shields = _handsSystem.EnumerateHeld((user, hands)).ToArray();
|
||||
|
||||
foreach (var shield in shields)
|
||||
{
|
||||
|
||||
@@ -947,12 +947,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
||||
if (!entity.Comp.HeldOnly)
|
||||
return true;
|
||||
|
||||
if (TryComp(examiner, out HandsComponent? handsComp))
|
||||
{
|
||||
return Hands.IsHolding(examiner, entity, out _, handsComp);
|
||||
}
|
||||
|
||||
return true;
|
||||
return Hands.IsHolding(examiner, entity, out _);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<SolutionContainerManagerComponent> entity, ref MapInitEvent args)
|
||||
|
||||
@@ -255,7 +255,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
}
|
||||
|
||||
// Drop the held item onto the floor. Return if the user cannot drop.
|
||||
if (!_handsSystem.TryDrop(args.User, args.Used, handsComp: hands))
|
||||
if (!_handsSystem.TryDrop(args.User, args.Used))
|
||||
return;
|
||||
|
||||
slots.Sort(SortEmpty);
|
||||
@@ -395,17 +395,17 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
if (!Resolve(user, ref hands, false))
|
||||
return false;
|
||||
|
||||
if (hands.ActiveHand?.HeldEntity is not { } held)
|
||||
if (!_handsSystem.TryGetActiveItem((uid, hands), out var held))
|
||||
return false;
|
||||
|
||||
if (!CanInsert(uid, held, user, slot))
|
||||
if (!CanInsert(uid, held.Value, user, slot))
|
||||
return false;
|
||||
|
||||
// hands.Drop(item) checks CanDrop action blocker
|
||||
if (!_handsSystem.TryDrop(user, hands.ActiveHand))
|
||||
if (!_handsSystem.TryDrop(user, hands.ActiveHandId!))
|
||||
return false;
|
||||
|
||||
Insert(uid, slot, held, user, excludeUserAudio: excludeUserAudio);
|
||||
Insert(uid, slot, held.Value, user, excludeUserAudio: excludeUserAudio);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -428,16 +428,14 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
TryComp(user, out HandsComponent? handsComp);
|
||||
|
||||
if (!TryGetAvailableSlot(ent,
|
||||
item,
|
||||
user == null ? null : (user.Value, handsComp),
|
||||
user,
|
||||
out var itemSlot,
|
||||
emptyOnly: true))
|
||||
return false;
|
||||
|
||||
if (user != null && !_handsSystem.TryDrop(user.Value, item, handsComp: handsComp))
|
||||
if (user != null && !_handsSystem.TryDrop(user.Value, item))
|
||||
return false;
|
||||
|
||||
Insert(ent, itemSlot, item, user, excludeUserAudio: excludeUserAudio);
|
||||
@@ -466,7 +464,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
&& Resolve(user, ref user.Comp)
|
||||
&& _handsSystem.IsHolding(user, item))
|
||||
{
|
||||
if (!_handsSystem.CanDrop(user, item, user.Comp))
|
||||
if (!_handsSystem.CanDrop(user, item))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -397,6 +397,10 @@ namespace Content.Shared.Cuffs
|
||||
/// </summary>
|
||||
private void OnHandCountChanged(Entity<CuffableComponent> ent, ref HandCountChangedEvent message)
|
||||
{
|
||||
// TODO: either don't store a container ref, or make it actually nullable.
|
||||
if (ent.Comp.Container == default!)
|
||||
return;
|
||||
|
||||
var dirty = false;
|
||||
var handCount = CompOrNull<HandsComponent>(ent.Owner)?.Count ?? 0;
|
||||
|
||||
@@ -431,19 +435,19 @@ namespace Content.Shared.Cuffs
|
||||
return;
|
||||
|
||||
var freeHands = 0;
|
||||
foreach (var hand in _hands.EnumerateHands(uid, handsComponent))
|
||||
foreach (var hand in _hands.EnumerateHands((uid, handsComponent)))
|
||||
{
|
||||
if (hand.HeldEntity == null)
|
||||
if (!_hands.TryGetHeldItem((uid, handsComponent), hand, out var held))
|
||||
{
|
||||
freeHands++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is this entity removable? (it might be an existing handcuff blocker)
|
||||
if (HasComp<UnremoveableComponent>(hand.HeldEntity))
|
||||
if (HasComp<UnremoveableComponent>(held))
|
||||
continue;
|
||||
|
||||
_hands.DoDrop(uid, hand, true, handsComponent);
|
||||
_hands.DoDrop(uid, hand, true);
|
||||
freeHands++;
|
||||
if (freeHands == 2)
|
||||
break;
|
||||
|
||||
@@ -141,7 +141,7 @@ public abstract class SharedDisposalUnitSystem : EntitySystem
|
||||
Category = VerbCategory.Insert,
|
||||
Act = () =>
|
||||
{
|
||||
_handsSystem.TryDropIntoContainer(args.User, args.Using.Value, component.Container, checkActionBlocker: false, args.Hands);
|
||||
_handsSystem.TryDropIntoContainer((args.User, args.Hands), args.Using.Value, component.Container, checkActionBlocker: false);
|
||||
_adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Using.Value)} into {ToPrettyString(uid)}");
|
||||
AfterInsert(uid, component, args.Using.Value, args.User);
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
|
||||
return true;
|
||||
|
||||
// If the user changes which hand is active at all, interrupt the do-after
|
||||
if (args.BreakOnHandChange && hands.ActiveHand?.Name != doAfter.InitialHand)
|
||||
if (args.BreakOnHandChange && hands.ActiveHandId != doAfter.InitialHand)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -221,8 +221,8 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
|
||||
if (!TryComp(args.User, out HandsComponent? handsComponent))
|
||||
return false;
|
||||
|
||||
doAfter.InitialHand = handsComponent.ActiveHand?.Name;
|
||||
doAfter.InitialItem = handsComponent.ActiveHandEntity;
|
||||
doAfter.InitialHand = handsComponent.ActiveHandId;
|
||||
doAfter.InitialItem = _hands.GetActiveItem((args.User, handsComponent));
|
||||
}
|
||||
|
||||
doAfter.NetInitialItem = GetNetEntity(doAfter.InitialItem);
|
||||
|
||||
@@ -70,7 +70,7 @@ public sealed class DeployFoldableSystem : EntitySystem
|
||||
}
|
||||
|
||||
if (!TryComp(args.User, out HandsComponent? hands)
|
||||
|| !_hands.TryDrop(args.User, args.Used, targetDropLocation: args.ClickLocation, handsComp: hands))
|
||||
|| !_hands.TryDrop((args.User, hands), args.Used, targetDropLocation: args.ClickLocation))
|
||||
return;
|
||||
|
||||
if (!_foldable.TrySetFolded(ent, foldable, false))
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
|
||||
namespace Content.Shared.Hands.Components;
|
||||
|
||||
/// <summary>
|
||||
/// These helpers exist to make getting basic information out of the hands component more convenient, without
|
||||
/// needing to resolve hands system or something like that.
|
||||
/// </summary>
|
||||
public static class HandHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns true if any hand is free. This is a LinQ method, not a property, so
|
||||
/// cache it instead of accessing this multiple times.
|
||||
/// </summary>
|
||||
public static bool IsAnyHandFree(this HandsComponent component) => component.Hands.Values.Any(hand => hand.IsEmpty);
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of hands that are not currently holding anything. This is a LinQ method, not a property, so
|
||||
/// cache it instead of accessing this multiple times.
|
||||
/// </summary>
|
||||
public static int CountFreeHands(this HandsComponent component) => component.Hands.Values.Count(hand => hand.IsEmpty);
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of hands that are not currently holding anything. This is a LinQ method, not a property, so
|
||||
/// cache it instead of accessing this multiple times.
|
||||
/// </summary>
|
||||
public static int CountFreeableHands(this Entity<HandsComponent> component, SharedHandsSystem system)
|
||||
{
|
||||
return system.CountFreeableHands(component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of hands that are currently holding nothing. This is a LinQ method, not a property, so cache
|
||||
/// it instead of accessing this multiple times.
|
||||
/// </summary>
|
||||
public static IEnumerable<Hand> GetFreeHands(this HandsComponent component) => component.Hands.Values.Where(hand => !hand.IsEmpty);
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of hands that are currently holding nothing. This is a LinQ method, not a property, so cache
|
||||
/// it instead of accessing this multiple times.
|
||||
/// </summary>
|
||||
public static IEnumerable<string> GetFreeHandNames(this HandsComponent component) => GetFreeHands(component).Select(hand => hand.Name);
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using Content.Shared.DisplacementMap;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
@@ -13,50 +12,50 @@ public sealed partial class HandsComponent : Component
|
||||
/// <summary>
|
||||
/// The currently active hand.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public Hand? ActiveHand;
|
||||
[DataField]
|
||||
public string? ActiveHandId;
|
||||
|
||||
/// <summary>
|
||||
/// The item currently held in the active hand.
|
||||
/// Dictionary relating a unique hand ID corresponding to a container slot on the attached entity to a class containing information about the Hand itself.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public EntityUid? ActiveHandEntity => ActiveHand?.HeldEntity;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField]
|
||||
public Dictionary<string, Hand> Hands = new();
|
||||
|
||||
/// <summary>
|
||||
/// The number of hands
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public int Count => Hands.Count;
|
||||
|
||||
/// <summary>
|
||||
/// List of hand-names. These are keys for <see cref="Hands"/>. The order of this list determines the order in which hands are iterated over.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<string> SortedHands = new();
|
||||
|
||||
/// <summary>
|
||||
/// If true, the items in the hands won't be affected by explosions.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool DisableExplosionRecursion = false;
|
||||
public bool DisableExplosionRecursion;
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the speed at which items are thrown.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseThrowspeed { get; set; } = 11f;
|
||||
public float BaseThrowspeed = 11f;
|
||||
|
||||
/// <summary>
|
||||
/// Distance after which longer throw targets stop increasing throw impulse.
|
||||
/// </summary>
|
||||
[DataField("throwRange")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float ThrowRange { get; set; } = 8f;
|
||||
[DataField]
|
||||
public float ThrowRange = 8f;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not to add in-hand sprites for held items. Some entities (e.g., drones) don't want these.
|
||||
/// Used by the client.
|
||||
/// </summary>
|
||||
[DataField("showInHands")]
|
||||
[DataField]
|
||||
public bool ShowInHands = true;
|
||||
|
||||
/// <summary>
|
||||
@@ -68,14 +67,13 @@ public sealed partial class HandsComponent : Component
|
||||
/// <summary>
|
||||
/// The time at which throws will be allowed again.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoPausedField]
|
||||
[DataField, AutoPausedField]
|
||||
public TimeSpan NextThrowTime;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum time inbetween throws.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public TimeSpan ThrowCooldown = TimeSpan.FromSeconds(0.5f);
|
||||
|
||||
/// <summary>
|
||||
@@ -103,48 +101,37 @@ public sealed partial class HandsComponent : Component
|
||||
public bool CanBeStripped = true;
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class Hand //TODO: This should definitely be a struct - Jezi
|
||||
public partial record struct Hand
|
||||
{
|
||||
[ViewVariables]
|
||||
public string Name { get; }
|
||||
[DataField]
|
||||
public HandLocation Location = HandLocation.Right;
|
||||
|
||||
[ViewVariables]
|
||||
public HandLocation Location { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The container used to hold the contents of this hand. Nullable because the client must get the containers via <see cref="ContainerManagerComponent"/>,
|
||||
/// which may not be synced with the server when the client hands are created.
|
||||
/// </summary>
|
||||
[ViewVariables, NonSerialized]
|
||||
public ContainerSlot? Container;
|
||||
|
||||
[ViewVariables]
|
||||
public EntityUid? HeldEntity => Container?.ContainedEntity;
|
||||
|
||||
public bool IsEmpty => HeldEntity == null;
|
||||
|
||||
public Hand(string name, HandLocation location, ContainerSlot? container = null)
|
||||
public Hand()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Hand(HandLocation location)
|
||||
{
|
||||
Name = name;
|
||||
Location = location;
|
||||
Container = container;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class HandsComponentState : ComponentState
|
||||
{
|
||||
public readonly List<Hand> Hands;
|
||||
public readonly List<string> HandNames;
|
||||
public readonly string? ActiveHand;
|
||||
public readonly Dictionary<string, Hand> Hands;
|
||||
public readonly List<string> SortedHands;
|
||||
public readonly string? ActiveHandId;
|
||||
|
||||
public HandsComponentState(HandsComponent handComp)
|
||||
{
|
||||
// cloning lists because of test networking.
|
||||
Hands = new(handComp.Hands.Values);
|
||||
HandNames = new(handComp.SortedHands);
|
||||
ActiveHand = handComp.ActiveHand?.Name;
|
||||
Hands = new(handComp.Hands);
|
||||
SortedHands = new(handComp.SortedHands);
|
||||
ActiveHandId = handComp.ActiveHandId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,17 +6,17 @@ namespace Content.Shared.Hands.EntitySystems;
|
||||
// These functions are mostly unused except for some AI operator stuff
|
||||
// Nothing stops them from being used in general. If they ever get used elsewhere, then this file probably needs to be renamed.
|
||||
|
||||
public abstract partial class SharedHandsSystem : EntitySystem
|
||||
public abstract partial class SharedHandsSystem
|
||||
{
|
||||
public bool TrySelect(EntityUid uid, EntityUid? entity, HandsComponent? handsComp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
return false;
|
||||
|
||||
if (!IsHolding(uid, entity, out var hand, handsComp))
|
||||
if (!IsHolding((uid, handsComp), entity, out var hand))
|
||||
return false;
|
||||
|
||||
SetActiveHand(uid, hand, handsComp);
|
||||
SetActiveHand((uid, handsComp), hand);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26,9 +26,12 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
return false;
|
||||
|
||||
foreach (var hand in handsComp.Hands.Values)
|
||||
foreach (var hand in handsComp.Hands.Keys)
|
||||
{
|
||||
if (TryComp(hand.HeldEntity, out component))
|
||||
if (!TryGetHeldItem((uid, handsComp), hand, out var held))
|
||||
continue;
|
||||
|
||||
if (TryComp(held, out component))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using Content.Shared.Tag;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Hands.EntitySystems;
|
||||
|
||||
@@ -28,10 +29,10 @@ public abstract partial class SharedHandsSystem
|
||||
return;
|
||||
}
|
||||
|
||||
var gotUnequipped = new GotUnequippedHandEvent(uid, args.Entity, hand);
|
||||
var gotUnequipped = new GotUnequippedHandEvent(uid, args.Entity, hand.Value);
|
||||
RaiseLocalEvent(args.Entity, gotUnequipped);
|
||||
|
||||
var didUnequip = new DidUnequipHandEvent(uid, args.Entity, hand);
|
||||
var didUnequip = new DidUnequipHandEvent(uid, args.Entity, hand.Value);
|
||||
RaiseLocalEvent(uid, didUnequip);
|
||||
|
||||
if (TryComp(args.Entity, out VirtualItemComponent? @virtual))
|
||||
@@ -47,26 +48,29 @@ public abstract partial class SharedHandsSystem
|
||||
/// <summary>
|
||||
/// Checks whether an entity can drop a given entity. Will return false if they are not holding the entity.
|
||||
/// </summary>
|
||||
public bool CanDrop(EntityUid uid, EntityUid entity, HandsComponent? handsComp = null, bool checkActionBlocker = true)
|
||||
public bool CanDrop(Entity<HandsComponent?> ent, EntityUid entity, bool checkActionBlocker = true)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp))
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
if (!IsHolding(uid, entity, out var hand, handsComp))
|
||||
if (!IsHolding(ent, entity, out var hand))
|
||||
return false;
|
||||
|
||||
return CanDropHeld(uid, hand, checkActionBlocker);
|
||||
return CanDropHeld(ent, hand, checkActionBlocker);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the contents of a hand is able to be removed from its container.
|
||||
/// </summary>
|
||||
public bool CanDropHeld(EntityUid uid, Hand hand, bool checkActionBlocker = true)
|
||||
public bool CanDropHeld(EntityUid uid, string handId, bool checkActionBlocker = true)
|
||||
{
|
||||
if (hand.Container?.ContainedEntity is not {} held)
|
||||
if (!ContainerSystem.TryGetContainer(uid, handId, out var container))
|
||||
return false;
|
||||
|
||||
if (!ContainerSystem.CanRemove(held, hand.Container))
|
||||
if (container.ContainedEntities.FirstOrNull() is not {} held)
|
||||
return false;
|
||||
|
||||
if (!ContainerSystem.CanRemove(held, container))
|
||||
return false;
|
||||
|
||||
if (checkActionBlocker && !_actionBlocker.CanDrop(uid))
|
||||
@@ -78,98 +82,100 @@ public abstract partial class SharedHandsSystem
|
||||
/// <summary>
|
||||
/// Attempts to drop the item in the currently active hand.
|
||||
/// </summary>
|
||||
public bool TryDrop(EntityUid uid, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true, HandsComponent? handsComp = null)
|
||||
public bool TryDrop(Entity<HandsComponent?> ent, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp))
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
if (handsComp.ActiveHand == null)
|
||||
if (ent.Comp.ActiveHandId == null)
|
||||
return false;
|
||||
|
||||
return TryDrop(uid, handsComp.ActiveHand, targetDropLocation, checkActionBlocker, doDropInteraction, handsComp);
|
||||
return TryDrop(ent, ent.Comp.ActiveHandId, targetDropLocation, checkActionBlocker, doDropInteraction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drops an item at the target location.
|
||||
/// </summary>
|
||||
public bool TryDrop(EntityUid uid, EntityUid entity, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true, HandsComponent? handsComp = null)
|
||||
public bool TryDrop(Entity<HandsComponent?> ent, EntityUid entity, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp))
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
if (!IsHolding(uid, entity, out var hand, handsComp))
|
||||
if (!IsHolding(ent, entity, out var hand))
|
||||
return false;
|
||||
|
||||
return TryDrop(uid, hand, targetDropLocation, checkActionBlocker, doDropInteraction, handsComp);
|
||||
return TryDrop(ent, hand, targetDropLocation, checkActionBlocker, doDropInteraction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drops a hands contents at the target location.
|
||||
/// </summary>
|
||||
public bool TryDrop(EntityUid uid, Hand hand, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true, HandsComponent? handsComp = null)
|
||||
public bool TryDrop(Entity<HandsComponent?> ent, string handId, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp))
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
if (!CanDropHeld(uid, hand, checkActionBlocker))
|
||||
if (!CanDropHeld(ent, handId, checkActionBlocker))
|
||||
return false;
|
||||
|
||||
var entity = hand.HeldEntity!.Value;
|
||||
if (!TryGetHeldItem(ent, handId, out var entity))
|
||||
return false;
|
||||
|
||||
// if item is a fake item (like with pulling), just delete it rather than bothering with trying to drop it into the world
|
||||
if (TryComp(entity, out VirtualItemComponent? @virtual))
|
||||
_virtualSystem.DeleteVirtualItem((entity, @virtual), uid);
|
||||
_virtualSystem.DeleteVirtualItem((entity.Value, @virtual), ent);
|
||||
|
||||
if (TerminatingOrDeleted(entity))
|
||||
return true;
|
||||
|
||||
var itemXform = Transform(entity);
|
||||
var itemXform = Transform(entity.Value);
|
||||
if (itemXform.MapUid == null)
|
||||
return true;
|
||||
|
||||
var userXform = Transform(uid);
|
||||
var isInContainer = ContainerSystem.IsEntityOrParentInContainer(uid, xform: userXform);
|
||||
var userXform = Transform(ent);
|
||||
var isInContainer = ContainerSystem.IsEntityOrParentInContainer(ent, xform: userXform);
|
||||
|
||||
// if the user is in a container, drop the item inside the container
|
||||
if (isInContainer) {
|
||||
TransformSystem.DropNextTo((entity, itemXform), (uid, userXform));
|
||||
if (isInContainer)
|
||||
{
|
||||
TransformSystem.DropNextTo((entity.Value, itemXform), (ent, userXform));
|
||||
return true;
|
||||
}
|
||||
|
||||
// drop the item with heavy calculations from their hands and place it at the calculated interaction range position
|
||||
// The DoDrop is handle if there's no drop target
|
||||
DoDrop(uid, hand, doDropInteraction: doDropInteraction, handsComp);
|
||||
DoDrop(ent, handId, doDropInteraction: doDropInteraction);
|
||||
|
||||
// if there's no drop location stop here
|
||||
if (targetDropLocation == null)
|
||||
return true;
|
||||
|
||||
// otherwise, also move dropped item and rotate it properly according to grid/map
|
||||
var (itemPos, itemRot) = TransformSystem.GetWorldPositionRotation(entity);
|
||||
var (itemPos, itemRot) = TransformSystem.GetWorldPositionRotation(entity.Value);
|
||||
var origin = new MapCoordinates(itemPos, itemXform.MapID);
|
||||
var target = TransformSystem.ToMapCoordinates(targetDropLocation.Value);
|
||||
TransformSystem.SetWorldPositionRotation(entity, GetFinalDropCoordinates(uid, origin, target, entity), itemRot);
|
||||
TransformSystem.SetWorldPositionRotation(entity.Value, GetFinalDropCoordinates(ent, origin, target, entity.Value), itemRot);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to move a held item from a hand into a container that is not another hand, without dropping it on the floor in-between.
|
||||
/// </summary>
|
||||
public bool TryDropIntoContainer(EntityUid uid, EntityUid entity, BaseContainer targetContainer, bool checkActionBlocker = true, HandsComponent? handsComp = null)
|
||||
public bool TryDropIntoContainer(Entity<HandsComponent?> ent, EntityUid entity, BaseContainer targetContainer, bool checkActionBlocker = true)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp))
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
if (!IsHolding(uid, entity, out var hand, handsComp))
|
||||
if (!IsHolding(ent, entity, out var hand))
|
||||
return false;
|
||||
|
||||
if (!CanDropHeld(uid, hand, checkActionBlocker))
|
||||
if (!CanDropHeld(ent, hand, checkActionBlocker))
|
||||
return false;
|
||||
|
||||
if (!ContainerSystem.CanInsert(entity, targetContainer))
|
||||
return false;
|
||||
|
||||
DoDrop(uid, hand, false, handsComp);
|
||||
DoDrop(ent, hand, false);
|
||||
ContainerSystem.Insert(entity, targetContainer);
|
||||
return true;
|
||||
}
|
||||
@@ -202,34 +208,38 @@ public abstract partial class SharedHandsSystem
|
||||
/// <summary>
|
||||
/// Removes the contents of a hand from its container. Assumes that the removal is allowed. In general, you should not be calling this directly.
|
||||
/// </summary>
|
||||
public virtual void DoDrop(EntityUid uid, Hand hand, bool doDropInteraction = true, HandsComponent? handsComp = null, bool log = true)
|
||||
public virtual void DoDrop(Entity<HandsComponent?> ent,
|
||||
string handId,
|
||||
bool doDropInteraction = true,
|
||||
bool log = true)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp))
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return;
|
||||
|
||||
if (hand.Container?.ContainedEntity == null)
|
||||
if (!ContainerSystem.TryGetContainer(ent, handId, out var container))
|
||||
return;
|
||||
|
||||
var entity = hand.Container.ContainedEntity.Value;
|
||||
|
||||
if (TerminatingOrDeleted(uid) || TerminatingOrDeleted(entity))
|
||||
if (!TryGetHeldItem(ent, handId, out var entity))
|
||||
return;
|
||||
|
||||
if (!ContainerSystem.Remove(entity, hand.Container))
|
||||
if (TerminatingOrDeleted(ent) || TerminatingOrDeleted(entity))
|
||||
return;
|
||||
|
||||
if (!ContainerSystem.Remove(entity.Value, container))
|
||||
{
|
||||
Log.Error($"Failed to remove {ToPrettyString(entity)} from users hand container when dropping. User: {ToPrettyString(uid)}. Hand: {hand.Name}.");
|
||||
Log.Error($"Failed to remove {ToPrettyString(entity)} from users hand container when dropping. User: {ToPrettyString(ent)}. Hand: {handId}.");
|
||||
return;
|
||||
}
|
||||
|
||||
Dirty(uid, handsComp);
|
||||
Dirty(ent);
|
||||
|
||||
if (doDropInteraction)
|
||||
_interactionSystem.DroppedInteraction(uid, entity);
|
||||
_interactionSystem.DroppedInteraction(ent, entity.Value);
|
||||
|
||||
if (log)
|
||||
_adminLogger.Add(LogType.Drop, LogImpact.Low, $"{ToPrettyString(uid):user} dropped {ToPrettyString(entity):entity}");
|
||||
_adminLogger.Add(LogType.Drop, LogImpact.Low, $"{ToPrettyString(ent):user} dropped {ToPrettyString(entity):entity}");
|
||||
|
||||
if (hand == handsComp.ActiveHand)
|
||||
RaiseLocalEvent(entity, new HandDeselectedEvent(uid));
|
||||
if (handId == ent.Comp.ActiveHandId)
|
||||
RaiseLocalEvent(entity.Value, new HandDeselectedEvent(ent));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,20 +97,20 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
if (!_actionBlocker.CanInteract(session.AttachedEntity.Value, null))
|
||||
return;
|
||||
|
||||
if (component.ActiveHand == null || component.Hands.Count < 2)
|
||||
if (component.ActiveHandId == null || component.Hands.Count < 2)
|
||||
return;
|
||||
|
||||
var currentIndex = component.SortedHands.IndexOf(component.ActiveHand.Name);
|
||||
var currentIndex = component.SortedHands.IndexOf(component.ActiveHandId);
|
||||
var newActiveIndex = (currentIndex + (reverse ? -1 : 1) + component.Hands.Count) % component.Hands.Count;
|
||||
var nextHand = component.SortedHands[newActiveIndex];
|
||||
|
||||
TrySetActiveHand(session.AttachedEntity.Value, nextHand, component);
|
||||
TrySetActiveHand((session.AttachedEntity.Value, component), nextHand);
|
||||
}
|
||||
|
||||
private bool DropPressed(ICommonSession? session, EntityCoordinates coords, EntityUid netEntity)
|
||||
{
|
||||
if (TryComp(session?.AttachedEntity, out HandsComponent? hands) && hands.ActiveHand != null)
|
||||
TryDrop(session.AttachedEntity.Value, hands.ActiveHand, coords, handsComp: hands);
|
||||
if (TryComp(session?.AttachedEntity, out HandsComponent? hands) && hands.ActiveHandId != null)
|
||||
TryDrop((session.AttachedEntity.Value, hands), hands.ActiveHandId, coords);
|
||||
|
||||
// always send to server.
|
||||
return false;
|
||||
@@ -122,14 +122,14 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
return false;
|
||||
|
||||
Hand? hand;
|
||||
if (handName == null || !handsComp.Hands.TryGetValue(handName, out hand))
|
||||
hand = handsComp.ActiveHand;
|
||||
var hand = handName;
|
||||
if (!TryGetHand(uid, hand, out _))
|
||||
hand = handsComp.ActiveHandId;
|
||||
|
||||
if (hand?.HeldEntity is not { } held)
|
||||
if (!TryGetHeldItem((uid, handsComp), hand, out var held))
|
||||
return false;
|
||||
|
||||
return _interactionSystem.InteractionActivate(uid, held);
|
||||
return _interactionSystem.InteractionActivate(uid, held.Value);
|
||||
}
|
||||
|
||||
public bool TryInteractHandWithActiveHand(EntityUid uid, string handName, HandsComponent? handsComp = null)
|
||||
@@ -137,16 +137,13 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
return false;
|
||||
|
||||
if (handsComp.ActiveHandEntity == null)
|
||||
if (!TryGetActiveItem((uid, handsComp), out var activeHeldItem))
|
||||
return false;
|
||||
|
||||
if (!handsComp.Hands.TryGetValue(handName, out var hand))
|
||||
if (!TryGetHeldItem((uid, handsComp), handName, out var held))
|
||||
return false;
|
||||
|
||||
if (hand.HeldEntity == null)
|
||||
return false;
|
||||
|
||||
_interactionSystem.InteractUsing(uid, handsComp.ActiveHandEntity.Value, hand.HeldEntity.Value, Transform(hand.HeldEntity.Value).Coordinates);
|
||||
_interactionSystem.InteractUsing(uid, activeHeldItem.Value, held.Value, Transform(held.Value).Coordinates);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -155,17 +152,16 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
return false;
|
||||
|
||||
Hand? hand;
|
||||
if (handName == null || !handsComp.Hands.TryGetValue(handName, out hand))
|
||||
hand = handsComp.ActiveHand;
|
||||
var hand = handName;
|
||||
if (!TryGetHand(uid, hand, out _))
|
||||
hand = handsComp.ActiveHandId;
|
||||
|
||||
if (hand?.HeldEntity is not { } held)
|
||||
if (!TryGetHeldItem((uid, handsComp), hand, out var held))
|
||||
return false;
|
||||
|
||||
if (altInteract)
|
||||
return _interactionSystem.AltInteract(uid, held);
|
||||
else
|
||||
return _interactionSystem.UseInHandInteraction(uid, held);
|
||||
return _interactionSystem.AltInteract(uid, held.Value);
|
||||
return _interactionSystem.UseInHandInteraction(uid, held.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -176,22 +172,20 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
if (!Resolve(uid, ref handsComp))
|
||||
return false;
|
||||
|
||||
if (handsComp.ActiveHand == null || !handsComp.ActiveHand.IsEmpty)
|
||||
if (handsComp.ActiveHandId == null || !HandIsEmpty((uid, handsComp), handsComp.ActiveHandId))
|
||||
return false;
|
||||
|
||||
if (!handsComp.Hands.TryGetValue(handName, out var hand))
|
||||
if (!TryGetHeldItem((uid, handsComp), handName, out var entity))
|
||||
return false;
|
||||
|
||||
if (!CanDropHeld(uid, hand, checkActionBlocker))
|
||||
if (!CanDropHeld(uid, handName, checkActionBlocker))
|
||||
return false;
|
||||
|
||||
var entity = hand.HeldEntity!.Value;
|
||||
|
||||
if (!CanPickupToHand(uid, entity, handsComp.ActiveHand, checkActionBlocker, handsComp))
|
||||
if (!CanPickupToHand(uid, entity.Value, handsComp.ActiveHandId, checkActionBlocker, handsComp))
|
||||
return false;
|
||||
|
||||
DoDrop(uid, hand, false, handsComp, log:false);
|
||||
DoPickup(uid, handsComp.ActiveHand, entity, handsComp, log: false);
|
||||
DoDrop(uid, handName, false, log: false);
|
||||
DoPickup(uid, handsComp.ActiveHandId, entity.Value, handsComp, log: false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -200,19 +194,19 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (component.ActiveHandEntity.HasValue)
|
||||
if (TryGetActiveItem((uid, component), out var activeHeldItem))
|
||||
{
|
||||
// allow for the item to return a different entity, e.g. virtual items
|
||||
RaiseLocalEvent(component.ActiveHandEntity.Value, ref args);
|
||||
RaiseLocalEvent(activeHeldItem.Value, ref args);
|
||||
}
|
||||
|
||||
args.Used ??= component.ActiveHandEntity;
|
||||
args.Used ??= activeHeldItem;
|
||||
}
|
||||
|
||||
//TODO: Actually shows all items/clothing/etc.
|
||||
private void HandleExamined(EntityUid examinedUid, HandsComponent handsComp, ExaminedEvent args)
|
||||
{
|
||||
var heldItemNames = EnumerateHeld(examinedUid, handsComp)
|
||||
var heldItemNames = EnumerateHeld((examinedUid, handsComp))
|
||||
.Where(entity => !HasComp<VirtualItemComponent>(entity))
|
||||
.Select(item => FormattedMessage.EscapeText(Identity.Name(item, EntityManager)))
|
||||
.Select(itemName => Loc.GetString("comp-hands-examine-wrapper", ("item", itemName)))
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
using Content.Shared.Clothing.Components;
|
||||
using System.Diagnostics;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Item;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Hands.EntitySystems;
|
||||
|
||||
public abstract partial class SharedHandsSystem : EntitySystem
|
||||
public abstract partial class SharedHandsSystem
|
||||
{
|
||||
private void InitializePickup()
|
||||
{
|
||||
@@ -23,11 +23,11 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
return;
|
||||
}
|
||||
|
||||
var didEquip = new DidEquipHandEvent(uid, args.Entity, hand);
|
||||
RaiseLocalEvent(uid, didEquip, false);
|
||||
var didEquip = new DidEquipHandEvent(uid, args.Entity, hand.Value);
|
||||
RaiseLocalEvent(uid, didEquip);
|
||||
|
||||
var gotEquipped = new GotEquippedHandEvent(uid, args.Entity, hand);
|
||||
RaiseLocalEvent(args.Entity, gotEquipped, false);
|
||||
var gotEquipped = new GotEquippedHandEvent(uid, args.Entity, hand.Value);
|
||||
RaiseLocalEvent(args.Entity, gotEquipped);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -35,32 +35,6 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
/// </summary>
|
||||
public const float MaxAnimationRange = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to pick up an entity to a specific hand. If no explicit hand is specified, defaults to using the currently active hand.
|
||||
/// </summary>
|
||||
public bool TryPickup(
|
||||
EntityUid uid,
|
||||
EntityUid entity,
|
||||
string? handName = null,
|
||||
bool checkActionBlocker = true,
|
||||
bool animateUser = false,
|
||||
bool animate = true,
|
||||
HandsComponent? handsComp = null,
|
||||
ItemComponent? item = null)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
return false;
|
||||
|
||||
var hand = handsComp.ActiveHand;
|
||||
if (handName != null && !handsComp.Hands.TryGetValue(handName, out hand))
|
||||
return false;
|
||||
|
||||
if (hand == null)
|
||||
return false;
|
||||
|
||||
return TryPickup(uid, entity, hand, checkActionBlocker, animate, handsComp, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to pick up an item into any empty hand. Prioritizes the currently active hand.
|
||||
/// </summary>
|
||||
@@ -80,17 +54,21 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
return false;
|
||||
|
||||
if (!TryGetEmptyHand(uid, out var hand, handsComp))
|
||||
if (!TryGetEmptyHand((uid, handsComp), out var hand))
|
||||
return false;
|
||||
|
||||
return TryPickup(uid, entity, hand, checkActionBlocker, animate, handsComp, item);
|
||||
return TryPickup(uid, entity, hand, checkActionBlocker, animateUser, animate, handsComp, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to pick up an entity to a specific hand. If no explicit hand is specified, defaults to using the currently active hand.
|
||||
/// </summary>
|
||||
public bool TryPickup(
|
||||
EntityUid uid,
|
||||
EntityUid entity,
|
||||
Hand hand,
|
||||
string? handId = null,
|
||||
bool checkActionBlocker = true,
|
||||
bool animateUser = false,
|
||||
bool animate = true,
|
||||
HandsComponent? handsComp = null,
|
||||
ItemComponent? item = null)
|
||||
@@ -98,10 +76,15 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
return false;
|
||||
|
||||
handId ??= handsComp.ActiveHandId;
|
||||
|
||||
if (handId == null)
|
||||
return false;
|
||||
|
||||
if (!Resolve(entity, ref item, false))
|
||||
return false;
|
||||
|
||||
if (!CanPickupToHand(uid, entity, hand, checkActionBlocker, handsComp, item))
|
||||
if (!CanPickupToHand(uid, entity, handId, checkActionBlocker, handsComp, item))
|
||||
return false;
|
||||
|
||||
if (animate)
|
||||
@@ -119,7 +102,7 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
_storage.PlayPickupAnimation(entity, initialPosition, xform.Coordinates, itemXform.LocalRotation, uid);
|
||||
}
|
||||
}
|
||||
DoPickup(uid, hand, entity, handsComp);
|
||||
DoPickup(uid, handId, entity, handsComp);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -129,20 +112,20 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
/// By default it does check if it's possible to drop items.
|
||||
/// </summary>
|
||||
public bool TryForcePickup(
|
||||
EntityUid uid,
|
||||
Entity<HandsComponent?> ent,
|
||||
EntityUid entity,
|
||||
Hand hand,
|
||||
string hand,
|
||||
bool checkActionBlocker = true,
|
||||
bool animate = true,
|
||||
HandsComponent? handsComp = null,
|
||||
ItemComponent? item = null)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
TryDrop(uid, hand, checkActionBlocker: checkActionBlocker, handsComp: handsComp);
|
||||
TryDrop(ent, hand, checkActionBlocker: checkActionBlocker);
|
||||
|
||||
return TryPickup(uid, entity, hand, checkActionBlocker, animate, handsComp, item);
|
||||
return TryPickup(ent, entity, hand, checkActionBlocker, animate: animate, handsComp: handsComp, item: item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -157,9 +140,9 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
if (TryPickupAnyHand(uid, entity, checkActionBlocker: checkActionBlocker, handsComp: handsComp))
|
||||
return true;
|
||||
|
||||
foreach (var hand in handsComp.Hands.Values)
|
||||
foreach (var hand in handsComp.Hands.Keys)
|
||||
{
|
||||
if (TryDrop(uid, hand, checkActionBlocker: checkActionBlocker, handsComp: handsComp) &&
|
||||
if (TryDrop((uid, handsComp), hand, checkActionBlocker: checkActionBlocker) &&
|
||||
TryPickup(uid, entity, hand, checkActionBlocker: checkActionBlocker, handsComp: handsComp))
|
||||
{
|
||||
return true;
|
||||
@@ -173,7 +156,7 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
return false;
|
||||
|
||||
if (!TryGetEmptyHand(uid, out var hand, handsComp))
|
||||
if (!TryGetEmptyHand((uid, handsComp), out var hand))
|
||||
return false;
|
||||
|
||||
return CanPickupToHand(uid, entity, hand, checkActionBlocker, handsComp, item);
|
||||
@@ -182,13 +165,15 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
/// <summary>
|
||||
/// Checks whether a given item will fit into a specific user's hand. Unless otherwise specified, this will also check the general CanPickup action blocker.
|
||||
/// </summary>
|
||||
public bool CanPickupToHand(EntityUid uid, EntityUid entity, Hand hand, bool checkActionBlocker = true, HandsComponent? handsComp = null, ItemComponent? item = null)
|
||||
public bool CanPickupToHand(EntityUid uid, EntityUid entity, string handId, bool checkActionBlocker = true, HandsComponent? handsComp = null, ItemComponent? item = null)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
return false;
|
||||
|
||||
var handContainer = hand.Container;
|
||||
if (handContainer == null || handContainer.ContainedEntity != null)
|
||||
if (!ContainerSystem.TryGetContainer(uid, handId, out var handContainer))
|
||||
return false;
|
||||
|
||||
if (handContainer.ContainedEntities.FirstOrNull() != null)
|
||||
return false;
|
||||
|
||||
if (!Resolve(entity, ref item, false))
|
||||
@@ -231,8 +216,7 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
{
|
||||
if (uid == null
|
||||
|| !Resolve(uid.Value, ref handsComp, false)
|
||||
|| !TryGetEmptyHand(uid.Value, out var hand, handsComp)
|
||||
|| !TryPickup(uid.Value, entity, hand, checkActionBlocker, animate, handsComp, item))
|
||||
|| !TryPickupAnyHand(uid.Value, entity, checkActionBlocker, animateUser, animate, handsComp, item))
|
||||
{
|
||||
// TODO make this check upwards for any container, and parent to that.
|
||||
// Currently this just checks the direct parent, so items can still teleport through containers.
|
||||
@@ -248,18 +232,20 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
/// <summary>
|
||||
/// Puts an entity into the player's hand, assumes that the insertion is allowed. In general, you should not be calling this function directly.
|
||||
/// </summary>
|
||||
public virtual void DoPickup(EntityUid uid, Hand hand, EntityUid entity, HandsComponent? hands = null, bool log = true)
|
||||
public virtual void DoPickup(EntityUid uid, string hand, EntityUid entity, HandsComponent? hands = null, bool log = true)
|
||||
{
|
||||
if (!Resolve(uid, ref hands))
|
||||
return;
|
||||
|
||||
var handContainer = hand.Container;
|
||||
if (handContainer == null || handContainer.ContainedEntity != null)
|
||||
if (!ContainerSystem.TryGetContainer(uid, hand, out var handContainer))
|
||||
return;
|
||||
|
||||
if (handContainer.ContainedEntities.FirstOrNull() != null)
|
||||
return;
|
||||
|
||||
if (!ContainerSystem.Insert(entity, handContainer))
|
||||
{
|
||||
Log.Error($"Failed to insert {ToPrettyString(entity)} into users hand container when picking up. User: {ToPrettyString(uid)}. Hand: {hand.Name}.");
|
||||
Log.Error($"Failed to insert {ToPrettyString(entity)} into users hand container when picking up. User: {ToPrettyString(uid)}. Hand: {hand}.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -270,7 +256,7 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
|
||||
Dirty(uid, hands);
|
||||
|
||||
if (hand == hands.ActiveHand)
|
||||
RaiseLocalEvent(entity, new HandSelectedEvent(uid), false);
|
||||
if (hand == hands.ActiveHandId)
|
||||
RaiseLocalEvent(entity, new HandSelectedEvent(uid));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public abstract partial class SharedHandsSystem
|
||||
{
|
||||
var ev = new HeldRelayedEvent<T>(args);
|
||||
|
||||
foreach (var held in EnumerateHeld(entity, entity.Comp))
|
||||
foreach (var held in EnumerateHeld(entity.AsNullable()))
|
||||
{
|
||||
RaiseLocalEvent(held, ref ev);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Content.Shared.Inventory.VirtualItem;
|
||||
using Content.Shared.Storage.EntitySystems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Hands.EntitySystems;
|
||||
|
||||
@@ -23,6 +24,8 @@ public abstract partial class SharedHandsSystem
|
||||
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
|
||||
[Dependency] private readonly SharedVirtualItemSystem _virtualSystem = default!;
|
||||
|
||||
public event Action<Entity<HandsComponent>, string, HandLocation>? OnPlayerAddHand;
|
||||
public event Action<Entity<HandsComponent>, string>? OnPlayerRemoveHand;
|
||||
protected event Action<Entity<HandsComponent>?>? OnHandSetActive;
|
||||
|
||||
public override void Initialize()
|
||||
@@ -33,6 +36,9 @@ public abstract partial class SharedHandsSystem
|
||||
InitializeDrop();
|
||||
InitializePickup();
|
||||
InitializeRelay();
|
||||
|
||||
SubscribeLocalEvent<HandsComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<HandsComponent, MapInitEvent>(OnMapInit);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
@@ -41,71 +47,94 @@ public abstract partial class SharedHandsSystem
|
||||
CommandBinds.Unregister<SharedHandsSystem>();
|
||||
}
|
||||
|
||||
public virtual void AddHand(EntityUid uid, string handName, HandLocation handLocation, HandsComponent? handsComp = null)
|
||||
private void OnInit(Entity<HandsComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
return;
|
||||
|
||||
if (handsComp.Hands.ContainsKey(handName))
|
||||
return;
|
||||
|
||||
var container = ContainerSystem.EnsureContainer<ContainerSlot>(uid, handName);
|
||||
container.OccludesLight = false;
|
||||
|
||||
var newHand = new Hand(handName, handLocation, container);
|
||||
handsComp.Hands.Add(handName, newHand);
|
||||
handsComp.SortedHands.Add(handName);
|
||||
|
||||
if (handsComp.ActiveHand == null)
|
||||
SetActiveHand(uid, newHand, handsComp);
|
||||
|
||||
RaiseLocalEvent(uid, new HandCountChangedEvent(uid));
|
||||
Dirty(uid, handsComp);
|
||||
var container = EnsureComp<ContainerManagerComponent>(ent);
|
||||
foreach (var id in ent.Comp.Hands.Keys)
|
||||
{
|
||||
ContainerSystem.EnsureContainer<ContainerSlot>(ent, id, container);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void RemoveHand(EntityUid uid, string handName, HandsComponent? handsComp = null)
|
||||
private void OnMapInit(Entity<HandsComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
if (ent.Comp.ActiveHandId == null)
|
||||
SetActiveHand(ent.AsNullable(), ent.Comp.SortedHands.FirstOrDefault());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a hand with the given container id and supplied location to the specified entity.
|
||||
/// </summary>
|
||||
public void AddHand(Entity<HandsComponent?> ent, string handName, HandLocation handLocation)
|
||||
{
|
||||
AddHand(ent, handName, new Hand(handLocation));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a hand with the given container id and supplied hand definition to the given entity.
|
||||
/// </summary>
|
||||
public void AddHand(Entity<HandsComponent?> ent, string handName, Hand hand)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return;
|
||||
|
||||
if (!handsComp.Hands.Remove(handName, out var hand))
|
||||
if (ent.Comp.Hands.ContainsKey(handName))
|
||||
return;
|
||||
|
||||
handsComp.SortedHands.Remove(hand.Name);
|
||||
TryDrop(uid, hand, null, false, true, handsComp);
|
||||
if (hand.Container != null)
|
||||
ContainerSystem.ShutdownContainer(hand.Container);
|
||||
var container = ContainerSystem.EnsureContainer<ContainerSlot>(ent, handName);
|
||||
container.OccludesLight = false;
|
||||
|
||||
if (handsComp.ActiveHand == hand)
|
||||
TrySetActiveHand(uid, handsComp.SortedHands.FirstOrDefault(), handsComp);
|
||||
ent.Comp.Hands.Add(handName, hand);
|
||||
ent.Comp.SortedHands.Add(handName);
|
||||
Dirty(ent);
|
||||
|
||||
RaiseLocalEvent(uid, new HandCountChangedEvent(uid));
|
||||
Dirty(uid, handsComp);
|
||||
OnPlayerAddHand?.Invoke((ent, ent.Comp), handName, hand.Location);
|
||||
|
||||
if (ent.Comp.ActiveHandId == null)
|
||||
SetActiveHand(ent, handName);
|
||||
|
||||
RaiseLocalEvent(ent, new HandCountChangedEvent(ent));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified hand from the specified entity
|
||||
/// </summary>
|
||||
public virtual void RemoveHand(Entity<HandsComponent?> ent, string handName)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return;
|
||||
|
||||
OnPlayerRemoveHand?.Invoke((ent, ent.Comp), handName);
|
||||
|
||||
TryDrop(ent, handName, null, false);
|
||||
|
||||
if (!ent.Comp.Hands.Remove(handName))
|
||||
return;
|
||||
|
||||
if (ContainerSystem.TryGetContainer(ent, handName, out var container))
|
||||
ContainerSystem.ShutdownContainer(container);
|
||||
|
||||
ent.Comp.SortedHands.Remove(handName);
|
||||
if (ent.Comp.ActiveHandId == handName)
|
||||
TrySetActiveHand(ent, ent.Comp.SortedHands.FirstOrDefault());
|
||||
|
||||
RaiseLocalEvent(ent, new HandCountChangedEvent(ent));
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets rid of all the entity's hands.
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="handsComp"></param>
|
||||
public void RemoveHands(EntityUid uid, HandsComponent? handsComp = null)
|
||||
public void RemoveHands(Entity<HandsComponent?> ent)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp))
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return;
|
||||
|
||||
RemoveHands(uid, EnumerateHands(uid), handsComp);
|
||||
}
|
||||
|
||||
private void RemoveHands(EntityUid uid, IEnumerable<Hand> hands, HandsComponent handsComp)
|
||||
{
|
||||
if (!hands.Any())
|
||||
return;
|
||||
|
||||
var hand = hands.First();
|
||||
RemoveHand(uid, hand.Name, handsComp);
|
||||
|
||||
// Repeats it for any additional hands.
|
||||
RemoveHands(uid, hands, handsComp);
|
||||
var handIds = new List<string>(ent.Comp.Hands.Keys);
|
||||
foreach (var handId in handIds)
|
||||
{
|
||||
RemoveHand(ent, handId);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSetHand(RequestSetHandEvent msg, EntitySessionEventArgs eventArgs)
|
||||
@@ -119,15 +148,15 @@ public abstract partial class SharedHandsSystem
|
||||
/// <summary>
|
||||
/// Get any empty hand. Prioritizes the currently active hand.
|
||||
/// </summary>
|
||||
public bool TryGetEmptyHand(EntityUid uid, [NotNullWhen(true)] out Hand? emptyHand, HandsComponent? handComp = null)
|
||||
public bool TryGetEmptyHand(Entity<HandsComponent?> ent, [NotNullWhen(true)] out string? emptyHand)
|
||||
{
|
||||
emptyHand = null;
|
||||
if (!Resolve(uid, ref handComp, false))
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
foreach (var hand in EnumerateHands(uid, handComp))
|
||||
foreach (var hand in EnumerateHands(ent))
|
||||
{
|
||||
if (hand.IsEmpty)
|
||||
if (HandIsEmpty(ent, hand))
|
||||
{
|
||||
emptyHand = hand;
|
||||
return true;
|
||||
@@ -137,28 +166,20 @@ public abstract partial class SharedHandsSystem
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetActiveHand(Entity<HandsComponent?> entity, [NotNullWhen(true)] out Hand? hand)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp, false))
|
||||
{
|
||||
hand = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
hand = entity.Comp.ActiveHand;
|
||||
return hand != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve the item held in the entity's active hand.
|
||||
/// </summary>
|
||||
public bool TryGetActiveItem(Entity<HandsComponent?> entity, [NotNullWhen(true)] out EntityUid? item)
|
||||
{
|
||||
if (!TryGetActiveHand(entity, out var hand))
|
||||
{
|
||||
item = null;
|
||||
item = null;
|
||||
if (!Resolve(entity, ref entity.Comp, false))
|
||||
return false;
|
||||
}
|
||||
|
||||
item = hand.HeldEntity;
|
||||
return item != null;
|
||||
if (!TryGetHeldItem(entity, entity.Comp.ActiveHandId, out var held))
|
||||
return false;
|
||||
|
||||
item = held;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -174,55 +195,73 @@ public abstract partial class SharedHandsSystem
|
||||
return item.Value;
|
||||
}
|
||||
|
||||
public Hand? GetActiveHand(Entity<HandsComponent?> entity)
|
||||
/// <summary>
|
||||
/// Gets the current active hand's Id for the specified entity
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
public string? GetActiveHand(Entity<HandsComponent?> entity)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp))
|
||||
if (!Resolve(entity, ref entity.Comp, false))
|
||||
return null;
|
||||
|
||||
return entity.Comp.ActiveHand;
|
||||
return entity.Comp.ActiveHandId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current active hand's held entity for the specified entity
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
public EntityUid? GetActiveItem(Entity<HandsComponent?> entity)
|
||||
{
|
||||
return GetActiveHand(entity)?.HeldEntity;
|
||||
if (!Resolve(entity, ref entity.Comp, false))
|
||||
return null;
|
||||
|
||||
return GetHeldItem(entity, entity.Comp.ActiveHandId);
|
||||
}
|
||||
|
||||
public bool ActiveHandIsEmpty(Entity<HandsComponent?> entity)
|
||||
{
|
||||
return GetActiveItem(entity) == null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerate over hands, starting with the currently active hand.
|
||||
/// </summary>
|
||||
public IEnumerable<Hand> EnumerateHands(EntityUid uid, HandsComponent? handsComp = null)
|
||||
public IEnumerable<string> EnumerateHands(Entity<HandsComponent?> ent)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
yield break;
|
||||
|
||||
if (handsComp.ActiveHand != null)
|
||||
yield return handsComp.ActiveHand;
|
||||
if (ent.Comp.ActiveHandId != null)
|
||||
yield return ent.Comp.ActiveHandId;
|
||||
|
||||
foreach (var name in handsComp.SortedHands)
|
||||
foreach (var name in ent.Comp.SortedHands)
|
||||
{
|
||||
if (name != handsComp.ActiveHand?.Name)
|
||||
yield return handsComp.Hands[name];
|
||||
if (name != ent.Comp.ActiveHandId)
|
||||
yield return name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerate over held items, starting with the item in the currently active hand (if there is one).
|
||||
/// </summary>
|
||||
public IEnumerable<EntityUid> EnumerateHeld(EntityUid uid, HandsComponent? handsComp = null)
|
||||
public IEnumerable<EntityUid> EnumerateHeld(Entity<HandsComponent?> ent)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
yield break;
|
||||
|
||||
if (handsComp.ActiveHandEntity != null)
|
||||
yield return handsComp.ActiveHandEntity.Value;
|
||||
if (TryGetActiveItem(ent, out var activeHeld))
|
||||
yield return activeHeld.Value;
|
||||
|
||||
foreach (var name in handsComp.SortedHands)
|
||||
foreach (var name in ent.Comp.SortedHands)
|
||||
{
|
||||
if (name == handsComp.ActiveHand?.Name)
|
||||
if (name == ent.Comp.ActiveHandId)
|
||||
continue;
|
||||
|
||||
if (handsComp.Hands[name].HeldEntity is { } held)
|
||||
yield return held;
|
||||
if (TryGetHeldItem(ent, name, out var held))
|
||||
yield return held.Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,18 +270,17 @@ public abstract partial class SharedHandsSystem
|
||||
/// </summary>
|
||||
/// <returns>True if the active hand was set to a NEW value. Setting it to the same value returns false and does
|
||||
/// not trigger interactions.</returns>
|
||||
public virtual bool TrySetActiveHand(EntityUid uid, string? name, HandsComponent? handComp = null)
|
||||
public bool TrySetActiveHand(Entity<HandsComponent?> ent, string? name)
|
||||
{
|
||||
if (!Resolve(uid, ref handComp))
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
if (name == handComp.ActiveHand?.Name)
|
||||
if (name == ent.Comp.ActiveHandId)
|
||||
return false;
|
||||
|
||||
Hand? hand = null;
|
||||
if (name != null && !handComp.Hands.TryGetValue(name, out hand))
|
||||
if (name != null && !ent.Comp.Hands.ContainsKey(name))
|
||||
return false;
|
||||
return SetActiveHand(uid, hand, handComp);
|
||||
return SetActiveHand(ent, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -250,50 +288,50 @@ public abstract partial class SharedHandsSystem
|
||||
/// </summary>
|
||||
/// <returns>True if the active hand was set to a NEW value. Setting it to the same value returns false and does
|
||||
/// not trigger interactions.</returns>
|
||||
public bool SetActiveHand(EntityUid uid, Hand? hand, HandsComponent? handComp = null)
|
||||
public bool SetActiveHand(Entity<HandsComponent?> ent, string? handId)
|
||||
{
|
||||
if (!Resolve(uid, ref handComp))
|
||||
if (!Resolve(ent, ref ent.Comp))
|
||||
return false;
|
||||
|
||||
if (hand == handComp.ActiveHand)
|
||||
if (handId == ent.Comp.ActiveHandId)
|
||||
return false;
|
||||
|
||||
if (handComp.ActiveHand?.HeldEntity is { } held)
|
||||
RaiseLocalEvent(held, new HandDeselectedEvent(uid));
|
||||
if (TryGetHeldItem(ent, handId, out var oldHeld))
|
||||
RaiseLocalEvent(oldHeld.Value, new HandDeselectedEvent(ent));
|
||||
|
||||
if (hand == null)
|
||||
if (handId == null)
|
||||
{
|
||||
handComp.ActiveHand = null;
|
||||
ent.Comp.ActiveHandId = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
handComp.ActiveHand = hand;
|
||||
OnHandSetActive?.Invoke((uid, handComp));
|
||||
ent.Comp.ActiveHandId = handId;
|
||||
OnHandSetActive?.Invoke((ent, ent.Comp));
|
||||
|
||||
if (hand.HeldEntity != null)
|
||||
RaiseLocalEvent(hand.HeldEntity.Value, new HandSelectedEvent(uid));
|
||||
if (TryGetHeldItem(ent, handId, out var newHeld))
|
||||
RaiseLocalEvent(newHeld.Value, new HandSelectedEvent(ent));
|
||||
|
||||
Dirty(uid, handComp);
|
||||
Dirty(ent);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsHolding(Entity<HandsComponent?> entity, [NotNullWhen(true)] EntityUid? item)
|
||||
{
|
||||
return IsHolding(entity, item, out _, entity);
|
||||
return IsHolding(entity, item, out _);
|
||||
}
|
||||
|
||||
public bool IsHolding(EntityUid uid, [NotNullWhen(true)] EntityUid? entity, [NotNullWhen(true)] out Hand? inHand, HandsComponent? handsComp = null)
|
||||
public bool IsHolding(Entity<HandsComponent?> ent, [NotNullWhen(true)] EntityUid? entity, [NotNullWhen(true)] out string? inHand)
|
||||
{
|
||||
inHand = null;
|
||||
if (entity == null)
|
||||
return false;
|
||||
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
foreach (var hand in handsComp.Hands.Values)
|
||||
foreach (var hand in ent.Comp.Hands.Keys)
|
||||
{
|
||||
if (hand.HeldEntity == entity)
|
||||
if (GetHeldItem(ent, hand) == entity)
|
||||
{
|
||||
inHand = hand;
|
||||
return true;
|
||||
@@ -303,23 +341,89 @@ public abstract partial class SharedHandsSystem
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetHand(EntityUid handsUid, string handId, [NotNullWhen(true)] out Hand? hand,
|
||||
HandsComponent? hands = null)
|
||||
/// <summary>
|
||||
/// Attempts to retrieve the associated hand struct corresponding to a hand ID on a given entity.
|
||||
/// </summary>
|
||||
public bool TryGetHand(Entity<HandsComponent?> ent, [NotNullWhen(true)] string? handId, [NotNullWhen(true)] out Hand? hand)
|
||||
{
|
||||
hand = null;
|
||||
|
||||
if (!Resolve(handsUid, ref hands))
|
||||
if (handId == null)
|
||||
return false;
|
||||
|
||||
return hands.Hands.TryGetValue(handId, out hand);
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
if (!ent.Comp.Hands.TryGetValue(handId, out var handsHand))
|
||||
return false;
|
||||
|
||||
hand = handsHand;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item currently held in the entity's specified hand. Returns null if no hands are present or there is no item.
|
||||
/// </summary>
|
||||
public EntityUid? GetHeldItem(Entity<HandsComponent?> ent, string? handId)
|
||||
{
|
||||
TryGetHeldItem(ent, handId, out var held);
|
||||
return held;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item currently held in the entity's specified hand. Returns false if no hands are present or there is no item.
|
||||
/// </summary>
|
||||
public bool TryGetHeldItem(Entity<HandsComponent?> ent, string? handId, [NotNullWhen(true)] out EntityUid? held)
|
||||
{
|
||||
held = null;
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
// Sanity check to make sure this is actually a hand.
|
||||
if (handId == null || !ent.Comp.Hands.ContainsKey(handId))
|
||||
return false;
|
||||
|
||||
if (!ContainerSystem.TryGetContainer(ent, handId, out var container))
|
||||
return false;
|
||||
|
||||
held = container.ContainedEntities.FirstOrNull();
|
||||
return held != null;
|
||||
}
|
||||
|
||||
public bool HandIsEmpty(Entity<HandsComponent?> ent, string handId)
|
||||
{
|
||||
return GetHeldItem(ent, handId) == null;
|
||||
}
|
||||
|
||||
public int GetHandCount(Entity<HandsComponent?> ent)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return 0;
|
||||
|
||||
return ent.Comp.Hands.Count;
|
||||
}
|
||||
|
||||
public int CountFreeHands(Entity<HandsComponent?> ent)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return 0;
|
||||
|
||||
var free = 0;
|
||||
foreach (var name in ent.Comp.Hands.Keys)
|
||||
{
|
||||
if (HandIsEmpty(ent, name))
|
||||
free++;
|
||||
}
|
||||
|
||||
return free;
|
||||
}
|
||||
|
||||
public int CountFreeableHands(Entity<HandsComponent> hands)
|
||||
{
|
||||
var freeable = 0;
|
||||
foreach (var hand in hands.Comp.Hands.Values)
|
||||
foreach (var name in hands.Comp.Hands.Keys)
|
||||
{
|
||||
if (hand.IsEmpty || CanDropHeld(hands, hand))
|
||||
if (HandIsEmpty(hands.AsNullable(), name) || CanDropHeld(hands, name))
|
||||
freeable++;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using Content.Shared.Database;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Input;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Interaction.Events;
|
||||
@@ -58,6 +59,7 @@ namespace Content.Shared.Interaction
|
||||
[Dependency] private readonly ISharedChatManager _chat = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
[Dependency] private readonly PullingSystem _pullSystem = default!;
|
||||
[Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!;
|
||||
@@ -343,7 +345,7 @@ namespace Content.Shared.Interaction
|
||||
public bool CombatModeCanHandInteract(EntityUid user, EntityUid? target)
|
||||
{
|
||||
// Always allow attack in these cases
|
||||
if (target == null || !_handsQuery.TryComp(user, out var hands) || hands.ActiveHand?.HeldEntity is not null)
|
||||
if (target == null || !_handsQuery.TryComp(user, out var hands) || _hands.GetActiveItem((user, hands)) is not null)
|
||||
return false;
|
||||
|
||||
// Only eat input if:
|
||||
|
||||
@@ -64,10 +64,10 @@ public sealed class SmartEquipSystem : EntitySystem
|
||||
return;
|
||||
|
||||
// early out if we don't have any hands or a valid inventory slot
|
||||
if (!TryComp<HandsComponent>(uid, out var hands) || hands.ActiveHand == null)
|
||||
if (!TryComp<HandsComponent>(uid, out var hands) || hands.ActiveHandId == null)
|
||||
return;
|
||||
|
||||
var handItem = hands.ActiveHand.HeldEntity;
|
||||
var handItem = _hands.GetActiveItem((uid, hands));
|
||||
|
||||
// can the user interact, and is the item interactable? e.g. virtual items
|
||||
if (!_actionBlocker.CanInteract(uid, handItem))
|
||||
@@ -80,7 +80,7 @@ public sealed class SmartEquipSystem : EntitySystem
|
||||
}
|
||||
|
||||
// early out if we have an item and cant drop it at all
|
||||
if (handItem != null && !_hands.CanDropHeld(uid, hands.ActiveHand))
|
||||
if (hands.ActiveHandId != null && !_hands.CanDropHeld(uid, hands.ActiveHandId))
|
||||
{
|
||||
_popup.PopupClient(Loc.GetString("smart-equip-cant-drop"), uid, uid);
|
||||
return;
|
||||
@@ -121,7 +121,7 @@ public sealed class SmartEquipSystem : EntitySystem
|
||||
return;
|
||||
}
|
||||
|
||||
_hands.TryDrop(uid, hands.ActiveHand, handsComp: hands);
|
||||
_hands.TryDrop((uid, hands), hands.ActiveHandId!);
|
||||
_inventory.TryEquip(uid, handItem.Value, equipmentSlot, predicted: true, checkDoafter:true);
|
||||
return;
|
||||
}
|
||||
@@ -149,7 +149,7 @@ public sealed class SmartEquipSystem : EntitySystem
|
||||
return;
|
||||
}
|
||||
|
||||
_hands.TryDrop(uid, hands.ActiveHand, handsComp: hands);
|
||||
_hands.TryDrop((uid, hands), hands.ActiveHandId!);
|
||||
_storage.Insert(slotItem, handItem.Value, out var stacked, out _);
|
||||
|
||||
// if the hand item stacked with the things in inventory, but there's no more space left for the rest
|
||||
|
||||
@@ -83,7 +83,7 @@ public abstract partial class InventorySystem
|
||||
if (!TryComp(actor, out InventoryComponent? inventory) || !TryComp<HandsComponent>(actor, out var hands))
|
||||
return;
|
||||
|
||||
var held = hands.ActiveHandEntity;
|
||||
var held = _handsSystem.GetActiveItem((actor, hands));
|
||||
TryGetSlotEntity(actor, ev.Slot, out var itemUid, inventory);
|
||||
|
||||
// attempt to perform some interaction
|
||||
@@ -115,7 +115,7 @@ public abstract partial class InventorySystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_handsSystem.CanDropHeld(actor, hands.ActiveHand!, checkActionBlocker: false))
|
||||
if (!_handsSystem.CanDropHeld(actor, hands.ActiveHandId!, checkActionBlocker: false))
|
||||
return;
|
||||
|
||||
RaiseLocalEvent(held.Value, new HandDeselectedEvent(actor));
|
||||
|
||||
@@ -16,12 +16,9 @@ public partial class InventorySystem
|
||||
{
|
||||
if (Resolve(user.Owner, ref user.Comp1, false))
|
||||
{
|
||||
foreach (var hand in user.Comp1.Hands.Values)
|
||||
foreach (var held in _handsSystem.EnumerateHeld(user))
|
||||
{
|
||||
if (hand.HeldEntity == null)
|
||||
continue;
|
||||
|
||||
yield return hand.HeldEntity.Value;
|
||||
yield return held;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
@@ -8,7 +7,6 @@ using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Inventory.VirtualItem;
|
||||
@@ -88,9 +86,9 @@ public abstract class SharedVirtualItemSystem : EntitySystem
|
||||
|
||||
// if the user is holding the real item the virtual item points to,
|
||||
// we allow them to use it in the interaction
|
||||
foreach (var hand in _handsSystem.EnumerateHands(args.User))
|
||||
foreach (var held in _handsSystem.EnumerateHeld(args.User))
|
||||
{
|
||||
if (hand.HeldEntity == ent.Comp.BlockingEntity)
|
||||
if (held == ent.Comp.BlockingEntity)
|
||||
{
|
||||
args.Used = ent.Comp.BlockingEntity;
|
||||
return;
|
||||
@@ -112,7 +110,7 @@ public abstract class SharedVirtualItemSystem : EntitySystem
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="TrySpawnVirtualItemInHand(Robust.Shared.GameObjects.EntityUid,Robust.Shared.GameObjects.EntityUid,bool)"/>
|
||||
public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem, bool dropOthers = false, Hand? empty = null)
|
||||
public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem, bool dropOthers = false, string? empty = null)
|
||||
{
|
||||
virtualItem = null;
|
||||
if (empty == null && !_handsSystem.TryGetEmptyHand(user, out empty))
|
||||
@@ -122,7 +120,7 @@ public abstract class SharedVirtualItemSystem : EntitySystem
|
||||
|
||||
foreach (var hand in _handsSystem.EnumerateHands(user))
|
||||
{
|
||||
if (hand.HeldEntity is not { } held)
|
||||
if (!_handsSystem.TryGetHeldItem(user, hand, out var held))
|
||||
continue;
|
||||
|
||||
if (held == blockingEnt)
|
||||
@@ -155,11 +153,11 @@ public abstract class SharedVirtualItemSystem : EntitySystem
|
||||
/// </summary>
|
||||
public void DeleteInHandsMatching(EntityUid user, EntityUid matching)
|
||||
{
|
||||
foreach (var hand in _handsSystem.EnumerateHands(user))
|
||||
foreach (var held in _handsSystem.EnumerateHeld(user))
|
||||
{
|
||||
if (TryComp(hand.HeldEntity, out VirtualItemComponent? virt) && virt.BlockingEntity == matching)
|
||||
if (TryComp(held, out VirtualItemComponent? virt) && virt.BlockingEntity == matching)
|
||||
{
|
||||
DeleteVirtualItem((hand.HeldEntity.Value, virt), user);
|
||||
DeleteVirtualItem((held, virt), user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Inventory.VirtualItem;
|
||||
using Content.Shared.Popups;
|
||||
@@ -38,7 +37,7 @@ public sealed class MultiHandedItemSystem : EntitySystem
|
||||
|
||||
private void OnAttemptPickup(Entity<MultiHandedItemComponent> ent, ref GettingPickedUpAttemptEvent args)
|
||||
{
|
||||
if (TryComp<HandsComponent>(args.User, out var hands) && hands.CountFreeHands() >= ent.Comp.HandsNeeded)
|
||||
if (_hands.CountFreeHands(ent.Owner) >= ent.Comp.HandsNeeded)
|
||||
return;
|
||||
|
||||
args.Cancel();
|
||||
|
||||
@@ -110,7 +110,7 @@ public abstract class SharedItemSystem : EntitySystem
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = _handsSystem.TryPickup(args.User, uid, animateUser: false);
|
||||
args.Handled = _handsSystem.TryPickup(args.User, uid, null, animateUser: false);
|
||||
}
|
||||
|
||||
private void AddPickupVerb(EntityUid uid, ItemComponent component, GetVerbsEvent<InteractionVerb> args)
|
||||
|
||||
@@ -434,7 +434,7 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
return;
|
||||
|
||||
EntityUid? wand = null;
|
||||
foreach (var item in _hands.EnumerateHeld(ev.Performer, handsComp))
|
||||
foreach (var item in _hands.EnumerateHeld((ev.Performer, handsComp)))
|
||||
{
|
||||
if (!_tag.HasTag(item, ev.WandTag))
|
||||
continue;
|
||||
|
||||
@@ -109,16 +109,12 @@ public sealed class PullingSystem : EntitySystem
|
||||
|
||||
// Try find hand that is doing this pull.
|
||||
// and clear it.
|
||||
foreach (var hand in component.Hands.Values)
|
||||
foreach (var held in _handsSystem.EnumerateHeld((uid, component)))
|
||||
{
|
||||
if (hand.HeldEntity == null
|
||||
|| !TryComp(hand.HeldEntity, out VirtualItemComponent? virtualItem)
|
||||
|| virtualItem.BlockingEntity != args.PulledUid)
|
||||
{
|
||||
if (!TryComp(held, out VirtualItemComponent? virtualItem) || virtualItem.BlockingEntity != args.PulledUid)
|
||||
continue;
|
||||
}
|
||||
|
||||
_handsSystem.TryDrop(args.PullerUid, hand, handsComp: component);
|
||||
_handsSystem.TryDrop((args.PullerUid, component), held);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Item.ItemToggle;
|
||||
@@ -19,6 +20,7 @@ public abstract class SharedNinjaGlovesSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedCombatModeSystem _combatMode = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
@@ -128,8 +130,7 @@ public abstract class SharedNinjaGlovesSystem : EntitySystem
|
||||
target = args.Target;
|
||||
return _timing.IsFirstTimePredicted
|
||||
&& !_combatMode.IsInCombatMode(uid)
|
||||
&& TryComp<HandsComponent>(uid, out var hands)
|
||||
&& hands.ActiveHandEntity == null
|
||||
&& _hands.GetActiveItem(uid) == null
|
||||
&& _interaction.InRangeUnobstructed(uid, target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,7 +459,7 @@ public sealed class FoodSystem : EntitySystem
|
||||
|
||||
var usedTypes = UtensilType.None;
|
||||
|
||||
foreach (var item in _hands.EnumerateHeld(user, hands))
|
||||
foreach (var item in _hands.EnumerateHeld((user, hands)))
|
||||
{
|
||||
// Is utensil?
|
||||
if (!TryComp<UtensilComponent>(item, out var utensil))
|
||||
|
||||
@@ -5,7 +5,7 @@ using Content.Shared.Construction;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Physics;
|
||||
@@ -35,6 +35,7 @@ public sealed class RCDSystem : EntitySystem
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedChargesSystem _sharedCharges = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly TurfSystem _turf = default!;
|
||||
@@ -296,11 +297,10 @@ public sealed class RCDSystem : EntitySystem
|
||||
var uid = GetEntity(ev.NetEntity);
|
||||
|
||||
// Determine if player that send the message is carrying the specified RCD in their active hand
|
||||
if (session.SenderSession.AttachedEntity == null)
|
||||
if (session.SenderSession.AttachedEntity is not { } player)
|
||||
return;
|
||||
|
||||
if (!TryComp<HandsComponent>(session.SenderSession.AttachedEntity, out var hands) ||
|
||||
uid != hands.ActiveHand?.HeldEntity)
|
||||
if (_hands.GetActiveItem(player) != uid)
|
||||
return;
|
||||
|
||||
if (!TryComp<RCDComponent>(uid, out var rcd))
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Cuffs;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Inventory;
|
||||
@@ -42,7 +41,7 @@ public sealed class RetractableItemActionSystem : EntitySystem
|
||||
|
||||
private void OnRetractableItemAction(Entity<RetractableItemActionComponent> ent, ref OnRetractableItemActionEvent args)
|
||||
{
|
||||
if (_hands.GetActiveHand(args.Performer) is not { } userHand)
|
||||
if (_hands.GetActiveHand(args.Performer) is not { } activeHand)
|
||||
return;
|
||||
|
||||
if (_actions.GetAction(ent.Owner) is not { } action)
|
||||
@@ -55,7 +54,9 @@ public sealed class RetractableItemActionSystem : EntitySystem
|
||||
return;
|
||||
|
||||
// Don't allow to summon an item if holding an unremoveable item unless that item is summoned by the action.
|
||||
if (userHand.HeldEntity != null && !_hands.IsHolding(args.Performer, ent.Comp.ActionItemUid) && !_hands.CanDropHeld(args.Performer, userHand, false))
|
||||
if (_hands.GetActiveItem(ent.Owner) != null
|
||||
&& !_hands.IsHolding(args.Performer, ent.Comp.ActionItemUid)
|
||||
&& !_hands.CanDropHeld(args.Performer, activeHand, false))
|
||||
{
|
||||
_popups.PopupClient(Loc.GetString("retractable-item-hand-cannot-drop"), args.Performer, args.Performer);
|
||||
return;
|
||||
@@ -67,7 +68,7 @@ public sealed class RetractableItemActionSystem : EntitySystem
|
||||
}
|
||||
else
|
||||
{
|
||||
SummonRetractableItem(args.Performer, ent.Comp.ActionItemUid.Value, userHand, ent.Owner);
|
||||
SummonRetractableItem(args.Performer, ent.Comp.ActionItemUid.Value, activeHand, ent.Owner);
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
@@ -93,7 +94,7 @@ public sealed class RetractableItemActionSystem : EntitySystem
|
||||
if (action.Comp.AttachedEntity == null)
|
||||
return;
|
||||
|
||||
if (_hands.GetActiveHand(action.Comp.AttachedEntity.Value) is not { } userHand)
|
||||
if (_hands.GetActiveHand(action.Comp.AttachedEntity.Value) is not { })
|
||||
return;
|
||||
|
||||
RetractRetractableItem(action.Comp.AttachedEntity.Value, ent, action.Owner);
|
||||
@@ -128,7 +129,7 @@ public sealed class RetractableItemActionSystem : EntitySystem
|
||||
_audio.PlayPredicted(action.Comp.RetractSounds, holder, holder);
|
||||
}
|
||||
|
||||
private void SummonRetractableItem(EntityUid holder, EntityUid item, Hand hand, Entity<RetractableItemActionComponent?> action)
|
||||
private void SummonRetractableItem(EntityUid holder, EntityUid item, string hand, Entity<RetractableItemActionComponent?> action)
|
||||
{
|
||||
if (!Resolve(action, ref action.Comp, false))
|
||||
return;
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace Content.Shared.Stacks
|
||||
}
|
||||
|
||||
// This is shit code until hands get fixed and give an easy way to enumerate over items, starting with the currently active item.
|
||||
foreach (var held in Hands.EnumerateHeld(user, hands))
|
||||
foreach (var held in Hands.EnumerateHeld((user, hands)))
|
||||
{
|
||||
TryMergeStacks(item, held, out _, donorStack: itemStack);
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ public abstract class SharedStationSpawningSystem : EntitySystem
|
||||
{
|
||||
var inhandEntity = EntityManager.SpawnEntity(prototype, coords);
|
||||
|
||||
if (_handsSystem.TryGetEmptyHand(entity, out var emptyHand, handsComponent))
|
||||
if (_handsSystem.TryGetEmptyHand((entity, handsComponent), out var emptyHand))
|
||||
{
|
||||
_handsSystem.TryPickup(entity, inhandEntity, emptyHand, checkActionBlocker: false, handsComp: handsComponent);
|
||||
}
|
||||
|
||||
@@ -689,7 +689,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
return;
|
||||
|
||||
// If the user's active hand is empty, try pick up the item.
|
||||
if (player.Comp.ActiveHandEntity == null)
|
||||
if (!_sharedHandsSystem.TryGetActiveItem(player.AsNullable(), out var activeItem))
|
||||
{
|
||||
_adminLog.Add(
|
||||
LogType.Storage,
|
||||
@@ -709,11 +709,11 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
_adminLog.Add(
|
||||
LogType.Storage,
|
||||
LogImpact.Low,
|
||||
$"{ToPrettyString(player):player} is interacting with {ToPrettyString(item):item} while it is stored in {ToPrettyString(storage):storage} using {ToPrettyString(player.Comp.ActiveHandEntity):used}");
|
||||
$"{ToPrettyString(player):player} is interacting with {ToPrettyString(item):item} while it is stored in {ToPrettyString(storage):storage} using {ToPrettyString(activeItem):used}");
|
||||
|
||||
// Else, interact using the held item
|
||||
if (_interactionSystem.InteractUsing(player,
|
||||
player.Comp.ActiveHandEntity.Value,
|
||||
activeItem.Value,
|
||||
item,
|
||||
Transform(item).Coordinates,
|
||||
checkCanInteract: false))
|
||||
@@ -1208,10 +1208,10 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
{
|
||||
if (!Resolve(ent.Owner, ref ent.Comp)
|
||||
|| !Resolve(player.Owner, ref player.Comp)
|
||||
|| player.Comp.ActiveHandEntity == null)
|
||||
|| !_sharedHandsSystem.TryGetActiveItem(player, out var activeItem))
|
||||
return false;
|
||||
|
||||
var toInsert = player.Comp.ActiveHandEntity;
|
||||
var toInsert = activeItem;
|
||||
|
||||
if (!CanInsert(ent, toInsert.Value, out var reason, ent.Comp))
|
||||
{
|
||||
@@ -1219,7 +1219,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_sharedHandsSystem.CanDrop(player, toInsert.Value, player.Comp))
|
||||
if (!_sharedHandsSystem.CanDrop(player, toInsert.Value))
|
||||
{
|
||||
_popupSystem.PopupClient(Loc.GetString("comp-storage-cant-drop", ("entity", toInsert.Value)), ent, player);
|
||||
return false;
|
||||
@@ -1933,7 +1933,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
|
||||
if (held)
|
||||
{
|
||||
if (!_sharedHandsSystem.IsHolding(player, itemUid, out _))
|
||||
if (!_sharedHandsSystem.IsHolding(player.AsNullable(), itemUid, out _))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -104,8 +104,8 @@ public abstract class SharedStrippableSystem : EntitySystem
|
||||
|
||||
var hasEnt = _inventorySystem.TryGetSlotEntity(strippable, args.Slot, out var held, inventory);
|
||||
|
||||
if (userHands.ActiveHandEntity != null && !hasEnt)
|
||||
StartStripInsertInventory((user, userHands), strippable.Owner, userHands.ActiveHandEntity.Value, args.Slot);
|
||||
if (_handsSystem.GetActiveItem((user, userHands)) is { } activeItem && !hasEnt)
|
||||
StartStripInsertInventory((user, userHands), strippable.Owner, activeItem, args.Slot);
|
||||
else if (hasEnt)
|
||||
StartStripRemoveInventory(user, strippable.Owner, held!.Value, args.Slot);
|
||||
}
|
||||
@@ -124,11 +124,10 @@ public abstract class SharedStrippableSystem : EntitySystem
|
||||
if (!target.Comp.CanBeStripped)
|
||||
return;
|
||||
|
||||
if (!_handsSystem.TryGetHand(target.Owner, handId, out var handSlot))
|
||||
return;
|
||||
var heldEntity = _handsSystem.GetHeldItem(target.Owner, handId);
|
||||
|
||||
// Is the target a handcuff?
|
||||
if (TryComp<VirtualItemComponent>(handSlot.HeldEntity, out var virtualItem) &&
|
||||
if (TryComp<VirtualItemComponent>(heldEntity, out var virtualItem) &&
|
||||
TryComp<CuffableComponent>(target.Owner, out var cuffable) &&
|
||||
_cuffableSystem.GetAllCuffs(cuffable).Contains(virtualItem.BlockingEntity))
|
||||
{
|
||||
@@ -136,10 +135,10 @@ public abstract class SharedStrippableSystem : EntitySystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.Comp.ActiveHandEntity != null && handSlot.HeldEntity == null)
|
||||
StartStripInsertHand(user, target, user.Comp.ActiveHandEntity.Value, handId, targetStrippable);
|
||||
else if (handSlot.HeldEntity != null)
|
||||
StartStripRemoveHand(user, target, handSlot.HeldEntity.Value, handId, targetStrippable);
|
||||
if (_handsSystem.GetActiveItem(user.AsNullable()) is { } activeItem && heldEntity == null)
|
||||
StartStripInsertHand(user, target, activeItem, handId, targetStrippable);
|
||||
else if (heldEntity != null)
|
||||
StartStripRemoveHand(user, target, heldEntity.Value, handId, targetStrippable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -154,16 +153,10 @@ public abstract class SharedStrippableSystem : EntitySystem
|
||||
if (!Resolve(user, ref user.Comp))
|
||||
return false;
|
||||
|
||||
if (user.Comp.ActiveHand == null)
|
||||
if (!_handsSystem.TryGetActiveItem(user, out var activeItem) || activeItem != held)
|
||||
return false;
|
||||
|
||||
if (user.Comp.ActiveHandEntity == null)
|
||||
return false;
|
||||
|
||||
if (user.Comp.ActiveHandEntity != held)
|
||||
return false;
|
||||
|
||||
if (!_handsSystem.CanDropHeld(user, user.Comp.ActiveHand))
|
||||
if (!_handsSystem.CanDropHeld(user, user.Comp.ActiveHandId!))
|
||||
{
|
||||
_popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop"));
|
||||
return false;
|
||||
@@ -210,10 +203,14 @@ public abstract class SharedStrippableSystem : EntitySystem
|
||||
var (time, stealth) = GetStripTimeModifiers(user, target, held, slotDef.StripTime);
|
||||
|
||||
if (!stealth)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-insert",
|
||||
("user", Identity.Entity(user, EntityManager)),
|
||||
("item", user.Comp.ActiveHandEntity!.Value)),
|
||||
target, target, PopupType.Large);
|
||||
("item", _handsSystem.GetActiveItem((user, user.Comp))!.Value)),
|
||||
target,
|
||||
target,
|
||||
PopupType.Large);
|
||||
}
|
||||
|
||||
var prefix = stealth ? "stealthily " : "";
|
||||
_adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}place the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s {slot} slot");
|
||||
@@ -246,7 +243,7 @@ public abstract class SharedStrippableSystem : EntitySystem
|
||||
if (!CanStripInsertInventory(user, target, held, slot))
|
||||
return;
|
||||
|
||||
if (!_handsSystem.TryDrop(user, handsComp: user.Comp))
|
||||
if (!_handsSystem.TryDrop(user))
|
||||
return;
|
||||
|
||||
_inventorySystem.TryEquip(user, target, held, slot, triggerHandContact: true);
|
||||
@@ -305,10 +302,15 @@ public abstract class SharedStrippableSystem : EntitySystem
|
||||
if (IsStripHidden(slotDef, user))
|
||||
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-hidden", ("slot", slot)), target, target, PopupType.Large);
|
||||
else
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner",
|
||||
("user", Identity.Entity(user, EntityManager)),
|
||||
("item", item)),
|
||||
target, target, PopupType.Large);
|
||||
target,
|
||||
target,
|
||||
PopupType.Large);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var prefix = stealth ? "stealthily " : "";
|
||||
@@ -368,23 +370,16 @@ public abstract class SharedStrippableSystem : EntitySystem
|
||||
if (!target.Comp.CanBeStripped)
|
||||
return false;
|
||||
|
||||
if (user.Comp.ActiveHand == null)
|
||||
if (!_handsSystem.TryGetActiveItem(user, out var activeItem) || activeItem != held)
|
||||
return false;
|
||||
|
||||
if (user.Comp.ActiveHandEntity == null)
|
||||
return false;
|
||||
|
||||
if (user.Comp.ActiveHandEntity != held)
|
||||
return false;
|
||||
|
||||
if (!_handsSystem.CanDropHeld(user, user.Comp.ActiveHand))
|
||||
if (!_handsSystem.CanDropHeld(user, user.Comp.ActiveHandId!))
|
||||
{
|
||||
_popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_handsSystem.TryGetHand(target, handName, out var handSlot, target.Comp) ||
|
||||
!_handsSystem.CanPickupToHand(target, user.Comp.ActiveHandEntity.Value, handSlot, checkActionBlocker: false, target.Comp))
|
||||
if (!_handsSystem.CanPickupToHand(target, activeItem.Value, handName, checkActionBlocker: false, target.Comp))
|
||||
{
|
||||
_popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-put-message", ("owner", Identity.Entity(target, EntityManager))));
|
||||
return false;
|
||||
@@ -414,10 +409,15 @@ public abstract class SharedStrippableSystem : EntitySystem
|
||||
var (time, stealth) = GetStripTimeModifiers(user, target, null, targetStrippable.HandStripDelay);
|
||||
|
||||
if (!stealth)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-insert-hand",
|
||||
("user", Identity.Entity(user, EntityManager)),
|
||||
("item", user.Comp.ActiveHandEntity!.Value)),
|
||||
target, target, PopupType.Large);
|
||||
("item", _handsSystem.GetActiveItem(user)!.Value)),
|
||||
target,
|
||||
target,
|
||||
PopupType.Large);
|
||||
|
||||
}
|
||||
|
||||
var prefix = stealth ? "stealthily " : "";
|
||||
_adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}place the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s hands");
|
||||
@@ -452,7 +452,7 @@ public abstract class SharedStrippableSystem : EntitySystem
|
||||
if (!CanStripInsertHand(user, target, held, handName))
|
||||
return;
|
||||
|
||||
_handsSystem.TryDrop(user, checkActionBlocker: false, handsComp: user.Comp);
|
||||
_handsSystem.TryDrop(user, checkActionBlocker: false);
|
||||
_handsSystem.TryPickup(target, held, handName, checkActionBlocker: false, animateUser: stealth, animate: !stealth, handsComp: target.Comp);
|
||||
_adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):actor} has placed the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s hands");
|
||||
|
||||
@@ -474,22 +474,22 @@ public abstract class SharedStrippableSystem : EntitySystem
|
||||
if (!target.Comp.CanBeStripped)
|
||||
return false;
|
||||
|
||||
if (!_handsSystem.TryGetHand(target, handName, out var handSlot, target.Comp))
|
||||
if (!_handsSystem.TryGetHand(target, handName, out _))
|
||||
{
|
||||
_popupSystem.PopupCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Identity.Entity(target, EntityManager))));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HasComp<VirtualItemComponent>(handSlot.HeldEntity))
|
||||
if (!_handsSystem.TryGetHeldItem(target, handName, out var heldEntity))
|
||||
return false;
|
||||
|
||||
if (handSlot.HeldEntity == null)
|
||||
if (HasComp<VirtualItemComponent>(heldEntity))
|
||||
return false;
|
||||
|
||||
if (handSlot.HeldEntity != item)
|
||||
if (heldEntity != item)
|
||||
return false;
|
||||
|
||||
if (!_handsSystem.CanDropHeld(target, handSlot, false))
|
||||
if (!_handsSystem.CanDropHeld(target, handName, false))
|
||||
{
|
||||
_popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop-message", ("owner", Identity.Entity(target, EntityManager))));
|
||||
return false;
|
||||
@@ -519,10 +519,13 @@ public abstract class SharedStrippableSystem : EntitySystem
|
||||
var (time, stealth) = GetStripTimeModifiers(user, target, null, targetStrippable.HandStripDelay);
|
||||
|
||||
if (!stealth)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner",
|
||||
("user", Identity.Entity(user, EntityManager)),
|
||||
("item", item)),
|
||||
target, target);
|
||||
target,
|
||||
target);
|
||||
}
|
||||
|
||||
var prefix = stealth ? "stealthily " : "";
|
||||
_adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}strip the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands");
|
||||
@@ -560,7 +563,7 @@ public abstract class SharedStrippableSystem : EntitySystem
|
||||
if (!CanStripRemoveHand(user, target, item, handName))
|
||||
return;
|
||||
|
||||
_handsSystem.TryDrop(target, item, checkActionBlocker: false, handsComp: target.Comp);
|
||||
_handsSystem.TryDrop(target, item, checkActionBlocker: false);
|
||||
_handsSystem.PickupOrDrop(user, item, animateUser: stealth, animate: !stealth, handsComp: user.Comp);
|
||||
_adminLogger.Add(LogType.Stripping, LogImpact.High, $"{ToPrettyString(user):actor} has stripped the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands");
|
||||
|
||||
@@ -580,13 +583,17 @@ public abstract class SharedStrippableSystem : EntitySystem
|
||||
{
|
||||
if ( ev.Event.InsertOrRemove && !CanStripInsertInventory((entity.Owner, entity.Comp), args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName) ||
|
||||
!ev.Event.InsertOrRemove && !CanStripRemoveInventory(entity.Owner, args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName))
|
||||
ev.Cancel();
|
||||
{
|
||||
ev.Cancel();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ev.Event.InsertOrRemove && !CanStripInsertHand((entity.Owner, entity.Comp), args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName) ||
|
||||
!ev.Event.InsertOrRemove && !CanStripRemoveHand(entity.Owner, args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName))
|
||||
ev.Cancel();
|
||||
{
|
||||
ev.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,10 +108,10 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
|
||||
if (component.InHandsOnly)
|
||||
{
|
||||
if (!_hands.IsHolding(args.User, uid, out var hand, args.Hands))
|
||||
if (!_hands.IsHolding((args.User, args.Hands), uid, out var hand ))
|
||||
return false;
|
||||
|
||||
if (component.RequireActiveHand && args.Hands.ActiveHand != hand)
|
||||
if (component.RequireActiveHand && args.Hands.ActiveHandId != hand)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -202,10 +202,10 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
if (!TryComp(user, out HandsComponent? hands))
|
||||
return false;
|
||||
|
||||
if (!_hands.IsHolding(user, uiEntity, out var hand, hands))
|
||||
if (!_hands.IsHolding((user, hands), uiEntity, out var hand))
|
||||
return false;
|
||||
|
||||
if (aui.RequireActiveHand && hands.ActiveHand != hand)
|
||||
if (aui.RequireActiveHand && hands.ActiveHandId != hand)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ using Content.Shared.Database;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Inventory;
|
||||
@@ -51,6 +52,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
[Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!;
|
||||
[Dependency] protected readonly ActionBlockerSystem Blocker = default!;
|
||||
[Dependency] protected readonly DamageableSystem Damageable = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
[Dependency] private readonly MeleeSoundSystem _meleeSound = default!;
|
||||
[Dependency] protected readonly MobStateSystem MobState = default!;
|
||||
@@ -288,15 +290,14 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
}
|
||||
|
||||
// Use inhands entity if we got one.
|
||||
if (EntityManager.TryGetComponent(entity, out HandsComponent? hands) &&
|
||||
hands.ActiveHandEntity is { } held)
|
||||
if (_hands.TryGetActiveItem(entity, out var held))
|
||||
{
|
||||
// Make sure the entity is a weapon AND it doesn't need
|
||||
// to be equipped to be used (E.g boxing gloves).
|
||||
if (EntityManager.TryGetComponent(held, out melee) &&
|
||||
!melee.MustBeEquippedToUse)
|
||||
{
|
||||
weaponUid = held;
|
||||
weaponUid = held.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -858,9 +859,9 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
|
||||
EntityUid? inTargetHand = null;
|
||||
|
||||
if (targetHandsComponent?.ActiveHand is { IsEmpty: false })
|
||||
if (_hands.TryGetActiveItem(target.Value, out var activeHeldEntity))
|
||||
{
|
||||
inTargetHand = targetHandsComponent.ActiveHand.HeldEntity!.Value;
|
||||
inTargetHand = activeHeldEntity.Value;
|
||||
}
|
||||
|
||||
var attemptEvent = new DisarmAttemptEvent(target.Value, user, inTargetHand);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Physics;
|
||||
@@ -25,6 +25,7 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly SharedJointSystem _joints = default!;
|
||||
[Dependency] private readonly SharedGunSystem _gun = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
@@ -80,9 +81,11 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
|
||||
|
||||
private void OnGrapplingReel(RequestGrapplingReelMessage msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var player = args.SenderSession.AttachedEntity;
|
||||
if (!TryComp<HandsComponent>(player, out var hands) ||
|
||||
!TryComp<GrapplingGunComponent>(hands.ActiveHandEntity, out var grappling))
|
||||
if (args.SenderSession.AttachedEntity is not { } player)
|
||||
return;
|
||||
|
||||
if (!_hands.TryGetActiveItem(player, out var activeItem) ||
|
||||
!TryComp<GrapplingGunComponent>(activeItem, out var grappling))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -94,7 +97,7 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
|
||||
return;
|
||||
}
|
||||
|
||||
SetReeling(hands.ActiveHandEntity.Value, grappling, msg.Reeling, player.Value);
|
||||
SetReeling(activeItem.Value, grappling, msg.Reeling, player);
|
||||
}
|
||||
|
||||
private void OnWeightlessMove(ref CanWeightlessMoveEvent ev)
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Movement.Events;
|
||||
@@ -23,6 +24,7 @@ public abstract partial class SharedTetherGunSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly MobStateSystem _mob = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
@@ -139,14 +141,14 @@ public abstract partial class SharedTetherGunSystem : EntitySystem
|
||||
gunUid = null;
|
||||
gun = null;
|
||||
|
||||
if (!TryComp<HandsComponent>(user, out var hands) ||
|
||||
!TryComp(hands.ActiveHandEntity, out gun) ||
|
||||
if (!_hands.TryGetActiveItem(user, out var activeItem) ||
|
||||
!TryComp(activeItem, out gun) ||
|
||||
_container.IsEntityInContainer(user))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
gunUid = hands.ActiveHandEntity.Value;
|
||||
gunUid = activeItem.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ using Content.Shared.Damage;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Gravity;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Projectiles;
|
||||
using Content.Shared.Tag;
|
||||
@@ -49,6 +49,7 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
[Dependency] protected readonly ISharedAdminLogManager Logs = default!;
|
||||
[Dependency] protected readonly DamageableSystem Damageable = default!;
|
||||
[Dependency] protected readonly ExamineSystemShared Examine = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly ItemSlotsSystem _slots = default!;
|
||||
[Dependency] private readonly RechargeBasicEntityAmmoSystem _recharge = default!;
|
||||
[Dependency] protected readonly SharedActionsSystem Actions = default!;
|
||||
@@ -173,8 +174,7 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
gunEntity = default;
|
||||
gunComp = null;
|
||||
|
||||
if (EntityManager.TryGetComponent(entity, out HandsComponent? hands) &&
|
||||
hands.ActiveHandEntity is { } held &&
|
||||
if (_hands.GetActiveItem(entity) is { } held &&
|
||||
TryComp(held, out GunComponent? gun))
|
||||
{
|
||||
gunEntity = held;
|
||||
|
||||
@@ -112,7 +112,7 @@ public abstract class SharedWieldableSystem : EntitySystem
|
||||
|
||||
private void OnDeselectWieldable(EntityUid uid, WieldableComponent component, HandDeselectedEvent args)
|
||||
{
|
||||
if (_hands.EnumerateHands(args.User).Count() > 2)
|
||||
if (_hands.GetHandCount(uid) > 2)
|
||||
return;
|
||||
|
||||
TryUnwield(uid, component, args.User);
|
||||
@@ -168,7 +168,7 @@ public abstract class SharedWieldableSystem : EntitySystem
|
||||
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
if (!_hands.IsHolding(args.User, uid, out _, args.Hands))
|
||||
if (!_hands.IsHolding((args.User, args.Hands), uid, out _))
|
||||
return;
|
||||
|
||||
// TODO VERB TOOLTIPS Make CanWield or some other function return string, set as verb tooltip and disable
|
||||
@@ -252,7 +252,7 @@ public abstract class SharedWieldableSystem : EntitySystem
|
||||
}
|
||||
|
||||
// Is it.. actually in one of their hands?
|
||||
if (!_hands.IsHolding(user, uid, out _, hands))
|
||||
if (!_hands.IsHolding((user, hands), uid, out _))
|
||||
{
|
||||
if (!quiet)
|
||||
_popup.PopupClient(Loc.GetString("wieldable-component-not-in-hands", ("item", uid)), user, user);
|
||||
@@ -373,7 +373,7 @@ public abstract class SharedWieldableSystem : EntitySystem
|
||||
/// <param name="force">If this is true we will bypass UnwieldAttemptEvent.</param>
|
||||
public void UnwieldAll(Entity<HandsComponent?> wielder, bool force = false)
|
||||
{
|
||||
foreach (var held in _hands.EnumerateHeld(wielder.Owner, wielder.Comp))
|
||||
foreach (var held in _hands.EnumerateHeld(wielder))
|
||||
{
|
||||
if (TryComp<WieldableComponent>(held, out var wieldable))
|
||||
TryUnwield(held, wieldable, wielder, force);
|
||||
|
||||
Reference in New Issue
Block a user