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:
Nemanja
2025-06-25 09:13:03 -04:00
committed by GitHub
parent 6cffa8aabe
commit 524725d378
79 changed files with 849 additions and 897 deletions

View File

@@ -42,13 +42,12 @@ public sealed class StripAllCommand : LocalizedEntityCommands
if (EntityManager.TryGetComponent<HandsComponent>(targetEntity, out var hands))
{
foreach (var hand in _handsSystem.EnumerateHands(targetEntity.Value, hands))
foreach (var hand in _handsSystem.EnumerateHands((targetEntity.Value, hands)))
{
_handsSystem.TryDrop(targetEntity.Value,
_handsSystem.TryDrop((targetEntity.Value, hands),
hand,
checkActionBlocker: false,
doDropInteraction: false,
handsComp: hands);
doDropInteraction: false);
}
}
}

View File

@@ -433,9 +433,9 @@ public sealed class AdminSystem : EntitySystem
if (TryComp(entity, out HandsComponent? hands))
{
foreach (var hand in _hands.EnumerateHands(entity, hands))
foreach (var hand in _hands.EnumerateHands((entity, hands)))
{
_hands.TryDrop(entity, hand, checkActionBlocker: false, doDropInteraction: false, handsComp: hands);
_hands.TryDrop((entity, hands), hand, checkActionBlocker: false, doDropInteraction: false);
}
}

View File

@@ -820,7 +820,7 @@ public sealed partial class AdminVerbSystem
}
else if (TryComp<HandsComponent>(target, out var hands))
{
foreach (var held in _handsSystem.EnumerateHeld(target, hands))
foreach (var held in _handsSystem.EnumerateHeld((target, hands)))
{
if (HasComp<AccessComponent>(held))
{

View File

@@ -97,8 +97,7 @@ public sealed class CryostorageSystem : SharedCryostorageSystem
EntityUid? entity = null;
if (args.Type == CryostorageRemoveItemBuiMessage.RemovalType.Hand)
{
if (_hands.TryGetHand(cryoContained, args.Key, out var hand))
entity = hand.HeldEntity;
entity = _hands.GetHeldItem(cryoContained, args.Key);
}
else
{
@@ -320,10 +319,10 @@ public sealed class CryostorageSystem : SharedCryostorageSystem
foreach (var hand in _hands.EnumerateHands(uid))
{
if (hand.HeldEntity == null)
if (!_hands.TryGetHeldItem(uid, hand, out var heldEntity))
continue;
data.HeldItems.Add(hand.Name, Name(hand.HeldEntity.Value));
data.HeldItems.Add(hand, Name(heldEntity.Value));
}
return data;

View File

@@ -1,5 +1,6 @@
using Content.Server.Atmos.EntitySystems;
using Content.Server.Botany.Components;
using Content.Server.Hands.Systems;
using Content.Server.Kitchen.Components;
using Content.Server.Popups;
using Content.Shared.Chemistry.EntitySystems;
@@ -37,6 +38,7 @@ public sealed class PlantHolderSystem : EntitySystem
[Dependency] private readonly MutationSystem _mutation = default!;
[Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly HandsSystem _hands = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
@@ -45,7 +47,7 @@ public sealed class PlantHolderSystem : EntitySystem
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlots = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
public const float HydroponicsSpeedMultiplier = 1f;
public const float HydroponicsConsumptionMultiplier = 2f;
@@ -706,9 +708,9 @@ public sealed class PlantHolderSystem : EntitySystem
if (component.Harvest && !component.Dead)
{
if (TryComp<HandsComponent>(user, out var hands))
if (_hands.TryGetActiveItem(user, out var activeItem))
{
if (!_botany.CanHarvest(component.Seed, hands.ActiveHandEntity))
if (!_botany.CanHarvest(component.Seed, activeItem))
{
_popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), user);
return false;

View File

@@ -1,9 +1,9 @@
using Content.Server.Ghost;
using Content.Server.Hands.Systems;
using Content.Shared.Administration.Logs;
using Content.Shared.Chat;
using Content.Shared.Damage;
using Content.Shared.Database;
using Content.Shared.Hands.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction.Events;
using Content.Shared.Item;
@@ -22,6 +22,7 @@ public sealed class SuicideSystem : EntitySystem
{
[Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly HandsSystem _hands = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
@@ -116,10 +117,9 @@ public sealed class SuicideSystem : EntitySystem
var suicideByEnvironmentEvent = new SuicideByEnvironmentEvent(victim);
// Try to suicide by raising an event on the held item
if (EntityManager.TryGetComponent(victim, out HandsComponent? handsComponent)
&& handsComponent.ActiveHandEntity is { } item)
if (_hands.TryGetActiveItem(victim.Owner, out var item))
{
RaiseLocalEvent(item, suicideByEnvironmentEvent);
RaiseLocalEvent(item.Value, suicideByEnvironmentEvent);
if (suicideByEnvironmentEvent.Handled)
{
args.Handled = suicideByEnvironmentEvent.Handled;

View File

@@ -471,7 +471,7 @@ namespace Content.Server.Construction
}
if (!_actionBlocker.CanInteract(user, null)
|| !EntityManager.TryGetComponent(user, out HandsComponent? hands) || hands.ActiveHandEntity == null)
|| !EntityManager.TryGetComponent(user, out HandsComponent? hands) || _handsSystem.GetActiveItem((user, hands)) == null)
{
Cleanup();
return;
@@ -496,7 +496,7 @@ namespace Content.Server.Construction
var valid = false;
if (hands.ActiveHandEntity is not {Valid: true} holding)
if (_handsSystem.GetActiveItem((user, hands)) is not {Valid: true} holding)
{
Cleanup();
return;

View File

@@ -1,5 +1,4 @@
using System.Numerics;
using Content.Server.Inventory;
using Content.Server.Stack;
using Content.Server.Stunnable;
using Content.Shared.ActionBlocker;
@@ -10,9 +9,7 @@ using Content.Shared.Explosion;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Input;
using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Movement.Pulling.Events;
using Content.Shared.Movement.Pulling.Systems;
using Content.Shared.Stacks;
using Content.Shared.Standing;
@@ -24,7 +21,6 @@ using Robust.Shared.Physics.Components;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Server.Hands.Systems
{
@@ -87,10 +83,9 @@ namespace Content.Server.Hands.Systems
if (ent.Comp.DisableExplosionRecursion)
return;
foreach (var hand in ent.Comp.Hands.Values)
foreach (var held in EnumerateHeld(ent.AsNullable()))
{
if (hand.HeldEntity is { } uid)
args.Contents.Add(uid);
args.Contents.Add(held);
}
}
@@ -112,7 +107,7 @@ namespace Content.Server.Hands.Systems
args.Handled = true; // no shove/stun.
}
private void HandleBodyPartAdded(EntityUid uid, HandsComponent component, ref BodyPartAddedEvent args)
private void HandleBodyPartAdded(Entity<HandsComponent> ent, ref BodyPartAddedEvent args)
{
if (args.Part.Comp.PartType != BodyPartType.Hand)
return;
@@ -127,7 +122,7 @@ namespace Content.Server.Hands.Systems
_ => throw new ArgumentOutOfRangeException(nameof(args.Part.Comp.Symmetry))
};
AddHand(uid, args.Slot, location);
AddHand(ent.AsNullable(), args.Slot, location);
}
private void HandleBodyPartRemoved(EntityUid uid, HandsComponent component, ref BodyPartRemovedEvent args)
@@ -155,8 +150,8 @@ namespace Content.Server.Hands.Systems
{
if (ContainerSystem.IsEntityInContainer(player) ||
!TryComp(player, out HandsComponent? hands) ||
hands.ActiveHandEntity is not { } throwEnt ||
!_actionBlockerSystem.CanThrow(player, throwEnt))
!TryGetActiveItem((player, hands), out var throwEnt) ||
!_actionBlockerSystem.CanThrow(player, throwEnt.Value))
return false;
if (_timing.CurTime < hands.NextThrowTime)
@@ -165,7 +160,7 @@ namespace Content.Server.Hands.Systems
if (EntityManager.TryGetComponent(throwEnt, out StackComponent? stack) && stack.Count > 1 && stack.ThrowIndividually)
{
var splitStack = _stackSystem.Split(throwEnt, 1, EntityManager.GetComponent<TransformComponent>(player).Coordinates, stack);
var splitStack = _stackSystem.Split(throwEnt.Value, 1, EntityManager.GetComponent<TransformComponent>(player).Coordinates, stack);
if (splitStack is not {Valid: true})
return false;
@@ -185,14 +180,14 @@ namespace Content.Server.Hands.Systems
// Let other systems change the thrown entity (useful for virtual items)
// or the throw strength.
var ev = new BeforeThrowEvent(throwEnt, direction, throwSpeed, player);
var ev = new BeforeThrowEvent(throwEnt.Value, direction, throwSpeed, player);
RaiseLocalEvent(player, ref ev);
if (ev.Cancelled)
return true;
// This can grief the above event so we raise it afterwards
if (IsHolding(player, throwEnt, out _, hands) && !TryDrop(player, throwEnt, handsComp: hands))
if (IsHolding((player, hands), throwEnt, out _) && !TryDrop(player, throwEnt.Value))
return false;
_throwingSystem.TryThrow(ev.ItemUid, ev.Direction, ev.ThrowSpeed, ev.PlayerUid, compensateFriction: !HasComp<LandAtCursorComponent>(ev.ItemUid));
@@ -207,20 +202,20 @@ namespace Content.Server.Hands.Systems
var spreadMaxAngle = Angle.FromDegrees(DropHeldItemsSpread);
var fellEvent = new FellDownEvent(entity);
RaiseLocalEvent(entity, fellEvent, false);
RaiseLocalEvent(entity, fellEvent);
foreach (var hand in entity.Comp.Hands.Values)
foreach (var hand in entity.Comp.Hands.Keys)
{
if (hand.HeldEntity is not EntityUid held)
if (!TryGetHeldItem(entity.AsNullable(), hand, out var heldEntity))
continue;
var throwAttempt = new FellDownThrowAttemptEvent(entity);
RaiseLocalEvent(hand.HeldEntity.Value, ref throwAttempt);
RaiseLocalEvent(heldEntity.Value, ref throwAttempt);
if (throwAttempt.Cancelled)
continue;
if (!TryDrop(entity, hand, null, checkActionBlocker: false, handsComp: entity.Comp))
if (!TryDrop(entity.AsNullable(), hand, checkActionBlocker: false))
continue;
// Rotate the item's throw vector a bit for each item
@@ -231,12 +226,12 @@ namespace Content.Server.Hands.Systems
itemVelocity *= _random.NextFloat(1f);
// Heavier objects don't get thrown as far
// If the item doesn't have a physics component, it isn't going to get thrown anyway, but we'll assume infinite mass
itemVelocity *= _physicsQuery.TryComp(held, out var heldPhysics) ? heldPhysics.InvMass : 0;
itemVelocity *= _physicsQuery.TryComp(heldEntity, out var heldPhysics) ? heldPhysics.InvMass : 0;
// Throw at half the holder's intentional throw speed and
// vary the speed a little to make it look more interesting
var throwSpeed = entity.Comp.BaseThrowspeed * _random.NextFloat(0.45f, 0.55f);
_throwingSystem.TryThrow(held,
_throwingSystem.TryThrow(heldEntity.Value,
itemVelocity,
throwSpeed,
entity,

View File

@@ -43,7 +43,7 @@ public sealed class HotPotatoSystem : SharedHotPotatoSystem
if (!TryComp<HandsComponent>(hitEntity, out var hands))
continue;
if (!_hands.IsHolding(hitEntity, uid, out _, hands) && _hands.TryForcePickupAnyHand(hitEntity, uid, handsComp: hands))
if (!_hands.IsHolding((hitEntity, hands), uid, out _) && _hands.TryForcePickupAnyHand(hitEntity, uid, handsComp: hands))
{
_popup.PopupEntity(Loc.GetString("hot-potato-passed",
("from", args.User), ("to", hitEntity)), uid, PopupType.Medium);

View File

@@ -1,4 +1,4 @@
using Content.Shared.Hands.Components;
using Content.Server.Hands.Systems;
using Robust.Shared.Prototypes;
namespace Content.Server.NPC.HTN.Preconditions;
@@ -18,14 +18,18 @@ public sealed partial class ActiveHandComponentPrecondition : HTNPrecondition
public override bool IsMet(NPCBlackboard blackboard)
{
if (!blackboard.TryGetValue<Hand>(NPCBlackboard.ActiveHand, out var hand, _entManager) || hand.HeldEntity == null)
if (!blackboard.TryGetValue<EntityUid>(NPCBlackboard.Owner, out var owner, _entManager) ||
!blackboard.TryGetValue<string>(NPCBlackboard.ActiveHand, out var hand, _entManager))
{
return Invert;
}
if (!_entManager.System<HandsSystem>().TryGetHeldItem(owner, hand, out var entity))
return Invert;
foreach (var comp in Components)
{
var hasComp = _entManager.HasComponent(hand.HeldEntity, comp.Value.Component.GetType());
var hasComp = _entManager.HasComponent(entity, comp.Value.Component.GetType());
if (!hasComp ||
Invert && hasComp)

View File

@@ -1,4 +1,4 @@
using Content.Shared.Hands.Components;
using Content.Server.Hands.Systems;
namespace Content.Server.NPC.HTN.Preconditions;
@@ -11,11 +11,12 @@ public sealed partial class ActiveHandEntityPrecondition : HTNPrecondition
public override bool IsMet(NPCBlackboard blackboard)
{
if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out Hand? activeHand, _entManager))
if (!blackboard.TryGetValue(NPCBlackboard.Owner, out EntityUid owner, _entManager) ||
!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out string? activeHand, _entManager))
{
return false;
}
return activeHand.HeldEntity != null;
return !_entManager.System<HandsSystem>().HandIsEmpty(owner, activeHand);
}
}

View File

@@ -12,7 +12,7 @@ public sealed partial class DropOperator : HTNOperator
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
{
if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out Hand? activeHand, _entManager))
if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out string? activeHand, _entManager))
{
return HTNOperatorStatus.Finished;
}

View File

@@ -1,11 +1,10 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using Content.Server.Interaction;
using Content.Server.Hands.Systems;
using Content.Shared.Access.Systems;
using Content.Shared.ActionBlocker;
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
using Content.Shared.Inventory;
using JetBrains.Annotations;
using Robust.Shared.Utility;
@@ -152,6 +151,8 @@ public sealed partial class NPCBlackboard : IEnumerable<KeyValuePair<string, obj
value = default;
EntityUid owner;
var handSys = entManager.System<HandsSystem>();
switch (key)
{
case Access:
@@ -168,25 +169,24 @@ public sealed partial class NPCBlackboard : IEnumerable<KeyValuePair<string, obj
case ActiveHand:
{
if (!TryGetValue(Owner, out owner, entManager) ||
!entManager.TryGetComponent<HandsComponent>(owner, out var hands) ||
hands.ActiveHand == null)
handSys.GetActiveHand(owner) is not { } activeHand)
{
return false;
}
value = hands.ActiveHand;
value = activeHand;
return true;
}
case ActiveHandFree:
{
if (!TryGetValue(Owner, out owner, entManager) ||
!entManager.TryGetComponent<HandsComponent>(owner, out var hands) ||
hands.ActiveHand == null)
handSys.GetActiveHand(owner) is not { } activeHand)
{
return false;
}
value = hands.ActiveHand.IsEmpty;
value = handSys.HandIsEmpty((owner, hands), activeHand);
return true;
}
case CanMove:
@@ -204,16 +204,16 @@ public sealed partial class NPCBlackboard : IEnumerable<KeyValuePair<string, obj
{
if (!TryGetValue(Owner, out owner, entManager) ||
!entManager.TryGetComponent<HandsComponent>(owner, out var hands) ||
hands.ActiveHand == null)
handSys.GetActiveHand(owner) is null)
{
return false;
}
var handos = new List<string>();
foreach (var (id, hand) in hands.Hands)
foreach (var id in hands.Hands.Keys)
{
if (!hand.IsEmpty)
if (!handSys.HandIsEmpty((owner, hands), id))
continue;
handos.Add(id);
@@ -226,16 +226,16 @@ public sealed partial class NPCBlackboard : IEnumerable<KeyValuePair<string, obj
{
if (!TryGetValue(Owner, out owner, entManager) ||
!entManager.TryGetComponent<HandsComponent>(owner, out var hands) ||
hands.ActiveHand == null)
handSys.GetActiveHand(owner) is null)
{
return false;
}
var handos = new List<string>();
foreach (var (id, hand) in hands.Hands)
foreach (var id in hands.Hands.Keys)
{
if (!hand.IsEmpty)
if (!handSys.HandIsEmpty((owner, hands), id))
continue;
handos.Add(id);

View File

@@ -1,5 +1,6 @@
using Content.Server.Atmos.Components;
using Content.Server.Fluids.EntitySystems;
using Content.Server.Hands.Systems;
using Content.Server.NPC.Queries;
using Content.Server.NPC.Queries.Considerations;
using Content.Server.NPC.Queries.Curves;
@@ -44,6 +45,7 @@ public sealed class NPCUtilitySystem : EntitySystem
[Dependency] private readonly DrinkSystem _drink = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly FoodSystem _food = default!;
[Dependency] private readonly HandsSystem _hands = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
@@ -256,8 +258,9 @@ public sealed class NPCUtilitySystem : EntitySystem
}
case TargetAmmoMatchesCon:
{
if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out Hand? activeHand, EntityManager) ||
!TryComp<BallisticAmmoProviderComponent>(activeHand.HeldEntity, out var heldGun))
if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out string? activeHand, EntityManager) ||
!_hands.TryGetHeldItem(owner, activeHand, out var heldEntity) ||
!TryComp<BallisticAmmoProviderComponent>(heldEntity, out var heldGun))
{
return 0f;
}

View File

@@ -152,7 +152,7 @@ public sealed class FTLDiskCommand : LocalizedCommands
if (_entManager.TryGetComponent<StorageComponent>(cdCaseUid, out var storage) && storageSystem.Insert(cdCaseUid, cdUid, out _, storageComp: storage, playSound: false))
{
if (_entManager.TryGetComponent<HandsComponent>(entity, out var handsComponent) && handsSystem.TryGetEmptyHand(entity, out var emptyHand, handsComponent))
if (_entManager.TryGetComponent<HandsComponent>(entity, out var handsComponent) && handsSystem.TryGetEmptyHand((entity, handsComponent), out var emptyHand))
{
handsSystem.TryPickup(entity, cdCaseUid, emptyHand, checkActionBlocker: false, handsComp: handsComponent);
}
@@ -161,7 +161,7 @@ public sealed class FTLDiskCommand : LocalizedCommands
{
_entManager.DeleteEntity(cdCaseUid); // something went wrong so just yeet the chaf
if (_entManager.TryGetComponent<HandsComponent>(entity, out var handsComponent) && handsSystem.TryGetEmptyHand(entity, out var emptyHand, handsComponent))
if (_entManager.TryGetComponent<HandsComponent>(entity, out var handsComponent) && handsSystem.TryGetEmptyHand((entity, handsComponent), out var emptyHand))
{
handsSystem.TryPickup(entity, cdUid, emptyHand, checkActionBlocker: false, handsComp: handsComponent);
}

View File

@@ -217,8 +217,8 @@ public sealed partial class BorgSystem
var handId = $"{uid}-item{component.HandCounter}";
component.HandCounter++;
_hands.AddHand(chassis, handId, HandLocation.Middle, hands);
_hands.DoPickup(chassis, hands.Hands[handId], item, hands);
_hands.AddHand((chassis, hands), handId, HandLocation.Middle);
_hands.DoPickup(chassis, handId, item, hands);
EnsureComp<UnremoveableComponent>(item);
component.ProvidedItems.Add(handId, item);
}
@@ -239,7 +239,7 @@ public sealed partial class BorgSystem
foreach (var (hand, item) in component.ProvidedItems)
{
QueueDel(item);
_hands.RemoveHand(chassis, hand, hands);
_hands.RemoveHand(chassis, hand);
}
component.ProvidedItems.Clear();
return;
@@ -252,7 +252,7 @@ public sealed partial class BorgSystem
RemComp<UnremoveableComponent>(item);
_container.Insert(item, component.ProvidedContainer);
}
_hands.RemoveHand(chassis, handId, hands);
_hands.RemoveHand(chassis, handId);
}
component.ProvidedItems.Clear();
}

View File

@@ -1,3 +1,4 @@
using Content.Server.Hands.Systems;
using Content.Server.Popups;
using Content.Server.Tabletop.Components;
using Content.Shared.CCVar;
@@ -22,6 +23,7 @@ namespace Content.Server.Tabletop
{
[Dependency] private readonly SharedMapSystem _map = default!;
[Dependency] private readonly EyeSystem _eye = default!;
[Dependency] private readonly HandsSystem _hands = default!;
[Dependency] private readonly ViewSubscriberSystem _viewSubscriberSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
@@ -84,18 +86,13 @@ namespace Content.Server.Tabletop
if (component.Session is not { } session)
return;
if (hands.ActiveHand == null)
if (!_hands.TryGetActiveItem(uid, out var handEnt))
return;
if (hands.ActiveHand.HeldEntity == null)
return;
var handEnt = hands.ActiveHand.HeldEntity.Value;
if (!TryComp<ItemComponent>(handEnt, out var item))
return;
var meta = MetaData(handEnt);
var meta = MetaData(handEnt.Value);
var protoId = meta.EntityPrototype?.ID;
var hologram = Spawn(protoId, session.Position.Offset(-1, 0));

View File

@@ -90,9 +90,9 @@ public sealed class InnateToolSystem : EntitySystem
if (TryComp<HandsComponent>(uid, out var hands))
{
foreach (var hand in hands.Hands)
foreach (var hand in hands.Hands.Keys)
{
_sharedHandsSystem.TryDrop(uid, hand.Value, checkActionBlocker: false, handsComp: hands);
_sharedHandsSystem.TryDrop((uid, hands), hand, checkActionBlocker: false);
}
}
}

View File

@@ -1,10 +1,10 @@
using System.Linq;
using Content.Server.Administration.Managers;
using Content.Server.Hands.Systems;
using Content.Server.Popups;
using Content.Shared.Administration;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.Hands.Components;
using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Verbs;
using Robust.Shared.Utility;
@@ -14,6 +14,7 @@ namespace Content.Server.Verbs
public sealed class VerbSystem : SharedVerbSystem
{
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly HandsSystem _hands = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly IAdminManager _adminMgr = default!;
@@ -91,8 +92,7 @@ namespace Content.Server.Verbs
{
// first get the held item. again.
EntityUid? holding = null;
if (TryComp(user, out HandsComponent? hands) &&
hands.ActiveHandEntity is EntityUid heldEntity)
if (_hands.GetActiveItem(user) is { } heldEntity)
{
holding = heldEntity;
}

View File

@@ -2,7 +2,6 @@ using Content.Server.Hands.Systems;
using Content.Server.Storage.EntitySystems;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.Hands.Components;
using Content.Shared.Inventory;
using Content.Shared.Popups;
using Content.Shared.Storage;
@@ -40,24 +39,20 @@ public sealed class StorageVoiceControlSystem : EntitySystem
if (!TryComp<StorageComponent>(ent, out var storage))
return;
// Get the hands component
if (!TryComp<HandsComponent>(args.Source, out var hands))
return;
// If the player has something in their hands, try to insert it into the storage
if (hands.ActiveHand != null && hands.ActiveHand.HeldEntity.HasValue)
if (_hands.TryGetActiveItem(ent.Owner, out var activeItem))
{
// Disallow insertion and provide a reason why if the person decides to insert the item into itself
if (ent.Owner.Equals(hands.ActiveHand.HeldEntity.Value))
if (ent.Owner.Equals(activeItem.Value))
{
_popup.PopupEntity(Loc.GetString("comp-storagevoicecontrol-self-insert", ("entity", hands.ActiveHand.HeldEntity.Value)), ent, args.Source);
_popup.PopupEntity(Loc.GetString("comp-storagevoicecontrol-self-insert", ("entity", activeItem.Value)), ent, args.Source);
return;
}
if (_storage.CanInsert(ent, hands.ActiveHand.HeldEntity.Value, out var failedReason))
if (_storage.CanInsert(ent, activeItem.Value, out var failedReason))
{
// We adminlog before insertion, otherwise the logger will attempt to pull info on an entity that no longer is present and throw an exception
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.Source)} inserted {ToPrettyString(hands.ActiveHand.HeldEntity.Value)} into {ToPrettyString(ent)} via voice control");
_storage.Insert(ent, hands.ActiveHand.HeldEntity.Value, out _);
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.Source)} inserted {ToPrettyString(activeItem.Value)} into {ToPrettyString(ent)} via voice control");
_storage.Insert(ent, activeItem.Value, out _);
return;
}
{
@@ -67,7 +62,7 @@ public sealed class StorageVoiceControlSystem : EntitySystem
_popup.PopupEntity(Loc.GetString(failedReason), ent, args.Source);
_adminLogger.Add(LogType.Action,
LogImpact.Low,
$"{ToPrettyString(args.Source)} failed to insert {ToPrettyString(hands.ActiveHand.HeldEntity.Value)} into {ToPrettyString(ent)} via voice control");
$"{ToPrettyString(args.Source)} failed to insert {ToPrettyString(activeItem.Value)} into {ToPrettyString(ent)} via voice control");
}
return;
}
@@ -80,7 +75,7 @@ public sealed class StorageVoiceControlSystem : EntitySystem
// E.g "go go s" would give you the screwdriver because "screwdriver" contains "s"
if (Name(item).Contains(args.MessageWithoutPhrase))
{
ExtractItemFromStorage(ent, item, args.Source, hands);
ExtractItemFromStorage(ent, item, args.Source);
break;
}
}
@@ -92,16 +87,14 @@ public sealed class StorageVoiceControlSystem : EntitySystem
/// <param name="ent">The entity with the <see cref="StorageVoiceControlComponent"/></param>
/// <param name="item">The entity to be extracted from the attached storage</param>
/// <param name="source">The entity wearing the item</param>
/// <param name="hands">The <see cref="HandsComponent"/> of the person wearing the item</param>
private void ExtractItemFromStorage(Entity<StorageVoiceControlComponent> ent,
EntityUid item,
EntityUid source,
HandsComponent hands)
EntityUid source)
{
_container.RemoveEntity(ent, item);
_adminLogger.Add(LogType.Action,
LogImpact.Low,
$"{ToPrettyString(source)} retrieved {ToPrettyString(item)} from {ToPrettyString(ent)} via voice control");
_hands.TryPickup(source, item, handsComp: hands);
_hands.TryPickup(source, item);
}
}

View File

@@ -3,6 +3,7 @@ using System.Linq;
using System.Threading;
using Content.Server.Construction;
using Content.Server.Construction.Components;
using Content.Server.Hands.Systems;
using Content.Server.Power.Components;
using Content.Shared.DoAfter;
using Content.Shared.GameTicking;
@@ -24,6 +25,7 @@ public sealed class WiresSystem : SharedWiresSystem
{
[Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly HandsSystem _hands = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
@@ -405,19 +407,13 @@ public sealed class WiresSystem : SharedWiresSystem
return;
}
var activeHand = handsComponent.ActiveHand;
if (activeHand == null)
if (!_hands.TryGetActiveItem((player, handsComponent), out var heldEntity))
return;
if (activeHand.HeldEntity == null)
if (!EntityManager.TryGetComponent(heldEntity, out ToolComponent? tool))
return;
var activeHandEntity = activeHand.HeldEntity.Value;
if (!EntityManager.TryGetComponent(activeHandEntity, out ToolComponent? tool))
return;
TryDoWireAction(uid, player, activeHandEntity, args.Id, args.Action, component, tool);
TryDoWireAction(uid, player, heldEntity.Value, args.Id, args.Action, component, tool);
}
private void OnDoAfter(EntityUid uid, WiresComponent component, WireDoAfterEvent args)