using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Player;
-using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Client.Hands.Systems
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IUserInterfaceManager _ui = default!;
- [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly StrippableSystem _stripSys = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly ExamineSystem _examine = default!;
[Dependency] private readonly DisplacementMapSystem _displacement = default!;
- public event Action<string, HandLocation>? OnPlayerAddHand;
- public event Action<string>? OnPlayerRemoveHand;
public event Action<string?>? OnPlayerSetActiveHand;
- public event Action<HandsComponent>? OnPlayerHandsAdded;
+ public event Action<Entity<HandsComponent>>? OnPlayerHandsAdded;
public event Action? OnPlayerHandsRemoved;
public event Action<string, EntityUid>? OnPlayerItemAdded;
public event Action<string, EntityUid>? OnPlayerItemRemoved;
}
#region StateHandling
- private void HandleComponentState(EntityUid uid, HandsComponent component, ref ComponentHandleState args)
+ private void HandleComponentState(Entity<HandsComponent> ent, ref ComponentHandleState args)
{
if (args.Current is not HandsComponentState state)
return;
- var handsModified = component.Hands.Count != state.Hands.Count;
- // we need to check that, even if we have the same amount, that the individual hands didn't change.
- if (!handsModified)
+ var newHands = state.Hands.Keys.Except(ent.Comp.Hands.Keys); // hands that were added between states
+ var oldHands = ent.Comp.Hands.Keys.Except(state.Hands.Keys); // hands that were removed between states
+
+ foreach (var handId in oldHands)
{
- foreach (var hand in component.Hands.Values)
- {
- if (state.Hands.Contains(hand))
- continue;
- handsModified = true;
- break;
- }
+ RemoveHand(ent.AsNullable(), handId);
}
- var manager = EnsureComp<ContainerManagerComponent>(uid);
-
- if (handsModified)
+ foreach (var handId in state.SortedHands.Intersect(newHands))
{
- List<Hand> addedHands = new();
- foreach (var hand in state.Hands)
- {
- if (component.Hands.ContainsKey(hand.Name))
- continue;
-
- var container = _containerSystem.EnsureContainer<ContainerSlot>(uid, hand.Name, manager);
- var newHand = new Hand(hand.Name, hand.Location, container);
- component.Hands.Add(hand.Name, newHand);
- addedHands.Add(newHand);
- }
-
- foreach (var name in component.Hands.Keys)
- {
- if (!state.HandNames.Contains(name))
- {
- RemoveHand(uid, name, component);
- }
- }
-
- component.SortedHands.Clear();
- component.SortedHands.AddRange(state.HandNames);
- var sorted = addedHands.OrderBy(hand => component.SortedHands.IndexOf(hand.Name));
-
- foreach (var hand in sorted)
- {
- AddHand(uid, hand, component);
- }
+ AddHand(ent.AsNullable(), handId, state.Hands[handId]);
}
+ ent.Comp.SortedHands = new (state.SortedHands);
- _stripSys.UpdateUi(uid);
-
- if (component.ActiveHand == null && state.ActiveHand == null)
- return; //edge case
+ SetActiveHand(ent.AsNullable(), state.ActiveHandId);
- if (component.ActiveHand != null && state.ActiveHand != component.ActiveHand.Name)
- {
- SetActiveHand(uid, component.Hands[state.ActiveHand!], component);
- }
+ _stripSys.UpdateUi(ent);
}
#endregion
return;
}
- OnPlayerHandsAdded?.Invoke(hands);
+ OnPlayerHandsAdded?.Invoke(hands.Value);
}
- public override void DoDrop(EntityUid uid, Hand hand, bool doDropInteraction = true, HandsComponent? hands = null, bool log = true)
+ public override void DoDrop(Entity<HandsComponent?> ent,
+ string handId,
+ bool doDropInteraction = true,
+ bool log = true)
{
- base.DoDrop(uid, hand, doDropInteraction, hands, log);
+ base.DoDrop(ent, handId, doDropInteraction, log);
- if (TryComp(hand.HeldEntity, out SpriteComponent? sprite))
+ if (TryGetHeldItem(ent, handId, out var held) && TryComp(held, out SpriteComponent? sprite))
sprite.RenderOrder = EntityManager.CurrentTick.Value;
}
public EntityUid? GetActiveHandEntity()
{
- return TryGetPlayerHands(out var hands) ? hands.ActiveHandEntity : null;
+ return TryGetPlayerHands(out var hands) ? GetActiveItem(hands.Value.AsNullable()) : null;
}
/// <summary>
/// Get the hands component of the local player
/// </summary>
- public bool TryGetPlayerHands([NotNullWhen(true)] out HandsComponent? hands)
+ public bool TryGetPlayerHands([NotNullWhen(true)] out Entity<HandsComponent>? hands)
{
var player = _playerManager.LocalEntity;
hands = null;
- return player != null && TryComp(player.Value, out hands);
+ if (player == null || !TryComp<HandsComponent>(player.Value, out var handsComp))
+ return false;
+
+ hands = (player.Value, handsComp);
+ return true;
}
/// <summary>
/// Called when a user clicked on their hands GUI
/// </summary>
- public void UIHandClick(HandsComponent hands, string handName)
+ public void UIHandClick(Entity<HandsComponent> ent, string handName)
{
- if (!hands.Hands.TryGetValue(handName, out var pressedHand))
- return;
-
- if (hands.ActiveHand == null)
+ var hands = ent.Comp;
+ if (hands.ActiveHandId == null)
return;
- var pressedEntity = pressedHand.HeldEntity;
- var activeEntity = hands.ActiveHand.HeldEntity;
+ var pressedEntity = GetHeldItem(ent.AsNullable(), handName);
+ var activeEntity = GetActiveItem(ent.AsNullable());
- if (pressedHand == hands.ActiveHand && activeEntity != null)
+ if (handName == hands.ActiveHandId && activeEntity != null)
{
// use item in hand
// it will always be attack_self() in my heart.
return;
}
- if (pressedHand != hands.ActiveHand && pressedEntity == null)
+ if (handName != hands.ActiveHandId && pressedEntity == null)
{
// change active hand
EntityManager.RaisePredictiveEvent(new RequestSetHandEvent(handName));
return;
}
- if (pressedHand != hands.ActiveHand && pressedEntity != null && activeEntity != null)
+ if (handName != hands.ActiveHandId && pressedEntity != null && activeEntity != null)
{
// use active item on held item
- EntityManager.RaisePredictiveEvent(new RequestHandInteractUsingEvent(pressedHand.Name));
+ EntityManager.RaisePredictiveEvent(new RequestHandInteractUsingEvent(handName));
return;
}
- if (pressedHand != hands.ActiveHand && pressedEntity != null && activeEntity == null)
+ if (handName != hands.ActiveHandId && pressedEntity != null && activeEntity == null)
{
// move the item to the active hand
- EntityManager.RaisePredictiveEvent(new RequestMoveHandItemEvent(pressedHand.Name));
+ EntityManager.RaisePredictiveEvent(new RequestMoveHandItemEvent(handName));
}
}
public void UIInventoryExamine(string handName)
{
if (!TryGetPlayerHands(out var hands) ||
- !hands.Hands.TryGetValue(handName, out var hand) ||
- hand.HeldEntity is not { Valid: true } entity)
+ !TryGetHeldItem(hands.Value.AsNullable(), handName, out var heldEntity))
{
return;
}
- _examine.DoExamine(entity);
+ _examine.DoExamine(heldEntity.Value);
}
/// <summary>
public void UIHandOpenContextMenu(string handName)
{
if (!TryGetPlayerHands(out var hands) ||
- !hands.Hands.TryGetValue(handName, out var hand) ||
- hand.HeldEntity is not { Valid: true } entity)
+ !TryGetHeldItem(hands.Value.AsNullable(), handName, out var heldEntity))
{
return;
}
- _ui.GetUIController<VerbMenuUIController>().OpenVerbMenu(entity);
+ _ui.GetUIController<VerbMenuUIController>().OpenVerbMenu(heldEntity.Value);
}
public void UIHandAltActivateItem(string handName)
{
base.HandleEntityInserted(uid, hands, args);
- if (!hands.Hands.TryGetValue(args.Container.ID, out var hand))
+ if (!hands.Hands.ContainsKey(args.Container.ID))
return;
- UpdateHandVisuals(uid, args.Entity, hand);
+
+ UpdateHandVisuals(uid, args.Entity, args.Container.ID);
_stripSys.UpdateUi(uid);
if (uid != _playerManager.LocalEntity)
return;
- OnPlayerItemAdded?.Invoke(hand.Name, args.Entity);
+ OnPlayerItemAdded?.Invoke(args.Container.ID, args.Entity);
if (HasComp<VirtualItemComponent>(args.Entity))
- OnPlayerHandBlocked?.Invoke(hand.Name);
+ OnPlayerHandBlocked?.Invoke(args.Container.ID);
}
protected override void HandleEntityRemoved(EntityUid uid, HandsComponent hands, EntRemovedFromContainerMessage args)
{
base.HandleEntityRemoved(uid, hands, args);
- if (!hands.Hands.TryGetValue(args.Container.ID, out var hand))
+ if (!hands.Hands.ContainsKey(args.Container.ID))
return;
- UpdateHandVisuals(uid, args.Entity, hand);
+
+ UpdateHandVisuals(uid, args.Entity, args.Container.ID);
_stripSys.UpdateUi(uid);
if (uid != _playerManager.LocalEntity)
return;
- OnPlayerItemRemoved?.Invoke(hand.Name, args.Entity);
+ OnPlayerItemRemoved?.Invoke(args.Container.ID, args.Entity);
if (HasComp<VirtualItemComponent>(args.Entity))
- OnPlayerHandUnblocked?.Invoke(hand.Name);
+ OnPlayerHandUnblocked?.Invoke(args.Container.ID);
}
/// <summary>
/// Update the players sprite with new in-hand visuals.
/// </summary>
- private void UpdateHandVisuals(EntityUid uid, EntityUid held, Hand hand, HandsComponent? handComp = null, SpriteComponent? sprite = null)
+ private void UpdateHandVisuals(Entity<HandsComponent?, SpriteComponent?> ent, EntityUid held, string handId)
{
- if (!Resolve(uid, ref handComp, ref sprite, false))
+ if (!Resolve(ent, ref ent.Comp1, ref ent.Comp2, false))
+ return;
+ var handComp = ent.Comp1;
+ var sprite = ent.Comp2;
+
+ if (!TryGetHand((ent, handComp), handId, out var hand))
return;
// visual update might involve changes to the entity's effective sprite -> need to update hands GUI.
- if (uid == _playerManager.LocalEntity)
- OnPlayerItemAdded?.Invoke(hand.Name, held);
+ if (ent == _playerManager.LocalEntity)
+ OnPlayerItemAdded?.Invoke(handId, held);
if (!handComp.ShowInHands)
return;
// Remove old layers. We could also just set them to invisible, but as items may add arbitrary layers, this
// may eventually bloat the player with lots of layers.
- if (handComp.RevealedLayers.TryGetValue(hand.Location, out var revealedLayers))
+ if (handComp.RevealedLayers.TryGetValue(hand.Value.Location, out var revealedLayers))
{
foreach (var key in revealedLayers)
{
- _sprite.RemoveLayer((uid, sprite), key);
+ _sprite.RemoveLayer((ent, sprite), key);
}
revealedLayers.Clear();
else
{
revealedLayers = new();
- handComp.RevealedLayers[hand.Location] = revealedLayers;
+ handComp.RevealedLayers[hand.Value.Location] = revealedLayers;
}
- if (hand.HeldEntity == null)
+ if (HandIsEmpty((ent, handComp), handId))
{
// the held item was removed.
- RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers), true);
+ RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(ent, revealedLayers), true);
return;
}
- var ev = new GetInhandVisualsEvent(uid, hand.Location);
+ var ev = new GetInhandVisualsEvent(ent, hand.Value.Location);
RaiseLocalEvent(held, ev);
if (ev.Layers.Count == 0)
{
- RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers), true);
+ RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(ent, revealedLayers), true);
return;
}
continue;
}
- var index = _sprite.LayerMapReserve((uid, sprite), key);
+ var index = _sprite.LayerMapReserve((ent, sprite), key);
// In case no RSI is given, use the item's base RSI as a default. This cuts down on a lot of unnecessary yaml entries.
if (layerData.RsiPath == null
&& sprite[index].Rsi == null)
{
if (TryComp<ItemComponent>(held, out var itemComponent) && itemComponent.RsiPath != null)
- _sprite.LayerSetRsi((uid, sprite), index, new ResPath(itemComponent.RsiPath));
+ _sprite.LayerSetRsi((ent, sprite), index, new ResPath(itemComponent.RsiPath));
else if (TryComp(held, out SpriteComponent? clothingSprite))
- _sprite.LayerSetRsi((uid, sprite), index, clothingSprite.BaseRSI);
+ _sprite.LayerSetRsi((ent, sprite), index, clothingSprite.BaseRSI);
}
- _sprite.LayerSetData((uid, sprite), index, layerData);
+ _sprite.LayerSetData((ent, sprite), index, layerData);
// Add displacement maps
- var displacement = hand.Location switch
+ var displacement = hand.Value.Location switch
{
HandLocation.Left => handComp.LeftHandDisplacement,
HandLocation.Right => handComp.RightHandDisplacement,
_ => handComp.HandDisplacement
};
- if (displacement is not null && _displacement.TryAddDisplacement(displacement, (uid, sprite), index, key, out var displacementKey))
+ if (displacement is not null && _displacement.TryAddDisplacement(displacement, (ent, sprite), index, key, out var displacementKey))
revealedLayers.Add(displacementKey);
}
- RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers), true);
+ RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(ent, revealedLayers), true);
}
private void OnVisualsChanged(EntityUid uid, HandsComponent component, VisualsChangedEvent args)
{
// update hands visuals if this item is in a hand (rather then inventory or other container).
- if (component.Hands.TryGetValue(args.ContainerId, out var hand))
- {
- UpdateHandVisuals(uid, GetEntity(args.Item), hand, component);
- }
+ if (!component.Hands.ContainsKey(args.ContainerId))
+ return;
+ UpdateHandVisuals((uid, component), GetEntity(args.Item), args.ContainerId);
}
#endregion
private void HandlePlayerAttached(EntityUid uid, HandsComponent component, LocalPlayerAttachedEvent args)
{
- OnPlayerHandsAdded?.Invoke(component);
+ OnPlayerHandsAdded?.Invoke((uid, component));
}
private void HandlePlayerDetached(EntityUid uid, HandsComponent component, LocalPlayerDetachedEvent args)
private void OnHandsStartup(EntityUid uid, HandsComponent component, ComponentStartup args)
{
if (_playerManager.LocalEntity == uid)
- OnPlayerHandsAdded?.Invoke(component);
+ OnPlayerHandsAdded?.Invoke((uid, component));
}
private void OnHandsShutdown(EntityUid uid, HandsComponent component, ComponentShutdown args)
}
#endregion
- private void AddHand(EntityUid uid, Hand newHand, HandsComponent? handsComp = null)
- {
- AddHand(uid, newHand.Name, newHand.Location, handsComp);
- }
-
- public override void AddHand(EntityUid uid, string handName, HandLocation handLocation, HandsComponent? handsComp = null)
- {
- base.AddHand(uid, handName, handLocation, handsComp);
-
- if (uid == _playerManager.LocalEntity)
- OnPlayerAddHand?.Invoke(handName, handLocation);
-
- if (handsComp == null)
- return;
-
- if (handsComp.ActiveHand == null)
- SetActiveHand(uid, handsComp.Hands[handName], handsComp);
- }
- public override void RemoveHand(EntityUid uid, string handName, HandsComponent? handsComp = null)
- {
- if (uid == _playerManager.LocalEntity && handsComp != null &&
- handsComp.Hands.ContainsKey(handName) && uid ==
- _playerManager.LocalEntity)
- {
- OnPlayerRemoveHand?.Invoke(handName);
- }
-
- base.RemoveHand(uid, handName, handsComp);
- }
-
private void OnHandActivated(Entity<HandsComponent>? ent)
{
if (ent is not { } hand)
if (_playerManager.LocalEntity != hand.Owner)
return;
- if (hand.Comp.ActiveHand == null)
- {
- OnPlayerSetActiveHand?.Invoke(null);
- return;
- }
-
- OnPlayerSetActiveHand?.Invoke(hand.Comp.ActiveHand.Name);
+ OnPlayerSetActiveHand?.Invoke(hand.Comp.ActiveHandId);
}
}
}
using System.Linq;
using System.Numerics;
using Content.Client.Examine;
+using Content.Client.Hands.Systems;
using Content.Client.Strip;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Controls;
[Dependency] private readonly IUserInterfaceManager _ui = default!;
private readonly ExamineSystem _examine;
+ private readonly HandsSystem _hands;
private readonly InventorySystem _inv;
private readonly SharedCuffableSystem _cuffable;
private readonly StrippableSystem _strippable;
public StrippableBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
_examine = EntMan.System<ExamineSystem>();
+ _hands = EntMan.System<HandsSystem>();
_inv = EntMan.System<InventorySystem>();
_cuffable = EntMan.System<SharedCuffableSystem>();
_strippable = EntMan.System<StrippableSystem>();
{
// good ol hands shit code. there is a GuiHands comparer that does the same thing... but these are hands
// and not gui hands... which are different...
- foreach (var hand in handsComp.Hands.Values)
+ foreach (var (id, hand) in handsComp.Hands)
{
if (hand.Location != HandLocation.Right)
continue;
- AddHandButton(hand);
+ AddHandButton((Owner, handsComp), id, hand);
}
- foreach (var hand in handsComp.Hands.Values)
+ foreach (var (id, hand) in handsComp.Hands)
{
if (hand.Location != HandLocation.Middle)
continue;
- AddHandButton(hand);
+ AddHandButton((Owner, handsComp), id, hand);
}
- foreach (var hand in handsComp.Hands.Values)
+ foreach (var (id, hand) in handsComp.Hands)
{
if (hand.Location != HandLocation.Left)
continue;
- AddHandButton(hand);
+ AddHandButton((Owner, handsComp), id, hand);
}
}
_strippingMenu.SetSize = new Vector2(horizontalMenuSize, verticalMenuSize);
}
- private void AddHandButton(Hand hand)
+ private void AddHandButton(Entity<HandsComponent> ent, string handId, Hand hand)
{
- var button = new HandButton(hand.Name, hand.Location);
+ var button = new HandButton(handId, hand.Location);
button.Pressed += SlotPressed;
- if (EntMan.TryGetComponent<VirtualItemComponent>(hand.HeldEntity, out var virt))
+ var heldEntity = _hands.GetHeldItem(ent.AsNullable(), handId);
+ if (EntMan.TryGetComponent<VirtualItemComponent>(heldEntity, out var virt))
{
button.Blocked = true;
if (EntMan.TryGetComponent<CuffableComponent>(Owner, out var cuff) && _cuffable.GetAllCuffs(cuff).Contains(virt.BlockingEntity))
button.BlockedRect.MouseFilter = MouseFilterMode.Ignore;
}
- UpdateEntityIcon(button, hand.HeldEntity);
+ UpdateEntityIcon(button, heldEntity);
_strippingMenu!.HandsContainer.AddChild(button);
LayoutContainer.SetPosition(button, new Vector2i(_handCount, 0) * (SlotControl.DefaultButtonSize + ButtonSeparation));
_handCount++;
using System.Numerics;
using Content.Client.Gameplay;
+using Content.Client.Hands.Systems;
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
using Content.Shared.RCD.Components;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
private readonly SharedMapSystem _mapSystem;
+ private readonly HandsSystem _handsSystem;
private readonly RCDSystem _rcdSystem;
private readonly SharedTransformSystem _transformSystem;
[Dependency] private readonly IPlayerManager _playerManager = default!;
{
IoCManager.InjectDependencies(this);
_mapSystem = _entityManager.System<SharedMapSystem>();
+ _handsSystem = _entityManager.System<HandsSystem>();
_rcdSystem = _entityManager.System<RCDSystem>();
_transformSystem = _entityManager.System<SharedTransformSystem>();
}
// Determine if player is carrying an RCD in their active hand
- if (!_entityManager.TryGetComponent<HandsComponent>(player, out var hands))
+ if (!_handsSystem.TryGetActiveItem(player.Value, out var heldEntity))
return false;
- var heldEntity = hands.ActiveHand?.HeldEntity;
-
if (!_entityManager.TryGetComponent<RCDComponent>(heldEntity, out var rcd))
return false;
+using Content.Client.Hands.Systems;
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
using Content.Shared.RCD;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IPlacementManager _placementManager = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
+ [Dependency] private readonly HandsSystem _hands = default!;
private string _placementMode = typeof(AlignRCDConstruction).Name;
private Direction _placementDirection = default;
return;
// Determine if player is carrying an RCD in their active hand
- var player = _playerManager.LocalSession?.AttachedEntity;
-
- if (!TryComp<HandsComponent>(player, out var hands))
+ if (_playerManager.LocalSession?.AttachedEntity is not { } player)
return;
- var heldEntity = hands.ActiveHand?.HeldEntity;
+ if (!_hands.TryGetActiveItem(player, out var heldEntity))
+ return;
if (!TryComp<RCDComponent>(heldEntity, out var rcd))
{
foreach (var hand in _hands.EnumerateHands(player.Value))
{
- if (!scannerQuery.TryGetComponent(hand.HeldEntity, out var heldScanner) || !heldScanner.Enabled)
+ if (!_hands.TryGetHeldItem(player.Value, hand, out var heldEntity))
+ continue;
+
+ if (!scannerQuery.TryGetComponent(heldEntity, out var heldScanner) || !heldScanner.Enabled)
continue;
range = MathF.Max(heldScanner.Range, range);
canSee = true;
+ break;
}
inRange = new HashSet<Entity<SubFloorHideComponent>>();
using Content.Shared.Input;
using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Timing;
+using JetBrains.Annotations;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers;
private readonly Dictionary<string, int> _handContainerIndices = new();
private readonly Dictionary<string, HandButton> _handLookup = new();
private HandsComponent? _playerHandsComponent;
- private HandButton? _activeHand = null;
+ private HandButton? _activeHand;
// We only have two item status controls (left and right hand),
// but we may have more than two hands.
private HandButton? _statusHandLeft;
private HandButton? _statusHandRight;
- private int _backupSuffix = 0; //this is used when autogenerating container names if they don't have names
+ private int _backupSuffix; //this is used when autogenerating container names if they don't have names
private HotbarGui? HandsGui => UIManager.GetActiveUIWidgetOrNull<HotbarGui>();
_handsSystem.OnPlayerItemAdded += OnItemAdded;
_handsSystem.OnPlayerItemRemoved += OnItemRemoved;
_handsSystem.OnPlayerSetActiveHand += SetActiveHand;
- _handsSystem.OnPlayerRemoveHand += RemoveHand;
+ _handsSystem.OnPlayerRemoveHand += OnRemoveHand;
_handsSystem.OnPlayerHandsAdded += LoadPlayerHands;
_handsSystem.OnPlayerHandsRemoved += UnloadPlayerHands;
_handsSystem.OnPlayerHandBlocked += HandBlocked;
_handsSystem.OnPlayerItemAdded -= OnItemAdded;
_handsSystem.OnPlayerItemRemoved -= OnItemRemoved;
_handsSystem.OnPlayerSetActiveHand -= SetActiveHand;
- _handsSystem.OnPlayerRemoveHand -= RemoveHand;
+ _handsSystem.OnPlayerRemoveHand -= OnRemoveHand;
_handsSystem.OnPlayerHandsAdded -= LoadPlayerHands;
_handsSystem.OnPlayerHandsRemoved -= UnloadPlayerHands;
_handsSystem.OnPlayerHandBlocked -= HandBlocked;
_handsSystem.OnPlayerHandUnblocked -= HandUnblocked;
}
- private void OnAddHand(string name, HandLocation location)
+ private void OnAddHand(Entity<HandsComponent> entity, string name, HandLocation location)
{
+ if (entity.Owner != _player.LocalEntity)
+ return;
AddHand(name, location);
}
+ private void OnRemoveHand(Entity<HandsComponent> entity, string name)
+ {
+ if (entity.Owner != _player.LocalEntity)
+ return;
+ RemoveHand(name);
+ }
+
private void HandPressed(GUIBoundKeyEventArgs args, SlotControl hand)
{
- if (_playerHandsComponent == null)
- {
+ if (!_handsSystem.TryGetPlayerHands(out var hands))
return;
- }
if (args.Function == EngineKeyFunctions.UIClick)
{
- _handsSystem.UIHandClick(_playerHandsComponent, hand.SlotName);
+ _handsSystem.UIHandClick(hands.Value, hand.SlotName);
args.Handle();
}
else if (args.Function == EngineKeyFunctions.UseSecondary)
}
}
- private void LoadPlayerHands(HandsComponent handsComp)
+ private void LoadPlayerHands(Entity<HandsComponent> handsComp)
{
DebugTools.Assert(_playerHandsComponent == null);
if (HandsGui != null)
HandsGui.Visible = true;
_playerHandsComponent = handsComp;
- foreach (var (name, hand) in handsComp.Hands)
+ foreach (var (name, hand) in handsComp.Comp.Hands)
{
var handButton = AddHand(name, hand.Location);
- if (_entities.TryGetComponent(hand.HeldEntity, out VirtualItemComponent? virt))
+ if (_handsSystem.TryGetHeldItem(handsComp.AsNullable(), name, out var held) &&
+ _entities.TryGetComponent(held, out VirtualItemComponent? virt))
{
handButton.SetEntity(virt.BlockingEntity);
handButton.Blocked = true;
}
else
{
- handButton.SetEntity(hand.HeldEntity);
+ handButton.SetEntity(held);
handButton.Blocked = false;
}
}
- var activeHand = handsComp.ActiveHand;
- if (activeHand == null)
+ if (handsComp.Comp.ActiveHandId == null)
return;
- SetActiveHand(activeHand.Name);
+ SetActiveHand(handsComp.Comp.ActiveHandId);
}
private void HandBlocked(string handName)
if (HandsGui != null &&
_playerHandsComponent != null &&
_player.LocalSession?.AttachedEntity is { } playerEntity &&
- _handsSystem.TryGetHand(playerEntity, handName, out var hand, _playerHandsComponent))
+ _handsSystem.TryGetHand((playerEntity, _playerHandsComponent), handName, out var hand))
{
- var foldedLocation = hand.Location.GetUILocation();
+ var heldEnt = _handsSystem.GetHeldItem((playerEntity, _playerHandsComponent), handName);
+
+ var foldedLocation = hand.Value.Location.GetUILocation();
if (foldedLocation == HandUILocation.Left)
{
_statusHandLeft = handControl;
- HandsGui.UpdatePanelEntityLeft(hand.HeldEntity);
+ HandsGui.UpdatePanelEntityLeft(heldEnt);
}
else
{
// Middle or right
_statusHandRight = handControl;
- HandsGui.UpdatePanelEntityRight(hand.HeldEntity);
+ HandsGui.UpdatePanelEntityRight(heldEnt);
}
HandsGui.SetHighlightHand(foldedLocation);
button.Pressed += HandPressed;
if (!_handLookup.TryAdd(handName, button))
- throw new Exception("Tried to add hand with duplicate name to UI. Name:" + handName);
+ return _handLookup[handName];
if (HandsGui != null)
{
RemoveHand(handName, out _);
}
+ [PublicAPI]
private bool RemoveHand(string handName, out HandButton? handButton)
{
if (!_handLookup.TryGetValue(handName, out handButton))
_statusHandRight = null;
_handLookup.Remove(handName);
- handButton.Dispose();
+ handButton.Orphan();
UpdateVisibleStatusPanels();
return true;
}
var player = _playerUid;
if (!control.MouseIsHovering ||
- _playerInventory == null ||
- !_entities.TryGetComponent<HandsComponent>(player, out var hands) ||
- hands.ActiveHandEntity is not { } held ||
+ player == null ||
+ !_handsSystem.TryGetActiveItem(player.Value, out var held) ||
!_entities.TryGetComponent(held, out SpriteComponent? sprite) ||
!_inventorySystem.TryGetSlotContainer(player.Value, control.SlotName, out var container, out var slotDef))
{
// Set green / red overlay at 50% transparency
var hoverEntity = _entities.SpawnEntity("hoverentity", MapCoordinates.Nullspace);
var hoverSprite = _entities.GetComponent<SpriteComponent>(hoverEntity);
- var fits = _inventorySystem.CanEquip(player.Value, held, control.SlotName, out _, slotDef) &&
- _container.CanInsert(held, container);
+ var fits = _inventorySystem.CanEquip(player.Value, held.Value, control.SlotName, out _, slotDef) &&
+ _container.CanInsert(held.Value, container);
if (!fits && _entities.TryGetComponent<StorageComponent>(container.ContainedEntity, out var storage))
{
- fits = _entities.System<StorageSystem>().CanInsert(container.ContainedEntity.Value, held, out _, storage);
+ fits = _entities.System<StorageSystem>().CanInsert(container.ContainedEntity.Value, held.Value, out _, storage);
}
else if (!fits && _entities.TryGetComponent<ItemSlotsComponent>(container.ContainedEntity, out var itemSlots))
{
if (!slot.InsertOnInteract)
continue;
- if (!itemSlotsSys.CanInsert(container.ContainedEntity.Value, held, null, slot))
+ if (!itemSlotsSys.CanInsert(container.ContainedEntity.Value, held.Value, null, slot))
continue;
fits = true;
break;
}
}
- _sprite.CopySprite((held, sprite), (hoverEntity, hoverSprite));
+ _sprite.CopySprite((held.Value, sprite), (hoverEntity, hoverSprite));
_sprite.SetColor((hoverEntity, hoverSprite), fits ? new Color(0, 255, 0, 127) : new Color(255, 0, 0, 127));
control.HoverSpriteView.SetEntity(hoverEntity);
await Server.WaitAssertion(() =>
{
// Make sure the player's hand starts empty
- var heldItem = Hands.ActiveHandEntity;
+ var heldItem = handsSystem.GetActiveItem((playerUid, Hands));
Assert.That(heldItem, Is.Null, $"Player is holding an item ({SEntMan.ToPrettyString(heldItem)}) at start of test.");
// Inspect the action prototype to find the item it spawns
var actionEnt = actionsSystem.GetAction(actionUid);
// Make sure the player's hand is still empty
- heldItem = Hands.ActiveHandEntity;
+ heldItem = handsSystem.GetActiveItem((playerUid, Hands));
Assert.That(heldItem, Is.Null, $"Player is holding an item ({SEntMan.ToPrettyString(heldItem)}) after adding action.");
// Activate the arm blade
actionsSystem.PerformAction(ToServer(Player), actionEnt!.Value);
// Make sure the player is now holding the expected item
- heldItem = Hands.ActiveHandEntity;
+ heldItem = handsSystem.GetActiveItem((playerUid, Hands));
Assert.That(heldItem, Is.Not.Null, $"Expected player to be holding {spawnedProtoId} but was holding nothing.");
AssertPrototype(spawnedProtoId, SEntMan.GetNetEntity(heldItem));
actionsSystem.PerformAction(ToServer(Player), actionEnt.Value);
// Make sure the player's hand is empty again
- heldItem = Hands.ActiveHandEntity;
+ heldItem = handsSystem.GetActiveItem((playerUid, Hands));
Assert.That(heldItem, Is.Null, $"Player is still holding an item ({SEntMan.ToPrettyString(heldItem)}) after second use.");
});
}
Assert.That(buckle.Buckled);
// With items in all hands
- foreach (var hand in hands.Hands.Values)
+ foreach (var hand in hands.Hands.Keys)
{
- Assert.That(hand.HeldEntity, Is.Not.Null);
+ Assert.That(handsSys.GetHeldItem((human, hands), hand), Is.Not.Null);
}
var bodySystem = entityManager.System<BodySystem>();
Assert.That(buckle.Buckled);
// Now with no item in any hand
- foreach (var hand in hands.Hands.Values)
+ foreach (var hand in hands.Hands.Keys)
{
- Assert.That(hand.HeldEntity, Is.Null);
+ Assert.That(handsSys.GetHeldItem((human, hands), hand), Is.Null);
}
buckleSystem.Unbuckle(human, human);
using Content.Client.Chemistry.UI;
using Content.IntegrationTests.Tests.Interaction;
using Content.Shared.Chemistry;
-using Content.Server.Chemistry.Components;
using Content.Shared.Containers.ItemSlots;
namespace Content.IntegrationTests.Tests.Chemistry;
// Insert beaker
await InteractUsing("Beaker");
- Assert.That(Hands.ActiveHandEntity, Is.Null);
+ Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
// Open BUI
await Interact();
await SendBui(ReagentDispenserUiKey.Key, ev);
// Beaker is back in the player's hands
- Assert.That(Hands.ActiveHandEntity, Is.Not.Null);
- AssertPrototype("Beaker", SEntMan.GetNetEntity(Hands.ActiveHandEntity));
+ Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Not.Null);
+ AssertPrototype("Beaker", SEntMan.GetNetEntity(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands))));
// Re-insert the beaker
await Interact();
- Assert.That(Hands.ActiveHandEntity, Is.Null);
+ Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
// Re-eject using the button directly instead of sending a BUI event. This test is really just a test of the
// bui/window helper methods.
await ClickControl<ReagentDispenserWindow>(nameof(ReagentDispenserWindow.EjectButton));
await RunTicks(5);
- Assert.That(Hands.ActiveHandEntity, Is.Not.Null);
- AssertPrototype("Beaker", SEntMan.GetNetEntity(Hands.ActiveHandEntity));
+ Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Not.Null);
+ AssertPrototype("Beaker", SEntMan.GetNetEntity(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands))));
}
}
await server.WaitPost(() =>
{
var item = entManager.SpawnEntity("SharpTestObject", transformSystem.GetMapCoordinates(player));
- Assert.That(handsSystem.TryPickup(player, item, handsComponent.ActiveHand!));
+ Assert.That(handsSystem.TryPickup(player, item, handsComponent.ActiveHandId!));
entManager.TryGetComponent<ExecutionComponent>(item, out var executionComponent);
Assert.That(executionComponent, Is.Not.EqualTo(null));
});
await server.WaitPost(() =>
{
var item = entManager.SpawnEntity("MixedDamageTestObject", transformSystem.GetMapCoordinates(player));
- Assert.That(handsSystem.TryPickup(player, item, handsComponent.ActiveHand!));
+ Assert.That(handsSystem.TryPickup(player, item, handsComponent.ActiveHandId!));
entManager.TryGetComponent<ExecutionComponent>(item, out var executionComponent);
Assert.That(executionComponent, Is.Not.EqualTo(null));
});
{
await StartConstruction(Wall);
await InteractUsing(Steel, 2);
- Assert.That(Hands.ActiveHandEntity, Is.Null);
+ Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
ClientAssertPrototype(Girder, Target);
await InteractUsing(Steel, 2);
- Assert.That(Hands.ActiveHandEntity, Is.Null);
+ Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
AssertPrototype(WallSolid);
}
var xform = entMan.GetComponent<TransformComponent>(player);
item = entMan.SpawnEntity("Crowbar", tSys.GetMapCoordinates(player, xform: xform));
hands = entMan.GetComponent<HandsComponent>(player);
- sys.TryPickup(player, item, hands.ActiveHand!);
+ sys.TryPickup(player, item, hands.ActiveHandId!);
});
// run ticks here is important, as errors may happen within the container system's frame update methods.
await pair.RunTicksSync(5);
- Assert.That(hands.ActiveHandEntity, Is.EqualTo(item));
+ Assert.That(sys.GetActiveItem((player, hands)), Is.EqualTo(item));
await server.WaitPost(() =>
{
- sys.TryDrop(player, item, null!);
+ sys.TryDrop(player, item);
});
await pair.RunTicksSync(5);
- Assert.That(hands.ActiveHandEntity, Is.Null);
+ Assert.That(sys.GetActiveItem((player, hands)), Is.Null);
await server.WaitPost(() => mapSystem.DeleteMap(data.MapId));
await pair.CleanReturnAsync();
player = playerMan.Sessions.First().AttachedEntity!.Value;
tSys.PlaceNextTo(player, item);
hands = entMan.GetComponent<HandsComponent>(player);
- sys.TryPickup(player, item, hands.ActiveHand!);
+ sys.TryPickup(player, item, hands.ActiveHandId!);
});
await pair.RunTicksSync(5);
- Assert.That(hands.ActiveHandEntity, Is.EqualTo(item));
+ Assert.That(sys.GetActiveItem((player, hands)), Is.EqualTo(item));
// Open then close the box to place the player, who is holding the crowbar, inside of it
var storage = server.System<EntityStorageSystem>();
// with the item not being in the player's hands
await server.WaitPost(() =>
{
- sys.TryDrop(player, item, null!);
+ sys.TryDrop(player, item);
});
await pair.RunTicksSync(5);
var xform = entMan.GetComponent<TransformComponent>(player);
var itemXform = entMan.GetComponent<TransformComponent>(item);
- Assert.That(hands.ActiveHandEntity, Is.Not.EqualTo(item));
+ Assert.That(sys.GetActiveItem((player, hands)), Is.Not.EqualTo(item));
Assert.That(containerSystem.IsInSameOrNoContainer((player, xform), (item, itemXform)));
await server.WaitPost(() => mapSystem.DeleteMap(map.MapId));
/// </summary>
protected async Task DeleteHeldEntity()
{
- if (Hands.ActiveHandEntity is { } held)
+ if (HandSys.GetActiveItem((ToServer(Player), Hands)) is { } held)
{
await Server.WaitPost(() =>
{
- Assert.That(HandSys.TryDrop(SEntMan.GetEntity(Player), null, false, true, Hands));
+ Assert.That(HandSys.TryDrop((SEntMan.GetEntity(Player), Hands), null, false, true));
SEntMan.DeleteEntity(held);
SLogger.Debug($"Deleting held entity");
});
}
await RunTicks(1);
- Assert.That(Hands.ActiveHandEntity, Is.Null);
+ Assert.That(HandSys.GetActiveItem((ToServer(Player), Hands)), Is.Null);
}
/// <summary>
/// <param name="enableToggleable">Whether or not to automatically enable any toggleable items</param>
protected async Task<NetEntity> PlaceInHands(EntitySpecifier entity, bool enableToggleable = true)
{
- if (Hands.ActiveHand == null)
+ if (Hands.ActiveHandId == null)
{
Assert.Fail("No active hand");
return default;
{
var playerEnt = SEntMan.GetEntity(Player);
- Assert.That(HandSys.TryPickup(playerEnt, item, Hands.ActiveHand, false, false, Hands));
+ Assert.That(HandSys.TryPickup(playerEnt, item, Hands.ActiveHandId, false, false, false, Hands));
// turn on welders
if (enableToggleable && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated)
});
await RunTicks(1);
- Assert.That(Hands.ActiveHandEntity, Is.EqualTo(item));
+ Assert.That(HandSys.GetActiveItem((ToServer(Player), Hands)), Is.EqualTo(item));
if (enableToggleable && itemToggle != null)
Assert.That(itemToggle.Activated);
{
entity ??= Target;
- if (Hands.ActiveHand == null)
+ if (Hands.ActiveHandId == null)
{
Assert.Fail("No active hand");
return;
await Server.WaitPost(() =>
{
- Assert.That(HandSys.TryPickup(SEntMan.GetEntity(Player), uid.Value, Hands.ActiveHand, false, false, Hands, item));
+ Assert.That(HandSys.TryPickup(ToServer(Player), uid.Value, Hands.ActiveHandId, false, false, false, Hands, item));
});
await RunTicks(1);
- Assert.That(Hands.ActiveHandEntity, Is.EqualTo(uid));
+ Assert.That(HandSys.GetActiveItem((ToServer(Player), Hands)), Is.EqualTo(uid));
}
/// <summary>
/// </summary>
protected async Task Drop()
{
- if (Hands.ActiveHandEntity == null)
+ if (HandSys.GetActiveItem((ToServer(Player), Hands)) == null)
{
Assert.Fail("Not holding any entity to drop");
return;
await Server.WaitPost(() =>
{
- Assert.That(HandSys.TryDrop(SEntMan.GetEntity(Player), handsComp: Hands));
+ Assert.That(HandSys.TryDrop((ToServer(Player), Hands)));
});
await RunTicks(1);
- Assert.That(Hands.ActiveHandEntity, Is.Null);
+ Assert.That(HandSys.GetActiveItem((ToServer(Player), Hands)), Is.Null);
}
#region Interact
/// </summary>
protected async Task UseInHand()
{
- if (Hands.ActiveHandEntity is not { } target)
+ if (HandSys.GetActiveItem((ToServer(Player), Hands)) is not { } target)
{
Assert.Fail("Not holding any entity");
return;
#nullable enable
-using System.Linq;
using System.Numerics;
using Content.Client.Construction;
using Content.Client.Examine;
using Content.Client.Gameplay;
using Content.IntegrationTests.Pair;
-using Content.Server.Body.Systems;
using Content.Server.Hands.Systems;
using Content.Server.Stack;
using Content.Server.Tools;
-using Content.Shared.Body.Part;
using Content.Shared.DoAfter;
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
- type: entity
id: InteractionTestMob
components:
- - type: Body
- prototype: Aghost
- type: DoAfter
- type: Hands
+ hands:
+ hand_right: # only one hand, so that they do not accidentally pick up deconstruction products
+ location: Right
+ sortedHands:
+ - hand_right
- type: ComplexInteraction
- type: MindContainer
- type: Stripping
SEntMan.DeleteEntity(old.Value);
});
- // Ensure that the player only has one hand, so that they do not accidentally pick up deconstruction products
- await Server.WaitPost(() =>
- {
- // I lost an hour of my life trying to track down how the hell interaction tests were breaking
- // so greatz to this. Just make your own body prototype!
- var bodySystem = SEntMan.System<BodySystem>();
- var hands = bodySystem.GetBodyChildrenOfType(SEntMan.GetEntity(Player), BodyPartType.Hand).ToArray();
-
- for (var i = 1; i < hands.Length; i++)
- {
- SEntMan.DeleteEntity(hands[i].Id);
- }
- });
-
// Change UI state to in-game.
var state = Client.ResolveDependency<IStateManager>();
await Client.WaitPost(() => state.RequestStateChange<GameplayState>());
await SetTile(null);
await InteractUsing(Rod);
await AssertTile(Lattice);
- Assert.That(Hands.ActiveHandEntity, Is.Null);
+ Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
await InteractUsing(Cut);
await AssertTile(null);
await AssertEntityLookup((Rod, 1));
AssertGridCount(1);
// Cut lattice
- Assert.That(Hands.ActiveHandEntity, Is.Null);
+ Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
await InteractUsing(Cut);
await AssertTile(null);
AssertGridCount(0);
// Lattice -> Plating
await InteractUsing(FloorItem);
- Assert.That(Hands.ActiveHandEntity, Is.Null);
+ Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
await AssertTile(Plating);
AssertGridCount(1);
// Plating -> Tile
await InteractUsing(FloorItem);
- Assert.That(Hands.ActiveHandEntity, Is.Null);
+ Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
await AssertTile(Floor);
AssertGridCount(1);
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);
}
}
}
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);
}
}
}
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))
{
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
{
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;
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;
[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!;
[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;
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;
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;
{
[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!;
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;
}
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;
var valid = false;
- if (hands.ActiveHandEntity is not {Valid: true} holding)
+ if (_handsSystem.GetActiveItem((user, hands)) is not {Valid: true} holding)
{
Cleanup();
return;
using System.Numerics;
-using Content.Server.Inventory;
using Content.Server.Stack;
using Content.Server.Stunnable;
using Content.Shared.ActionBlocker;
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;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Timing;
-using Robust.Shared.Utility;
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);
}
}
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;
_ => 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)
{
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)
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;
// 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));
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
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,
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);
-using Content.Shared.Hands.Components;
+using Content.Server.Hands.Systems;
using Robust.Shared.Prototypes;
namespace Content.Server.NPC.HTN.Preconditions;
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)
-using Content.Shared.Hands.Components;
+using Content.Server.Hands.Systems;
namespace Content.Server.NPC.HTN.Preconditions;
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);
}
}
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;
}
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;
value = default;
EntityUid owner;
+ var handSys = entManager.System<HandsSystem>();
+
switch (key)
{
case Access:
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:
{
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);
{
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);
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;
[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!;
}
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;
}
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);
}
{
_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);
}
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);
}
foreach (var (hand, item) in component.ProvidedItems)
{
QueueDel(item);
- _hands.RemoveHand(chassis, hand, hands);
+ _hands.RemoveHand(chassis, hand);
}
component.ProvidedItems.Clear();
return;
RemComp<UnremoveableComponent>(item);
_container.Insert(item, component.ProvidedContainer);
}
- _hands.RemoveHand(chassis, handId, hands);
+ _hands.RemoveHand(chassis, handId);
}
component.ProvidedItems.Clear();
}
+using Content.Server.Hands.Systems;
using Content.Server.Popups;
using Content.Server.Tabletop.Components;
using Content.Shared.CCVar;
{
[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!;
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));
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);
}
}
}
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;
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!;
{
// 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;
}
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;
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;
}
{
_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;
}
// 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;
}
}
/// <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);
}
}
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;
{
[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!;
return;
}
- var activeHand = handsComponent.ActiveHand;
-
- if (activeHand == null)
- return;
-
- if (activeHand.HeldEntity == null)
+ if (!_hands.TryGetActiveItem((player, handsComponent), out var heldEntity))
return;
- var activeHandEntity = activeHand.HeldEntity.Value;
- if (!EntityManager.TryGetComponent(activeHandEntity, out ToolComponent? tool))
+ if (!EntityManager.TryGetComponent(heldEntity, 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)
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;
[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!;
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;
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)
{
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)
{
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)
}
// 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);
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;
}
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);
&& Resolve(user, ref user.Comp)
&& _handsSystem.IsHolding(user, item))
{
- if (!_handsSystem.CanDrop(user, item, user.Comp))
+ if (!_handsSystem.CanDrop(user, item))
return false;
}
/// </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;
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;
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);
}
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;
}
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);
}
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))
+++ /dev/null
-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);
-}
using Content.Shared.DisplacementMap;
using Content.Shared.Hands.EntitySystems;
-using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
/// <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>
/// <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>
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; }
-
- [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;
+ [DataField]
+ public HandLocation Location = HandLocation.Right;
- [ViewVariables]
- public EntityUid? HeldEntity => Container?.ContainedEntity;
+ public Hand()
+ {
- public bool IsEmpty => HeldEntity == null;
+ }
- public Hand(string name, HandLocation location, ContainerSlot? container = null)
+ 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;
}
}
// 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;
}
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;
}
using Robust.Shared.Containers;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
namespace Content.Shared.Hands.EntitySystems;
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))
/// <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))
/// <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;
}
/// <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 (!TryGetHeldItem(ent, handId, out var entity))
+ return;
- if (TerminatingOrDeleted(uid) || TerminatingOrDeleted(entity))
+ if (TerminatingOrDeleted(ent) || TerminatingOrDeleted(entity))
return;
- if (!ContainerSystem.Remove(entity, hand.Container))
+ 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));
}
}
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;
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)
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;
}
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>
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;
}
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)))
-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()
{
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>
/// </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>
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)
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)
_storage.PlayPickupAnimation(entity, initialPosition, xform.Coordinates, itemXform.LocalRotation, uid);
}
}
- DoPickup(uid, hand, entity, handsComp);
+ DoPickup(uid, handId, entity, handsComp);
return true;
}
/// 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>
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;
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);
/// <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))
{
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.
/// <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;
}
Dirty(uid, hands);
- if (hand == hands.ActiveHand)
- RaiseLocalEvent(entity, new HandSelectedEvent(uid), false);
+ if (hand == hands.ActiveHandId)
+ RaiseLocalEvent(entity, new HandSelectedEvent(uid));
}
}
{
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);
}
using Content.Shared.Storage.EntitySystems;
using Robust.Shared.Containers;
using Robust.Shared.Input.Binding;
+using Robust.Shared.Utility;
namespace Content.Shared.Hands.EntitySystems;
[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()
InitializeDrop();
InitializePickup();
InitializeRelay();
+
+ SubscribeLocalEvent<HandsComponent, ComponentInit>(OnInit);
+ SubscribeLocalEvent<HandsComponent, MapInitEvent>(OnMapInit);
}
public override void Shutdown()
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)
+ {
+ var container = EnsureComp<ContainerManagerComponent>(ent);
+ foreach (var id in ent.Comp.Hands.Keys)
+ {
+ ContainerSystem.EnsureContainer<ContainerSlot>(ent, id, container);
+ }
+ }
+
+ 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.ContainsKey(handName))
+ if (ent.Comp.Hands.ContainsKey(handName))
return;
- var container = ContainerSystem.EnsureContainer<ContainerSlot>(uid, handName);
+ var container = ContainerSystem.EnsureContainer<ContainerSlot>(ent, handName);
container.OccludesLight = false;
- var newHand = new Hand(handName, handLocation, container);
- handsComp.Hands.Add(handName, newHand);
- handsComp.SortedHands.Add(handName);
+ ent.Comp.Hands.Add(handName, hand);
+ ent.Comp.SortedHands.Add(handName);
+ Dirty(ent);
+
+ OnPlayerAddHand?.Invoke((ent, ent.Comp), handName, hand.Location);
- if (handsComp.ActiveHand == null)
- SetActiveHand(uid, newHand, handsComp);
+ if (ent.Comp.ActiveHandId == null)
+ SetActiveHand(ent, handName);
- RaiseLocalEvent(uid, new HandCountChangedEvent(uid));
- Dirty(uid, handsComp);
+ RaiseLocalEvent(ent, new HandCountChangedEvent(ent));
}
- public virtual void RemoveHand(EntityUid uid, string handName, HandsComponent? handsComp = null)
+ /// <summary>
+ /// Removes the specified hand from the specified entity
+ /// </summary>
+ public virtual void RemoveHand(Entity<HandsComponent?> ent, string handName)
{
- if (!Resolve(uid, ref handsComp, false))
+ if (!Resolve(ent, ref ent.Comp, false))
return;
- if (!handsComp.Hands.Remove(handName, out var hand))
+ OnPlayerRemoveHand?.Invoke((ent, ent.Comp), handName);
+
+ TryDrop(ent, handName, null, false);
+
+ if (!ent.Comp.Hands.Remove(handName))
return;
- handsComp.SortedHands.Remove(hand.Name);
- TryDrop(uid, hand, null, false, true, handsComp);
- if (hand.Container != null)
- ContainerSystem.ShutdownContainer(hand.Container);
+ if (ContainerSystem.TryGetContainer(ent, handName, out var container))
+ ContainerSystem.ShutdownContainer(container);
- if (handsComp.ActiveHand == hand)
- TrySetActiveHand(uid, handsComp.SortedHands.FirstOrDefault(), handsComp);
+ ent.Comp.SortedHands.Remove(handName);
+ if (ent.Comp.ActiveHandId == handName)
+ TrySetActiveHand(ent, ent.Comp.SortedHands.FirstOrDefault());
- RaiseLocalEvent(uid, new HandCountChangedEvent(uid));
- Dirty(uid, handsComp);
+ 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)
/// <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;
return false;
}
- public bool TryGetActiveHand(Entity<HandsComponent?> entity, [NotNullWhen(true)] out Hand? hand)
+ /// <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)
{
+ item = null;
if (!Resolve(entity, ref entity.Comp, false))
- {
- hand = null;
return false;
- }
- hand = entity.Comp.ActiveHand;
- return hand != null;
- }
-
- public bool TryGetActiveItem(Entity<HandsComponent?> entity, [NotNullWhen(true)] out EntityUid? item)
- {
- if (!TryGetActiveHand(entity, out var hand))
- {
- item = null;
+ if (!TryGetHeldItem(entity, entity.Comp.ActiveHandId, out var held))
return false;
- }
- item = hand.HeldEntity;
- return item != null;
+ item = held;
+ return true;
}
/// <summary>
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;
}
}
/// </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>
/// </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;
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;
+
+ 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;
- return hands.Hands.TryGetValue(handId, out hand);
+ 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++;
}
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;
[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!;
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:
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))
}
// 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;
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;
}
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
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
return;
}
- if (!_handsSystem.CanDropHeld(actor, hands.ActiveHand!, checkActionBlocker: false))
+ if (!_handsSystem.CanDropHeld(actor, hands.ActiveHandId!, checkActionBlocker: false))
return;
RaiseLocalEvent(held.Value, new HandDeselectedEvent(actor));
{
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;
}
}
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;
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;
// 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;
}
/// <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))
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)
/// </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);
}
}
}
using Content.Shared.Hands;
-using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Popups;
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();
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)
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;
// 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;
}
}
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;
{
[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!;
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);
}
}
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))
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;
[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!;
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))
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;
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)
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;
}
else
{
- SummonRetractableItem(args.Performer, ent.Comp.ActionItemUid.Value, userHand, ent.Owner);
+ SummonRetractableItem(args.Performer, ent.Comp.ActionItemUid.Value, activeHand, ent.Owner);
}
args.Handled = true;
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);
_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;
}
// 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);
{
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);
}
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,
_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))
{
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))
{
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;
if (held)
{
- if (!_sharedHandsSystem.IsHolding(player, itemUid, out _))
+ if (!_sharedHandsSystem.IsHolding(player.AsNullable(), itemUid, out _))
return false;
}
else
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);
}
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))
{
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>
if (!Resolve(user, ref user.Comp))
return false;
- if (user.Comp.ActiveHand == null)
- return false;
-
- if (user.Comp.ActiveHandEntity == null)
- return false;
-
- if (user.Comp.ActiveHandEntity != held)
+ if (!_handsSystem.TryGetActiveItem(user, out var activeItem) || activeItem != 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;
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");
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);
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 " : "";
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;
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");
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");
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;
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");
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");
{
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();
+ }
}
}
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;
}
}
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;
}
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;
[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!;
}
// 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;
}
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);
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;
[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!;
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;
}
return;
}
- SetReeling(hands.ActiveHandEntity.Value, grappling, msg.Reeling, player.Value);
+ SetReeling(activeItem.Value, grappling, msg.Reeling, player);
}
private void OnWeightlessMove(ref CanWeightlessMoveEvent ev)
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;
{
[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!;
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;
}
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;
[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!;
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;
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);
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
}
// 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);
/// <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);
+++ /dev/null
-- type: body
- id: Aghost
- name: "aghost"
- root: torso
- slots:
- torso:
- part: TorsoHuman
- connections:
- - right_arm
- - left_arm
- right_arm:
- part: RightArmHuman
- connections:
- - right_hand
- left_arm:
- part: LeftArmHuman
- connections:
- - left_hand
- right_hand:
- part: RightHandHuman
- left_hand:
- part: LeftHandHuman
canInteract: true
- type: GhostHearing
- type: Hands
+ hands:
+ hand_right:
+ location: Right
+ hand_left:
+ location: Left
+ sortedHands:
+ - hand_right
+ - hand_left
- type: ComplexInteraction
- type: Puller
needsHands: false
- type: Physics
ignorePaused: true
bodyType: Kinematic
- - type: Body
- prototype: Aghost
- type: Access
groups:
- AllAccess