This commit is contained in:
Leon Friedrich
2022-03-17 20:13:31 +13:00
committed by GitHub
parent 7b84362901
commit bfd95c493b
94 changed files with 1454 additions and 2185 deletions

View File

@@ -108,8 +108,8 @@ namespace Content.Client.EscapeMenu.UI.Tabs
AddHeader("ui-options-header-interaction-basic");
AddButton(EngineKeyFunctions.Use);
AddButton(ContentKeyFunctions.WideAttack);
AddButton(ContentKeyFunctions.ActivateItemInHand);
AddButton(ContentKeyFunctions.AltActivateItemInHand);
AddButton(ContentKeyFunctions.UseItemInHand);
AddButton(ContentKeyFunctions.AltUseItemInHand);
AddButton(ContentKeyFunctions.ActivateItemInWorld);
AddButton(ContentKeyFunctions.AltActivateItemInWorld);
AddButton(ContentKeyFunctions.Drop);

View File

@@ -4,17 +4,14 @@ using Content.Client.Animations;
using Content.Client.HUD;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Item;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
namespace Content.Client.Hands
@@ -25,11 +22,15 @@ namespace Content.Client.Hands
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IGameHud _gameHud = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SharedHandsComponent, EntRemovedFromContainerMessage>(HandleContainerModified);
SubscribeLocalEvent<SharedHandsComponent, EntInsertedIntoContainerMessage>(HandleContainerModified);
SubscribeLocalEvent<HandsComponent, PlayerAttachedEvent>(HandlePlayerAttached);
SubscribeLocalEvent<HandsComponent, PlayerDetachedEvent>(HandlePlayerDetached);
SubscribeLocalEvent<HandsComponent, ComponentRemove>(HandleCompRemove);
@@ -45,46 +46,29 @@ namespace Content.Client.Hands
if (args.Current is not HandsComponentState state)
return;
// Do we have a NEW hand?
var handsModified = component.Hands.Count != state.Hands.Count;
if (!handsModified)
var manager = EnsureComp<ContainerManagerComponent>(uid);
foreach (var hand in state.Hands)
{
for (var i = 0; i < state.Hands.Count; i++)
{
if (component.Hands[i].Name != state.Hands[i].Name ||
component.Hands[i].Location != state.Hands[i].Location)
if (component.Hands.TryAdd(hand.Name, hand))
{
hand.Container = _containerSystem.EnsureContainer<ContainerSlot>(uid, hand.Name, manager);
handsModified = true;
break;
}
}
}
if (handsModified)
{
// we have new hands, get the new containers.
component.Hands = state.Hands;
UpdateHandContainers(uid, component);
foreach (var name in component.Hands.Keys)
{
if (!state.HandNames.Contains(name))
component.Hands.Remove(name);
}
component.SortedHands = new(state.HandNames);
}
TrySetActiveHand(uid, state.ActiveHand, component);
}
/// <summary>
/// Used to update the hand-containers when hands have been added or removed. Also updates the GUI
/// </summary>
public void UpdateHandContainers(EntityUid uid, HandsComponent? hands = null, ContainerManagerComponent? containerMan = null)
{
if (!Resolve(uid, ref hands, ref containerMan))
return;
foreach (var hand in hands.Hands)
{
if (hand.Container == null)
{
hand.Container = hands.Owner.EnsureContainer<ContainerSlot>(hand.Name);
}
}
if (uid == _playerManager.LocalPlayer?.ControlledEntity)
UpdateGui();
@@ -117,9 +101,7 @@ namespace Content.Client.Hands
public EntityUid? GetActiveHandEntity()
{
return TryGetPlayerHands(out var hands) && hands.TryGetActiveHeldEntity(out var entity)
? entity
: null;
return TryGetPlayerHands(out var hands) ? hands.ActiveHandEntity : null;
}
/// <summary>
@@ -137,56 +119,57 @@ namespace Content.Client.Hands
/// </summary>
public void UIHandClick(HandsComponent hands, string handName)
{
if (!hands.TryGetHand(handName, out var pressedHand))
if (!hands.Hands.TryGetValue(handName, out var pressedHand))
return;
if (!hands.TryGetActiveHand(out var activeHand))
if (hands.ActiveHand == null)
return;
var pressedEntity = pressedHand.HeldEntity;
var activeEntity = activeHand.HeldEntity;
var activeEntity = hands.ActiveHand.HeldEntity;
if (pressedHand == activeHand && activeEntity != null)
if (pressedHand == hands.ActiveHand && activeEntity != null)
{
// use item in hand
// it will always be attack_self() in my heart.
RaiseNetworkEvent(new UseInHandMsg());
EntityManager.RaisePredictiveEvent(new RequestUseInHandEvent());
return;
}
if (pressedHand != activeHand && pressedEntity == null)
if (pressedHand != hands.ActiveHand && pressedEntity == null)
{
// change active hand
EntityManager.RaisePredictiveEvent(new RequestSetHandEvent(handName));
return;
}
if (pressedHand != activeHand && pressedEntity != null && activeEntity != null)
if (pressedHand != hands.ActiveHand && pressedEntity != null && activeEntity != null)
{
// use active item on held item
RaiseNetworkEvent(new ClientInteractUsingInHandMsg(pressedHand.Name));
EntityManager.RaisePredictiveEvent(new RequestHandInteractUsingEvent(pressedHand.Name));
return;
}
if (pressedHand != activeHand && pressedEntity != default && activeEntity == default)
if (pressedHand != hands.ActiveHand && pressedEntity != null && activeEntity == null)
{
// use active item on held item
RaiseNetworkEvent(new MoveItemFromHandMsg(pressedHand.Name));
// move the item to the active hand
EntityManager.RaisePredictiveEvent(new RequestMoveHandItemEvent(pressedHand.Name));
}
}
/// <summary>
/// Called when a user clicks on an item in their hands GUI.
/// Called when a user clicks on the little "activation" icon in the hands GUI. This is currently only used
/// by storage (backpacks, etc).
/// </summary>
public void UIHandActivate(string handName)
{
RaiseNetworkEvent(new ActivateInHandMsg(handName));
EntityManager.RaisePredictiveEvent(new RequestActivateInHandEvent(handName));
}
#region visuals
protected override void HandleContainerModified(EntityUid uid, SharedHandsComponent handComp, ContainerModifiedMessage args)
private void HandleContainerModified(EntityUid uid, SharedHandsComponent handComp, ContainerModifiedMessage args)
{
if (handComp.TryGetHand(args.Container.ID, out var hand))
if (handComp.Hands.TryGetValue(args.Container.ID, out var hand))
{
UpdateHandVisuals(uid, args.Entity, hand);
}
@@ -267,25 +250,24 @@ namespace Content.Client.Hands
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.TryGetHand(args.ContainerId, out var hand))
if (component.Hands.TryGetValue(args.ContainerId, out var hand))
{
UpdateHandVisuals(uid, args.Item, hand, component);
}
}
#endregion
#region Gui
public void UpdateGui(HandsComponent? hands = null)
{
if (hands == null && !TryGetPlayerHands(out hands) || hands.Gui == null)
return;
var states = hands.Hands
var states = hands.Hands.Values
.Select(hand => new GuiHand(hand.Name, hand.Location, hand.HeldEntity))
.ToArray();
hands.Gui.Update(new HandsGuiState(states, hands.ActiveHand));
hands.Gui.Update(new HandsGuiState(states, hands.ActiveHand?.Name));
}
public override bool TrySetActiveHand(EntityUid uid, string? value, SharedHandsComponent? handComp = null)

View File

@@ -32,8 +32,8 @@ namespace Content.Client.Input
var human = contexts.GetContext("human");
human.AddFunction(ContentKeyFunctions.SwapHands);
human.AddFunction(ContentKeyFunctions.Drop);
human.AddFunction(ContentKeyFunctions.ActivateItemInHand);
human.AddFunction(ContentKeyFunctions.AltActivateItemInHand);
human.AddFunction(ContentKeyFunctions.UseItemInHand);
human.AddFunction(ContentKeyFunctions.AltUseItemInHand);
human.AddFunction(ContentKeyFunctions.OpenCharacterMenu);
human.AddFunction(ContentKeyFunctions.ActivateItemInWorld);
human.AddFunction(ContentKeyFunctions.ThrowItemInHand);

View File

@@ -196,15 +196,15 @@ namespace Content.Client.Inventory
if (!Resolve(uid, ref hands, false))
return;
if (!hands.TryGetActiveHeldEntity(out var heldEntity))
if (hands.ActiveHandEntity is not EntityUid heldEntity)
return;
if(!TryGetSlotContainer(uid, slot, out var containerSlot, out var slotDef, inventoryComponent))
return;
_itemSlotManager.HoverInSlot(button, heldEntity.Value,
CanEquip(uid, heldEntity.Value, slot, out _, slotDef, inventoryComponent) &&
containerSlot.CanInsert(heldEntity.Value, EntityManager));
_itemSlotManager.HoverInSlot(button, heldEntity,
CanEquip(uid, heldEntity, slot, out _, slotDef, inventoryComponent) &&
containerSlot.CanInsert(heldEntity, EntityManager));
}
private void HandleSlotButtonPressed(EntityUid uid, string slot, ItemSlotButton button,
@@ -217,7 +217,7 @@ namespace Content.Client.Inventory
return;
// only raise event if either itemUid is not null, or the user is holding something
if (itemUid != null || TryComp(uid, out SharedHandsComponent? hands) && hands.TryGetActiveHeldEntity(out _))
if (itemUid != null || TryComp(uid, out SharedHandsComponent? hands) && hands.ActiveHandEntity != null)
EntityManager.RaisePredictiveEvent(new UseSlotNetworkMessage(slot));
}

View File

@@ -59,7 +59,7 @@ namespace Content.Client.Weapons.Ranged
return;
}
if (!hands.TryGetActiveHeldEntity(out var held) || !EntityManager.TryGetComponent(held, out ClientRangedWeaponComponent? weapon))
if (hands.ActiveHandEntity is not EntityUid held || !EntityManager.TryGetComponent(held, out ClientRangedWeaponComponent? weapon))
{
_blocked = true;
return;

View File

@@ -5,6 +5,7 @@ using Content.Shared.ActionBlocker;
using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Content.Shared.Buckle.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Item;
using Content.Shared.Standing;
using NUnit.Framework;
@@ -251,9 +252,7 @@ namespace Content.IntegrationTests.Tests.Buckle
{
var akms = entityManager.SpawnEntity(ItemDummyId, coordinates);
// Equip items
Assert.True(entityManager.TryGetComponent(akms, out SharedItemComponent item));
Assert.True(hands.PutInHand(item));
Assert.True(EntitySystem.Get<SharedHandsSystem>().TryPickupAnyHand(human, akms));
}
});
@@ -265,9 +264,9 @@ namespace Content.IntegrationTests.Tests.Buckle
Assert.True(buckle.Buckled);
// With items in all hands
foreach (var slot in hands.HandNames)
foreach (var hand in hands.Hands.Values)
{
Assert.NotNull(hands.GetItem(slot));
Assert.NotNull(hands.ActiveHandEntity);
}
var legs = body.GetPartsOfType(BodyPartType.Leg);
@@ -287,9 +286,9 @@ namespace Content.IntegrationTests.Tests.Buckle
Assert.True(buckle.Buckled);
// Now with no item in any hand
foreach (var slot in hands.HandNames)
foreach (var hand in hands.Hands.Values)
{
Assert.Null(hands.GetItem(slot));
Assert.Null(hands.ActiveHandEntity);
}
buckle.TryUnbuckle(human, true);

View File

@@ -81,7 +81,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
AddHand(cuffed.Owner);
Assert.That(cuffed.CuffedHandCount, Is.EqualTo(2));
Assert.That(hands.HandNames.Count(), Is.EqualTo(4));
Assert.That(hands.SortedHands.Count(), Is.EqualTo(4));
// Test to give a player with 4 hands 2 sets of cuffs
cuffed.TryAddNewCuffs(human, secondCuffs);

View File

@@ -4,6 +4,7 @@ using Content.Client.Items.Components;
using Content.Server.Hands.Components;
using Content.Server.Interaction;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Item;
using Content.Shared.Weapons.Melee;
@@ -54,6 +55,8 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
var sEntities = server.ResolveDependency<IEntityManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var sysMan = server.ResolveDependency<IEntitySystemManager>();
var handSys = sysMan.GetEntitySystem<SharedHandsSystem>();
var mapId = MapId.Nullspace;
var coords = MapCoordinates.Nullspace;
@@ -71,7 +74,8 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
server.Assert(() =>
{
user = sEntities.SpawnEntity(null, coords);
user.EnsureComponent<HandsComponent>().AddHand("hand", HandLocation.Left);
user.EnsureComponent<HandsComponent>();
handSys.AddHand(user, "hand", HandLocation.Left);
target = sEntities.SpawnEntity(null, coords);
item = sEntities.SpawnEntity(null, coords);
item.EnsureComponent<ItemComponent>();
@@ -98,8 +102,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
Assert.That(interactUsing, Is.False);
Assert.That(interactHand);
Assert.That(sEntities.TryGetComponent<HandsComponent>(user, out var hands));
Assert.That(hands.PutInHand(sEntities.GetComponent<SharedItemComponent>(item)));
Assert.That(handSys.TryPickup(user, item));
interactionSystem.UserInteraction(user, sEntities.GetComponent<TransformComponent>(target).Coordinates, target);
Assert.That(interactUsing);
@@ -124,6 +127,8 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
var sEntities = server.ResolveDependency<IEntityManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var sysMan = server.ResolveDependency<IEntitySystemManager>();
var handSys = sysMan.GetEntitySystem<SharedHandsSystem>();
var mapId = MapId.Nullspace;
var coords = MapCoordinates.Nullspace;
@@ -142,7 +147,8 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
server.Assert(() =>
{
user = sEntities.SpawnEntity(null, coords);
user.EnsureComponent<HandsComponent>().AddHand("hand", HandLocation.Left);
user.EnsureComponent<HandsComponent>();
handSys.AddHand(user, "hand", HandLocation.Left);
target = sEntities.SpawnEntity(null, new MapCoordinates((1.9f, 0), mapId));
item = sEntities.SpawnEntity(null, coords);
item.EnsureComponent<ItemComponent>();
@@ -170,8 +176,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
Assert.That(interactUsing, Is.False);
Assert.That(interactHand, Is.False);
Assert.That(sEntities.TryGetComponent<HandsComponent?>(user, out var hands));
Assert.That(hands.PutInHand(sEntities.GetComponent<SharedItemComponent>(item)));
Assert.That(handSys.TryPickup(user, item));
interactionSystem.UserInteraction(user, sEntities.GetComponent<TransformComponent>(target).Coordinates, target);
Assert.That(interactUsing, Is.False);
@@ -195,6 +200,8 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
var sEntities = server.ResolveDependency<IEntityManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var sysMan = server.ResolveDependency<IEntitySystemManager>();
var handSys = sysMan.GetEntitySystem<SharedHandsSystem>();
var mapId = MapId.Nullspace;
var coords = MapCoordinates.Nullspace;
@@ -212,7 +219,8 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
server.Assert(() =>
{
user = sEntities.SpawnEntity(null, coords);
user.EnsureComponent<HandsComponent>().AddHand("hand", HandLocation.Left);
user.EnsureComponent<HandsComponent>();
handSys.AddHand(user, "hand", HandLocation.Left);
target = sEntities.SpawnEntity(null, new MapCoordinates((InteractionSystem.InteractionRange - 0.1f, 0), mapId));
item = sEntities.SpawnEntity(null, coords);
item.EnsureComponent<ItemComponent>();
@@ -239,8 +247,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
Assert.That(interactUsing, Is.False);
Assert.That(interactHand);
Assert.That(sEntities.TryGetComponent<HandsComponent>(user, out var hands));
Assert.That(hands.PutInHand(sEntities.GetComponent<SharedItemComponent>(item)));
Assert.That(handSys.TryPickup(user, item));
interactionSystem.UserInteraction(user, sEntities.GetComponent<TransformComponent>(target).Coordinates, target);
Assert.That(interactUsing);
@@ -265,6 +272,8 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
var sEntities = server.ResolveDependency<IEntityManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var sysMan = server.ResolveDependency<IEntitySystemManager>();
var handSys = sysMan.GetEntitySystem<SharedHandsSystem>();
var mapId = MapId.Nullspace;
var coords = MapCoordinates.Nullspace;
@@ -282,7 +291,8 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
server.Assert(() =>
{
user = sEntities.SpawnEntity(null, coords);
user.EnsureComponent<HandsComponent>().AddHand("hand", HandLocation.Left);
user.EnsureComponent<HandsComponent>();
handSys.AddHand(user, "hand", HandLocation.Left);
target = sEntities.SpawnEntity(null, new MapCoordinates((SharedInteractionSystem.InteractionRange + 0.01f, 0), mapId));
item = sEntities.SpawnEntity(null, coords);
item.EnsureComponent<ItemComponent>();
@@ -309,8 +319,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
Assert.That(interactUsing, Is.False);
Assert.That(interactHand, Is.False);
Assert.That(sEntities.TryGetComponent<HandsComponent?>(user, out var hands));
Assert.That(hands.PutInHand(sEntities.GetComponent<SharedItemComponent>(item)));
Assert.That(handSys.TryPickup(user, item));
interactionSystem.UserInteraction(user, sEntities.GetComponent<TransformComponent>(target).Coordinates, target);
Assert.That(interactUsing, Is.False);
@@ -335,6 +344,8 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
var sEntities = server.ResolveDependency<IEntityManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var sysMan = server.ResolveDependency<IEntitySystemManager>();
var handSys = sysMan.GetEntitySystem<SharedHandsSystem>();
var mapId = MapId.Nullspace;
var coords = MapCoordinates.Nullspace;
@@ -354,7 +365,8 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
server.Assert(() =>
{
user = sEntities.SpawnEntity(null, coords);
user.EnsureComponent<HandsComponent>().AddHand("hand", HandLocation.Left);
user.EnsureComponent<HandsComponent>();
handSys.AddHand(user, "hand", HandLocation.Left);
target = sEntities.SpawnEntity(null, coords);
item = sEntities.SpawnEntity(null, coords);
item.EnsureComponent<ItemComponent>();
@@ -394,8 +406,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
Assert.That(interactUsing, Is.False);
Assert.That(interactHand);
Assert.That(sEntities.TryGetComponent<HandsComponent?>(user, out var hands));
Assert.That(hands.PutInHand(sEntities.GetComponent<SharedItemComponent>(item)));
Assert.That(handSys.TryPickup(user, item));
interactionSystem.UserInteraction(user, sEntities.GetComponent<TransformComponent>(target).Coordinates, target);
Assert.That(interactUsing, Is.False);

View File

@@ -1,133 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using Content.Server.Hands.Components;
using Content.Server.PDA;
using Content.Shared.Access.Components;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Inventory;
using Content.Shared.Item;
using Content.Shared.PDA;
using NUnit.Framework;
using Robust.Server.Player;
using Robust.Shared.GameObjects;
namespace Content.IntegrationTests.Tests.PDA
{
public sealed class PDAExtensionsTests : ContentIntegrationTest
{
private const string IdCardDummy = "DummyIdCard";
private const string PdaDummy = "DummyPda";
private static readonly string Prototypes = $@"
- type: entity
id: {IdCardDummy}
name: {IdCardDummy}
components:
- type: IdCard
- type: Item
- type: entity
id: {PdaDummy}
name: {PdaDummy}
components:
- type: PDA
idSlot:
name: ID Card
whitelist:
components:
- IdCard
- type: Item";
[Test]
public async Task PlayerGetIdComponent()
{
var clientOptions = new ClientIntegrationOptions
{
ExtraPrototypes = Prototypes
};
var serverOptions = new ServerIntegrationOptions
{
ExtraPrototypes = Prototypes
};
var (client, server) = await StartConnectedServerClientPair(clientOptions, serverOptions);
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
var sPlayerManager = server.ResolveDependency<IPlayerManager>();
var sEntityManager = server.ResolveDependency<IEntityManager>();
var invSystem = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<InventorySystem>();
await server.WaitAssertion(() =>
{
var player = sPlayerManager.Sessions.Single().AttachedEntity.GetValueOrDefault();
Assert.That(player != default);
// The player spawns with an ID on by default
Assert.NotNull(player.GetHeldId());
Assert.True(player.TryGetHeldId(out var id));
Assert.NotNull(id);
// Put PDA in hand
var dummyPda = sEntityManager.SpawnEntity(PdaDummy, sEntityManager.GetComponent<TransformComponent>(player).MapPosition);
var pdaItemComponent = sEntityManager.GetComponent<SharedItemComponent>(dummyPda);
sEntityManager.GetComponent<HandsComponent>(player).PutInHand(pdaItemComponent);
var pdaComponent = sEntityManager.GetComponent<PDAComponent>(dummyPda);
var pdaIdCard = sEntityManager.SpawnEntity(IdCardDummy, sEntityManager.GetComponent<TransformComponent>(player).MapPosition);
var itemSlots = sEntityManager.GetComponent<ItemSlotsComponent>(dummyPda);
sEntityManager.EntitySysManager.GetEntitySystem<ItemSlotsSystem>()
.TryInsert(dummyPda, pdaComponent.IdSlot, pdaIdCard, null);
var pdaContainedId = pdaComponent.ContainedID;
// The PDA in the hand should be found first
Assert.NotNull(player.GetHeldId());
Assert.True(player.TryGetHeldId(out id));
Assert.NotNull(id);
Assert.That(id, Is.EqualTo(pdaContainedId));
// Put ID card in hand
var idDummy = sEntityManager.SpawnEntity(IdCardDummy, sEntityManager.GetComponent<TransformComponent>(player).MapPosition);
var idItemComponent = sEntityManager.GetComponent<SharedItemComponent>(idDummy);
sEntityManager.GetComponent<HandsComponent>(player).PutInHand(idItemComponent);
var idCardComponent = sEntityManager.GetComponent<IdCardComponent>(idDummy);
// The ID in the hand should be found first
Assert.NotNull(player.GetHeldId());
Assert.True(player.TryGetHeldId(out id));
Assert.NotNull(id);
Assert.That(id, Is.EqualTo(idCardComponent));
// Remove all IDs and PDAs
Assert.That(invSystem.TryGetSlots(player, out var slots));
foreach (var slot in slots)
{
if(!invSystem.TryGetSlotEntity(player, slot.Name, out var item))
continue;
if (sEntityManager.HasComponent<PDAComponent>(item))
{
invSystem.TryUnequip(player, slot.Name, force: true);
}
}
var hands = sEntityManager.GetComponent<HandsComponent>(player);
hands.Drop(dummyPda, false);
hands.Drop(idDummy, false);
// No ID
Assert.Null(player.GetHeldId());
Assert.False(player.TryGetHeldId(out id));
Assert.Null(id);
});
}
}
}

View File

@@ -66,12 +66,12 @@ namespace Content.Server.AI.Operators.Combat.Melee
return Outcome.Success;
}
if (!_entMan.TryGetComponent(_owner, out HandsComponent? hands) || hands.GetActiveHandItem == null)
if (!_entMan.TryGetComponent(_owner, out HandsComponent? hands) || hands.ActiveHandEntity == null)
{
return Outcome.Failed;
}
var meleeWeapon = hands.GetActiveHandItem.Owner;
var meleeWeapon = hands.ActiveHandEntity;
_entMan.TryGetComponent(meleeWeapon, out MeleeWeaponComponent? meleeWeaponComponent);
if ((_entMan.GetComponent<TransformComponent>(_target).Coordinates.Position - _entMan.GetComponent<TransformComponent>(_owner).Coordinates.Position).Length >

View File

@@ -1,6 +1,5 @@
using Content.Server.Hands.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Content.Shared.Hands.EntitySystems;
namespace Content.Server.AI.Operators.Inventory
{
@@ -21,12 +20,7 @@ namespace Content.Server.AI.Operators.Inventory
/// <returns></returns>
public override Outcome Execute(float frameTime)
{
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent(_owner, out HandsComponent? handsComponent))
{
return Outcome.Failed;
}
return handsComponent.Drop(_entity) ? Outcome.Success : Outcome.Failed;
return IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedHandsSystem>().TryDrop(_owner, _entity) ? Outcome.Success : Outcome.Failed;
}
}
}

View File

@@ -1,6 +1,5 @@
using Content.Server.Hands.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Content.Shared.Hands.EntitySystems;
namespace Content.Server.AI.Operators.Inventory
{
@@ -20,9 +19,12 @@ namespace Content.Server.AI.Operators.Inventory
return Outcome.Failed;
}
foreach (var item in handsComponent.GetAllHeldItems())
var sys = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedHandsSystem>();
foreach (var hand in handsComponent.Hands.Values)
{
handsComponent.Drop(item.Owner);
if (!hand.IsEmpty)
sys.TryDrop(_owner, hand, handsComp: handsComponent);
}
return Outcome.Success;

View File

@@ -1,4 +1,5 @@
using Content.Server.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -16,19 +17,12 @@ namespace Content.Server.AI.Operators.Inventory
public override Outcome Execute(float frameTime)
{
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent(_owner, out HandsComponent? handsComponent))
{
return Outcome.Failed;
}
var sys = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedHandsSystem>();
// TODO: If in clothing then click on it
foreach (var hand in handsComponent.ActivePriorityEnumerable())
{
if (handsComponent.GetItem(hand)?.Owner == _entity)
{
handsComponent.ActiveHand = hand;
if (sys.TrySelect(_owner, _entity))
return Outcome.Success;
}
}
// TODO: Get free hand count; if no hands free then fail right here

View File

@@ -1,5 +1,6 @@
using Content.Server.Hands.Components;
using Content.Server.Interaction;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
using Content.Shared.Item;
@@ -24,42 +25,22 @@ namespace Content.Server.AI.Operators.Inventory
public override Outcome Execute(float frameTime)
{
var entMan = IoCManager.Resolve<IEntityManager>();
var sysMan = IoCManager.Resolve<IEntitySystemManager>();
var interactionSystem = sysMan.GetEntitySystem<InteractionSystem>();
var handsSys = sysMan.GetEntitySystem<SharedHandsSystem>();
if (entMan.Deleted(_target)
|| !entMan.HasComponent<SharedItemComponent>(_target)
|| _target.IsInContainer()
|| !EntitySystem.Get<SharedInteractionSystem>().InRangeUnobstructed(_owner, _target, popup: true))
|| !interactionSystem.InRangeUnobstructed(_owner, _target, popup: true))
{
return Outcome.Failed;
}
if (!entMan.TryGetComponent(_owner, out HandsComponent? handsComponent))
{
// select empty hand
if (!handsSys.TrySelectEmptyHand(_owner))
return Outcome.Failed;
}
var emptyHands = false;
foreach (var hand in handsComponent.ActivePriorityEnumerable())
{
if (handsComponent.GetItem(hand) == null)
{
if (handsComponent.ActiveHand != hand)
{
handsComponent.ActiveHand = hand;
}
emptyHands = true;
break;
}
}
if (!emptyHands)
{
return Outcome.Failed;
}
var interactionSystem = EntitySystem.Get<InteractionSystem>();
interactionSystem.InteractHand(_owner, _target);
return Outcome.Success;
}

View File

@@ -1,4 +1,5 @@
using Content.Server.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Item;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -22,27 +23,18 @@ namespace Content.Server.AI.Operators.Inventory
public override Outcome Execute(float frameTime)
{
var entMan = IoCManager.Resolve<IEntityManager>();
var sysMan = IoCManager.Resolve<IEntitySystemManager>();
var sys = sysMan.GetEntitySystem<SharedHandsSystem>();
// TODO: Also have this check storage a la backpack etc.
if (!entMan.TryGetComponent(_owner, out HandsComponent? handsComponent))
if (!entMan.TryGetComponent(_owner, out HandsComponent? handsComponent)
|| !sys.TrySelect(_owner, _target, handsComponent)
|| !sys.TryUseItemInHand(_owner, false, handsComponent))
{
return Outcome.Failed;
}
if (!entMan.TryGetComponent(_target, out SharedItemComponent? itemComponent))
{
return Outcome.Failed;
}
foreach (var slot in handsComponent.ActivePriorityEnumerable())
{
if (handsComponent.GetItem(slot) != itemComponent) continue;
handsComponent.ActiveHand = slot;
handsComponent.ActivateItem();
return Outcome.Success;
}
return Outcome.Failed;
}
}
}

View File

@@ -1,10 +1,8 @@
using Content.Server.Hands.Components;
using Content.Server.Nutrition.Components;
using Content.Server.Nutrition.EntitySystems;
using Content.Shared.Item;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Nutrition.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Random;
namespace Content.Server.AI.Operators.Nutrition
@@ -30,35 +28,23 @@ namespace Content.Server.AI.Operators.Nutrition
}
var entities = IoCManager.Resolve<IEntityManager>();
var sysMan = IoCManager.Resolve<IEntitySystemManager>();
var handsSys = sysMan.GetEntitySystem<SharedHandsSystem>();
// TODO: Also have this check storage a la backpack etc.
if (entities.Deleted(_target) ||
!entities.TryGetComponent(_owner, out HandsComponent? handsComponent) ||
!entities.TryGetComponent(_target, out SharedItemComponent? itemComponent))
!entities.TryGetComponent(_owner, out HandsComponent? handsComponent))
{
return Outcome.Failed;
}
DrinkComponent? drinkComponent = null;
foreach (var slot in handsComponent.ActivePriorityEnumerable())
{
if (handsComponent.GetItem(slot) != itemComponent) continue;
handsComponent.ActiveHand = slot;
if (!entities.TryGetComponent(_target, out drinkComponent))
{
if (!handsSys.TrySelect<DrinkComponent>(_owner, out var drinkComponent, handsComponent))
return Outcome.Failed;
if (!handsSys.TryUseItemInHand(_owner, false, handsComponent))
return Outcome.Failed;
}
// This should also implicitly open it.
handsComponent.ActivateItem();
_interactionCooldown = IoCManager.Resolve<IRobustRandom>().NextFloat() + 0.5f;
}
if (drinkComponent == null)
{
return Outcome.Failed;
}
if (drinkComponent.Deleted || EntitySystem.Get<DrinkSystem>().IsEmpty(drinkComponent.Owner, drinkComponent)
|| entities.TryGetComponent(_owner, out ThirstComponent? thirstComponent) &&
@@ -67,6 +53,7 @@ namespace Content.Server.AI.Operators.Nutrition
return Outcome.Success;
}
/// uuhhh do afters for drinks might mess this up?
return Outcome.Continuing;
}
}

View File

@@ -1,5 +1,6 @@
using Content.Server.Hands.Components;
using Content.Server.Nutrition.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Item;
using Content.Shared.Nutrition.Components;
using Robust.Shared.GameObjects;
@@ -29,35 +30,21 @@ namespace Content.Server.AI.Operators.Nutrition
}
var entities = IoCManager.Resolve<IEntityManager>();
var sysMan = IoCManager.Resolve<IEntitySystemManager>();
var handsSys = sysMan.GetEntitySystem<SharedHandsSystem>();
// TODO: Also have this check storage a la backpack etc.
if (entities.Deleted(_target) ||
!entities.TryGetComponent(_owner, out HandsComponent? handsComponent) ||
!entities.TryGetComponent(_target, out SharedItemComponent? itemComponent))
!entities.TryGetComponent(_owner, out HandsComponent? handsComponent))
{
return Outcome.Failed;
}
FoodComponent? foodComponent = null;
foreach (var slot in handsComponent.ActivePriorityEnumerable())
{
if (handsComponent.GetItem(slot) != itemComponent) continue;
handsComponent.ActiveHand = slot;
if (!entities.TryGetComponent(_target, out foodComponent))
{
if (!handsSys.TrySelect<FoodComponent>(_owner, out var foodComponent, handsComponent))
return Outcome.Failed;
}
// This should also implicitly open it.
handsComponent.ActivateItem();
_interactionCooldown = IoCManager.Resolve<IRobustRandom>().NextFloat() + 0.5f;
}
if (foodComponent == null)
{
if (!handsSys.TryUseItemInHand(_owner, false, handsComponent))
return Outcome.Failed;
}
if ((!entities.EntityExists(_target) ? EntityLifeStage.Deleted : entities.GetComponent<MetaDataComponent>(_target).EntityLifeStage) >= EntityLifeStage.Deleted ||
foodComponent.UsesRemaining == 0 ||
@@ -67,6 +54,7 @@ namespace Content.Server.AI.Operators.Nutrition
return Outcome.Success;
}
/// do afters for food might mess this up?
return Outcome.Continuing;
}
}

View File

@@ -1,8 +1,7 @@
using Content.Server.AI.WorldState;
using Content.Server.AI.WorldState.States;
using Content.Server.Hands.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Content.Shared.Hands.Components;
using System.Linq;
namespace Content.Server.AI.Utility.Considerations.Hands
{
@@ -12,24 +11,12 @@ namespace Content.Server.AI.Utility.Considerations.Hands
{
var owner = context.GetState<SelfState>().GetValue();
if (!owner.IsValid() || !IoCManager.Resolve<IEntityManager>().TryGetComponent(owner, out HandsComponent? handsComponent))
if (!owner.IsValid() || !IoCManager.Resolve<IEntityManager>().TryGetComponent(owner, out SharedHandsComponent? handsComponent))
{
return 0.0f;
}
var handCount = 0;
var freeCount = 0;
foreach (var hand in handsComponent.ActivePriorityEnumerable())
{
handCount++;
if (handsComponent.GetItem(hand) == null)
{
freeCount += 1;
}
}
return (float) freeCount / handCount;
return (float) handsComponent.CountFreeHands() / handsComponent.Count;
}
}
}

View File

@@ -1,4 +1,5 @@
using Content.Server.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -11,20 +12,7 @@ namespace Content.Server.AI.WorldState.States.Hands
public override string Name => "AnyFreeHand";
public override bool GetValue()
{
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent(Owner, out HandsComponent? handsComponent))
{
return false;
}
foreach (var hand in handsComponent.ActivePriorityEnumerable())
{
if (handsComponent.GetItem(hand) == null)
{
return true;
}
}
return false;
return IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedHandsSystem>().TryGetEmptyHand(Owner, out _);
}
}
}

View File

@@ -1,8 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using Content.Server.Hands.Components;
using Content.Shared.Hands.Components;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.AI.WorldState.States.Hands
{
@@ -17,18 +16,10 @@ namespace Content.Server.AI.WorldState.States.Hands
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent(Owner, out HandsComponent? handsComponent))
{
return result;
return new List<string>();
}
foreach (var hand in handsComponent.ActivePriorityEnumerable())
{
if (handsComponent.GetItem(hand) == null)
{
result.Add(hand);
}
}
return result;
return handsComponent.GetFreeHandNames().ToList();
}
}
}

View File

@@ -1,8 +1,6 @@
using System.Collections.Generic;
using Content.Server.Hands.Components;
using System.Linq;
using Content.Shared.Hands.EntitySystems;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.AI.WorldState.States.Hands
{
@@ -12,23 +10,7 @@ namespace Content.Server.AI.WorldState.States.Hands
public override string Name => "HandItems";
public override List<EntityUid> GetValue()
{
var result = new List<EntityUid>();
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent(Owner, out HandsComponent? handsComponent))
{
return result;
}
foreach (var hand in handsComponent.ActivePriorityEnumerable())
{
var item = handsComponent.GetItem(hand);
if (item != null)
{
result.Add(item.Owner);
}
}
return result;
return IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedHandsSystem>().EnumerateHeld(Owner).ToList();
}
}
}

View File

@@ -15,7 +15,7 @@ namespace Content.Server.AI.WorldState.States.Inventory
public override EntityUid? GetValue()
{
return IoCManager.Resolve<IEntityManager>().GetComponentOrNull<HandsComponent>(Owner)?.GetActiveHandItem?.Owner;
return IoCManager.Resolve<IEntityManager>().GetComponentOrNull<HandsComponent>(Owner)?.ActiveHandEntity;
}
}
}

View File

@@ -1,8 +1,5 @@
using System.Collections.Generic;
using Content.Server.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.AI.WorldState.States.Inventory
{
@@ -13,17 +10,7 @@ namespace Content.Server.AI.WorldState.States.Inventory
public override IEnumerable<EntityUid> GetValue()
{
var entMan = IoCManager.Resolve<IEntityManager>();
if (entMan.TryGetComponent(Owner, out HandsComponent? handsComponent))
{
foreach (var item in handsComponent.GetAllHeldItems())
{
if (entMan.Deleted(item.Owner))
continue;
yield return item.Owner;
}
}
return IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedHandsSystem>().EnumerateHeld(Owner);
}
}
}

View File

@@ -7,6 +7,7 @@ using Content.Server.Power.Components;
using Content.Server.UserInterface;
using Content.Shared.ActionBlocker;
using Content.Shared.AME;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Item;
using Content.Shared.Popups;
@@ -23,6 +24,7 @@ namespace Content.Server.AME.Components
public sealed class AMEControllerComponent : SharedAMEControllerComponent, IInteractUsing
{
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(AMEControllerUiKey.Key);
private bool _injecting;
@@ -210,10 +212,7 @@ namespace Content.Server.AME.Components
_jarSlot.Remove(jar);
UpdateUserInterface();
if (!_entities.TryGetComponent<HandsComponent?>(user, out var hands) || !_entities.TryGetComponent<SharedItemComponent?>(jar, out var item))
return;
if (hands.CanPutInHand(item))
hands.PutInHand(item);
_sysMan.GetEntitySystem<SharedHandsSystem>().PickupOrDrop(user, jar);
}
private void ToggleInjection()
@@ -306,13 +305,13 @@ namespace Content.Server.AME.Components
return true;
}
if (hands.GetActiveHandItem == null)
if (hands.ActiveHandEntity == null)
{
Owner.PopupMessage(args.User, Loc.GetString("ame-controller-component-interact-using-nothing-in-hands-text"));
return false;
}
var activeHandEntity = hands.GetActiveHandItem.Owner;
var activeHandEntity = hands.ActiveHandEntity;
if (_entities.HasComponent<AMEFuelContainerComponent?>(activeHandEntity))
{
if (HasJar)
@@ -322,7 +321,7 @@ namespace Content.Server.AME.Components
else
{
_jarSlot.Insert(activeHandEntity);
_jarSlot.Insert(activeHandEntity.Value);
Owner.PopupMessage(args.User, Loc.GetString("ame-controller-component-interact-using-success"));
UpdateUserInterface();
}

View File

@@ -140,8 +140,8 @@ namespace Content.Server.Access.Systems
{
// check held item?
if (EntityManager.TryGetComponent(uid, out SharedHandsComponent? hands) &&
hands.TryGetActiveHeldEntity(out var heldItem) &&
TryGetIdCard(heldItem.Value, out idCard))
hands.ActiveHandEntity is EntityUid heldItem &&
TryGetIdCard(heldItem, out idCard))
{
return true;
}

View File

@@ -161,7 +161,7 @@ namespace Content.Server.Atmos.Components
if (!_entities.TryGetComponent(playerEntity, out HandsComponent? handsComponent))
return;
if (handsComponent?.GetActiveHandItem?.Owner is not {Valid: true} activeHandEntity ||
if (handsComponent?.ActiveHandEntity is not {Valid: true} activeHandEntity ||
!_entities.TryGetComponent(activeHandEntity, out GasAnalyzerComponent? gasAnalyzer))
{
return;
@@ -228,7 +228,7 @@ namespace Content.Server.Atmos.Components
return;
}
if (handsComponent.GetActiveHandItem?.Owner is not {Valid: true} activeHandEntity ||
if (handsComponent.ActiveHandEntity is not {Valid: true} activeHandEntity ||
!_entities.TryGetComponent(activeHandEntity, out GasAnalyzerComponent? gasAnalyzer))
{
serverMsg.Session.AttachedEntity.Value.PopupMessage(Loc.GetString("gas-analyzer-component-need-gas-analyzer-in-hand-message"));

View File

@@ -1,4 +1,3 @@
using System;
using Content.Server.Administration.Logs;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
@@ -8,19 +7,14 @@ using Content.Server.Hands.Components;
using Content.Server.NodeContainer;
using Content.Server.NodeContainer.NodeGroups;
using Content.Server.NodeContainer.Nodes;
using Content.Shared.ActionBlocker;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Binary.Components;
using Content.Shared.Database;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Players;
namespace Content.Server.Atmos.Piping.Unary.EntitySystems
{
@@ -30,6 +24,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly AdminLogSystem _adminLogSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
public override void Initialize()
{
@@ -248,11 +243,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
if (!EntityManager.TryGetComponent(args.Used, out GasTankComponent? _))
return;
// Check the user has hands.
if (!EntityManager.TryGetComponent(args.User, out HandsComponent? hands))
return;
if (!hands.Drop(args.Used, container))
if (!_handsSystem.TryDropIntoContainer(args.User, args.Used, container))
return;
_adminLogSystem.Add(LogType.CanisterTankInserted, LogImpact.Medium, $"Player {ToPrettyString(args.User):player} inserted tank {ToPrettyString(container.ContainedEntities[0]):tank} into {ToPrettyString(canister):canister}");

View File

@@ -432,7 +432,7 @@ namespace Content.Server.Botany.Components
{
if (_entMan.TryGetComponent(user, out HandsComponent? hands))
{
if (!botanySystem.CanHarvest(Seed, hands.GetActiveHandItem?.Owner))
if (!botanySystem.CanHarvest(Seed, hands.ActiveHandEntity))
return false;
}
else if (!botanySystem.CanHarvest(Seed))

View File

@@ -89,9 +89,9 @@ namespace Content.Server.Chat.Commands
// Held item suicide
if (_entities.TryGetComponent(owner, out HandsComponent handsComponent)
&& handsComponent.GetActiveHandItem is {} itemComponent)
&& handsComponent.ActiveHandEntity is EntityUid item)
{
var suicide = _entities.GetComponents<ISuicideAct>(itemComponent.Owner).FirstOrDefault();
var suicide = _entities.GetComponents<ISuicideAct>(item).FirstOrDefault();
if (suicide != null)
{

View File

@@ -1,14 +1,12 @@
using Content.Server.Chemistry.EntitySystems;
using Content.Server.Hands.Components;
using Content.Server.Labels.Components;
using Content.Server.Power.Components;
using Content.Server.UserInterface;
using Content.Shared.Chemistry.Components;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.FixedPoint;
using Content.Shared.Item;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Popups;
using Content.Shared.Random.Helpers;
using Content.Shared.Sound;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
@@ -28,6 +26,7 @@ namespace Content.Server.Chemistry.Components
public sealed class ChemMasterComponent : SharedChemMasterComponent
{
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
[ViewVariables]
private uint _pillType = 1;
@@ -276,6 +275,9 @@ namespace Content.Server.Chemistry.Components
return;
}
var handSys = _sysMan.GetEntitySystem<SharedHandsSystem>();
var solSys = _sysMan.GetEntitySystem<SolutionContainerSystem>();
if (action == UiAction.CreateBottles)
{
var individualVolume = BufferSolution.TotalVolume / FixedPoint2.New(bottleAmount);
@@ -298,25 +300,12 @@ namespace Content.Server.Chemistry.Components
labelComponent.CurrentLabel = label;
var bufferSolution = BufferSolution.SplitSolution(actualVolume);
var bottleSolution = EntitySystem.Get<SolutionContainerSystem>().EnsureSolution(bottle, "drink");
var bottleSolution = solSys.EnsureSolution(bottle, "drink");
EntitySystem.Get<SolutionContainerSystem>().TryAddSolution(bottle, bottleSolution, bufferSolution);
solSys.TryAddSolution(bottle, bottleSolution, bufferSolution);
//Try to give them the bottle
if (_entities.TryGetComponent<HandsComponent?>(user, out var hands) &&
_entities.TryGetComponent<SharedItemComponent?>(bottle, out var item))
{
if (hands.CanPutInHand(item))
{
hands.PutInHand(item);
continue;
}
}
//Put it on the floor
_entities.GetComponent<TransformComponent>(bottle).Coordinates = _entities.GetComponent<TransformComponent>(user).Coordinates;
//Give it an offset
bottle.RandomOffset(0.2f);
handSys.PickupOrDrop(user, bottle);
}
}
else //Pills
@@ -342,7 +331,7 @@ namespace Content.Server.Chemistry.Components
var bufferSolution = BufferSolution.SplitSolution(actualVolume);
var pillSolution = EntitySystem.Get<SolutionContainerSystem>().EnsureSolution(pill, "food");
EntitySystem.Get<SolutionContainerSystem>().TryAddSolution(pill, pillSolution, bufferSolution);
solSys.TryAddSolution(pill, pillSolution, bufferSolution);
//Change pill Sprite component state
if (!_entities.TryGetComponent(pill, out SpriteComponent? sprite))
@@ -352,20 +341,7 @@ namespace Content.Server.Chemistry.Components
sprite?.LayerSetState(0, "pill" + _pillType);
//Try to give them the bottle
if (_entities.TryGetComponent<HandsComponent?>(user, out var hands) &&
_entities.TryGetComponent<SharedItemComponent?>(pill, out var item))
{
if (hands.CanPutInHand(item))
{
hands.PutInHand(item);
continue;
}
}
//Put it on the floor
_entities.GetComponent<TransformComponent>(pill).Coordinates = _entities.GetComponent<TransformComponent>(user).Coordinates;
//Give it an offset
pill.RandomOffset(0.2f);
handSys.PickupOrDrop(user, pill);
}
}

View File

@@ -1,17 +1,13 @@
using System;
using System.Globalization;
using System.Threading;
using Content.Server.Access.Systems;
using Content.Server.Chat.Managers;
using Content.Server.PDA;
using Content.Server.Power.Components;
using Content.Server.RoundEnd;
using Content.Server.UserInterface;
using Content.Shared.Communications;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Timing;
using Robust.Shared.ViewVariables;
using Timer = Robust.Shared.Timing.Timer;
namespace Content.Server.Communications
@@ -23,6 +19,7 @@ namespace Content.Server.Communications
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
private bool Powered => !_entities.TryGetComponent(Owner, out ApcPowerReceiverComponent? receiver) || receiver.Powered;
@@ -101,9 +98,10 @@ namespace Content.Server.Communications
UpdateBoundInterface();
var message = msg.Message.Length <= 256 ? msg.Message.Trim() : $"{msg.Message.Trim().Substring(0, 256)}...";
var sys = _sysMan.GetEntitySystem<IdCardSystem>();
var author = "Unknown";
if (obj.Session.AttachedEntity is {Valid: true} mob && mob.TryGetHeldId(out var id))
if (obj.Session.AttachedEntity is {Valid: true} mob && sys.TryFindIdCard(mob, out var id))
{
author = $"{id.FullName} ({CultureInfo.CurrentCulture.TextInfo.ToTitleCase(id.JobTitle ?? string.Empty)})".Trim();
}

View File

@@ -10,6 +10,7 @@ using Content.Shared.Construction;
using Content.Shared.Construction.Prototypes;
using Content.Shared.Construction.Steps;
using Content.Shared.Coordinates;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Inventory;
using Content.Shared.Popups;
@@ -25,6 +26,7 @@ namespace Content.Server.Construction
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
// --- WARNING! LEGACY CODE AHEAD! ---
// This entire file contains the legacy code for initial construction.
@@ -43,11 +45,9 @@ namespace Content.Server.Construction
// LEGACY CODE. See warning at the top of the file!
private IEnumerable<EntityUid> EnumerateNearby(EntityUid user)
{
if (EntityManager.TryGetComponent(user, out HandsComponent? hands))
foreach (var item in _handsSystem.EnumerateHeld(user))
{
foreach (var itemComponent in hands?.GetAllHeldItems()!)
{
if (EntityManager.TryGetComponent(itemComponent.Owner, out ServerStorageComponent? storage))
if (TryComp(item, out ServerStorageComponent? storage))
{
foreach (var storedEntity in storage.StoredEntities!)
{
@@ -55,8 +55,7 @@ namespace Content.Server.Construction
}
}
yield return itemComponent.Owner;
}
yield return item;
}
if (_inventorySystem.TryGetContainerSlotEnumerator(user, out var containerSlotEnumerator))
@@ -334,7 +333,7 @@ namespace Content.Server.Construction
}
if (await Construct(user, "item_construction", constructionGraph, edge, targetNode) is {Valid: true} item)
hands.PutInHandOrDrop(item);
_handsSystem.PickupOrDrop(user, item);
}
// LEGACY CODE. See warning at the top of the file!
@@ -401,7 +400,7 @@ namespace Content.Server.Construction
}
if (!_actionBlocker.CanInteract(user, null)
|| !EntityManager.TryGetComponent(user, out HandsComponent? hands) || hands.GetActiveHandItem == null)
|| !EntityManager.TryGetComponent(user, out HandsComponent? hands) || hands.ActiveHandEntity == null)
{
Cleanup();
return;
@@ -426,7 +425,7 @@ namespace Content.Server.Construction
var valid = false;
if (hands.GetActiveHandItem?.Owner is not {Valid: true} holding)
if (hands.ActiveHandEntity is not {Valid: true} holding)
{
Cleanup();
return;

View File

@@ -5,6 +5,7 @@ using Content.Server.DoAfter;
using Content.Server.Hands.Components;
using Content.Shared.Alert;
using Content.Shared.Cuffs.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
using Content.Shared.Popups;
@@ -26,6 +27,7 @@ namespace Content.Server.Cuffs.Components
public sealed class CuffableComponent : SharedCuffableComponent
{
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
/// <summary>
/// How many of this entity's hands are currently cuffed.
@@ -103,15 +105,13 @@ namespace Content.Server.Cuffs.Components
return true;
}
var sys = _sysMan.GetEntitySystem<SharedHandsSystem>();
// Success!
if (_entMan.TryGetComponent(user, out HandsComponent? handsComponent) && handsComponent.IsHolding(handcuff))
{
// Good lord handscomponent is scuffed, I hope some smug person will fix it someday
handsComponent.Drop(handcuff);
}
sys.TryDrop(user, handcuff);
Container.Insert(handcuff);
CanStillInteract = _entMan.TryGetComponent(Owner, out HandsComponent? ownerHands) && ownerHands.HandNames.Count() > CuffedHandCount;
CanStillInteract = _entMan.TryGetComponent(Owner, out HandsComponent? ownerHands) && ownerHands.Hands.Count() > CuffedHandCount;
OnCuffedStateChanged?.Invoke();
UpdateAlert();
@@ -133,23 +133,22 @@ namespace Content.Server.Cuffs.Components
{
if (!_entMan.TryGetComponent(Owner, out HandsComponent? handsComponent)) return;
var itemCount = handsComponent.GetAllHeldItems().Count();
var freeHandCount = handsComponent.HandNames.Count() - CuffedHandCount;
var sys = _sysMan.GetEntitySystem<SharedHandsSystem>();
if (freeHandCount < itemCount)
var freeHandCount = handsComponent.Hands.Count() - CuffedHandCount;
foreach (var hand in handsComponent.Hands.Values)
{
foreach (var item in handsComponent.GetAllHeldItems())
if (hand.IsEmpty)
continue;
if (freeHandCount > 0)
{
if (freeHandCount < itemCount)
{
freeHandCount++;
handsComponent.Drop(item.Owner, false);
}
else
{
break;
}
freeHandCount--;
continue;
}
sys.TryDrop(Owner, hand, checkActionBlocker: false, handsComp: handsComponent);
}
}
@@ -267,7 +266,7 @@ namespace Content.Server.Cuffs.Components
}
}
CanStillInteract = _entMan.TryGetComponent(Owner, out HandsComponent? handsComponent) && handsComponent.HandNames.Count() > CuffedHandCount;
CanStillInteract = _entMan.TryGetComponent(Owner, out HandsComponent? handsComponent) && handsComponent.SortedHands.Count() > CuffedHandCount;
OnCuffedStateChanged?.Invoke();
UpdateAlert();
Dirty();

View File

@@ -2,7 +2,7 @@ using Content.Server.Cuffs.Components;
using Content.Server.Hands.Components;
using Content.Shared.ActionBlocker;
using Content.Shared.Cuffs;
using Content.Shared.Hands.Components;
using Content.Shared.Hands;
using Content.Shared.MobState.Components;
using Content.Shared.Popups;
using Content.Shared.Verbs;

View File

@@ -84,6 +84,7 @@ namespace Content.Server.Disposal.Tube.Components
{
SoundSystem.Play(Filter.Pvs(Owner), _clickSound.GetSound(), Owner, AudioParams.Default.WithVolume(-2f));
}
protected override void OnRemove()
{
base.OnRemove();

View File

@@ -1,4 +1,4 @@
using Content.Server.Disposal.Tube.Components;
using Content.Server.Disposal.Tube.Components;
using Content.Server.UserInterface;
using Content.Server.Hands.Components;
using Content.Shared.Movement;
@@ -81,7 +81,7 @@ namespace Content.Server.Disposal.Tube
return;
}
var activeHandEntity = hands.GetActiveHandItem?.Owner;
var activeHandEntity = hands.ActiveHandEntity;
if (activeHandEntity != null)
{
args.Cancel();
@@ -96,7 +96,7 @@ namespace Content.Server.Disposal.Tube
return;
}
var activeHandEntity = hands.GetActiveHandItem?.Owner;
var activeHandEntity = hands.ActiveHandEntity;
if (activeHandEntity != null)
{
args.Cancel();

View File

@@ -16,21 +16,16 @@ using Content.Shared.Atmos;
using Content.Shared.Disposal;
using Content.Shared.Disposal.Components;
using Content.Shared.DragDrop;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Item;
using Content.Shared.Movement;
using Content.Shared.Popups;
using Content.Shared.Throwing;
using Content.Shared.Verbs;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Player;
using Robust.Shared.Random;
@@ -43,6 +38,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly AtmosphereSystem _atmosSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
private readonly List<DisposalUnitComponent> _activeDisposals = new();
@@ -140,7 +136,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
Category = VerbCategory.Insert,
Act = () =>
{
args.Hands.Drop(args.Using.Value, component.Container);
_handsSystem.TryDropIntoContainer(args.User, args.Using.Value, component.Container, checkActionBlocker: false, args.Hands);
AfterInsert(component, args.Using.Value);
}
};
@@ -249,7 +245,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
return;
}
if (!CanInsert(component, args.Used) || !hands.Drop(args.Used, component.Container))
if (!CanInsert(component, args.Used) || !_handsSystem.TryDropIntoContainer(args.User, args.Used, component.Container))
{
return;
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Threading.Tasks;
using Content.Server.Hands.Components;
using Content.Shared.Item;
@@ -30,7 +30,7 @@ namespace Content.Server.DoAfter
// NeedHand
private readonly string? _activeHand;
private readonly SharedItemComponent? _activeItem;
private readonly EntityUid? _activeItem;
public DoAfter(DoAfterEventArgs eventArgs, IEntityManager entityManager)
{
@@ -52,8 +52,8 @@ namespace Content.Server.DoAfter
// (or if there is no item there we need to keep it free).
if (eventArgs.NeedHand && entityManager.TryGetComponent(eventArgs.User, out HandsComponent? handsComponent))
{
_activeHand = handsComponent.ActiveHand;
_activeItem = handsComponent.GetActiveHandItem;
_activeHand = handsComponent.ActiveHand?.Name;
_activeItem = handsComponent.ActiveHandEntity;
}
Tcs = new TaskCompletionSource<DoAfterStatus>();
@@ -152,13 +152,13 @@ namespace Content.Server.DoAfter
}
else
{
var currentActiveHand = handsComponent.ActiveHand;
var currentActiveHand = handsComponent.ActiveHand?.Name;
if (_activeHand != currentActiveHand)
{
return true;
}
var currentItem = handsComponent.GetActiveHandItem;
var currentItem = handsComponent.ActiveHandEntity;
if (_activeItem != currentItem)
{
return true;

View File

@@ -19,6 +19,7 @@ using Content.Server.Ghost.Roles.Components;
using Content.Server.Hands.Components;
using Content.Server.UserInterface;
using Robust.Shared.Player;
using Content.Shared.Hands.EntitySystems;
using Robust.Shared.Timing;
namespace Content.Server.Drone
@@ -28,6 +29,7 @@ namespace Content.Server.Drone
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -122,7 +124,12 @@ namespace Content.Server.Drone
{
var item = EntityManager.SpawnEntity(entry.PrototypeId, spawnCoord);
AddComp<UnremoveableComponent>(item);
hands.PutInHand(item);
if (!_handsSystem.TryPickupAnyHand(uid, item, checkActionBlocker: false))
{
QueueDel(item);
Logger.Error($"Drone ({ToPrettyString(uid)}) failed to pick up innate item ({ToPrettyString(item)})");
continue;
}
drone.ToolUids.Add(item);
}
}

View File

@@ -1,8 +1,7 @@
using Content.Server.DoAfter;
using Content.Server.Engineering.Components;
using Content.Server.Hands.Components;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Item;
using Content.Shared.Verbs;
using JetBrains.Annotations;
@@ -11,6 +10,8 @@ namespace Content.Server.Engineering.EntitySystems
[UsedImplicitly]
public sealed class DisassembleOnAltVerbSystem : EntitySystem
{
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
public override void Initialize()
{
base.Initialize();
@@ -63,11 +64,7 @@ namespace Content.Server.Engineering.EntitySystems
var entity = EntityManager.SpawnEntity(component.Prototype, transformComp.Coordinates);
if (TryComp<HandsComponent?>(user, out var hands)
&& TryComp<SharedItemComponent?>(entity, out var item))
{
hands.PutInHandOrDrop(item);
}
_handsSystem.TryPickup(user, entity);
EntityManager.DeleteEntity(component.Owner);

View File

@@ -13,6 +13,7 @@ using Content.Shared.Access.Components;
using Content.Shared.Database;
using Content.Shared.GameTicking;
using Content.Shared.Ghost;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Inventory;
using Content.Shared.PDA;
using Content.Shared.Preferences;
@@ -33,6 +34,7 @@ namespace Content.Server.GameTicking
[Dependency] private readonly IdCardSystem _cardSystem = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
/// <summary>
/// Can't yet be removed because every test ever seems to depend on it. I'll make removing this a different PR.
@@ -319,14 +321,15 @@ namespace Content.Server.GameTicking
}
}
if (EntityManager.TryGetComponent(entity, out HandsComponent? handsComponent))
{
if (!TryComp(entity, out HandsComponent? handsComponent))
return;
var inhand = startingGear.Inhand;
var coords = EntityManager.GetComponent<TransformComponent>(entity).Coordinates;
foreach (var (hand, prototype) in inhand)
{
var inhandEntity = EntityManager.SpawnEntity(prototype, EntityManager.GetComponent<TransformComponent>(entity).Coordinates);
handsComponent.TryPickupEntity(hand, inhandEntity, checkActionBlocker: false);
}
var inhandEntity = EntityManager.SpawnEntity(prototype, coords);
_handsSystem.TryPickup(entity, inhandEntity, hand, checkActionBlocker: false, handsComp: handsComponent);
}
}

View File

@@ -5,6 +5,7 @@ using Content.Shared.Actions;
using Content.Shared.Audio;
using Content.Shared.Damage;
using Content.Shared.Examine;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.MobState;
@@ -25,6 +26,7 @@ namespace Content.Server.Guardian
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly DamageableSystem _damageSystem = default!;
[Dependency] private readonly SharedActionsSystem _actionSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
public override void Initialize()
{
@@ -180,8 +182,7 @@ namespace Content.Server.Guardian
if (comp.Deleted ||
comp.Used ||
!TryComp<HandsComponent>(ev.User, out var hands) ||
!hands.IsHolding(comp.Owner) ||
!_handsSystem.IsHolding(ev.User, comp.Owner, out _) ||
HasComp<GuardianHostComponent>(ev.Target))
{
comp.Injecting = false;

View File

@@ -1,19 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Act;
using Content.Server.Popups;
using Content.Server.Pulling;
using Content.Shared.Audio;
using Content.Shared.Body.Part;
using Content.Shared.Hands.Components;
using Content.Shared.Item;
using Content.Shared.Popups;
using Content.Shared.Pulling.Components;
using Content.Shared.Sound;
using Robust.Shared.Audio;
using Robust.Shared.Player;
using Content.Shared.Hands.EntitySystems;
namespace Content.Server.Hands.Components
{
@@ -24,9 +11,6 @@ namespace Content.Server.Hands.Components
#pragma warning restore 618
{
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
[Dependency] private readonly IEntityManager _entities = default!;
#region Pull/Disarm
void IBodyPartAdded.BodyPartAdded(BodyPartAddedEventArgs args)
{
@@ -43,7 +27,7 @@ namespace Content.Server.Hands.Components
_ => throw new ArgumentOutOfRangeException()
};
AddHand(args.Slot, location);
_entitySystemManager.GetEntitySystem<SharedHandsSystem>().AddHand(Owner, args.Slot, location);
}
void IBodyPartRemoved.BodyPartRemoved(BodyPartRemovedEventArgs args)
@@ -51,94 +35,8 @@ namespace Content.Server.Hands.Components
if (args.Part.PartType != BodyPartType.Hand)
return;
RemoveHand(args.Slot);
_entitySystemManager.GetEntitySystem<SharedHandsSystem>().RemoveHand(Owner, args.Slot);
}
public bool BreakPulls()
{
// What is this API??
// I just wanted to do actions not deal with this shit...
if (!_entities.TryGetComponent(Owner, out SharedPullerComponent? puller)
|| puller.Pulling is not {Valid: true} pulling || !_entities.TryGetComponent(puller.Pulling.Value, out SharedPullableComponent? pullable))
return false;
return _entitySystemManager.GetEntitySystem<PullingSystem>().TryStopPull(pullable);
}
#endregion
#region Old public methods
public IEnumerable<string> HandNames => Hands.Select(h => h.Name);
public int Count => Hands.Count;
/// <summary>
/// Returns a list of all hand names, with the active hand being first.
/// </summary>
public IEnumerable<string> ActivePriorityEnumerable()
{
if (ActiveHand != null)
yield return ActiveHand;
foreach (var hand in Hands)
{
if (hand.Name == ActiveHand)
continue;
yield return hand.Name;
}
}
/// <summary>
/// Tries to get the ItemComponent on the entity held by a hand.
/// </summary>
public SharedItemComponent? GetItem(string handName)
{
if (!TryGetHeldEntity(handName, out var heldEntity))
return null;
_entities.TryGetComponent(heldEntity, out SharedItemComponent? item);
return item;
}
/// <summary>
/// Tries to get the ItemComponent on the entity held by a hand.
/// </summary>
public bool TryGetItem(string handName, [NotNullWhen(true)] out SharedItemComponent? item)
{
item = null;
if (!TryGetHeldEntity(handName, out var heldEntity))
return false;
return _entities.TryGetComponent(heldEntity, out item);
}
/// <summary>
/// Tries to get the ItemComponent off the entity in the active hand.
/// </summary>
public SharedItemComponent? GetActiveHandItem
{
get
{
if (!TryGetActiveHeldEntity(out var heldEntity))
return null;
_entities.TryGetComponent(heldEntity, out SharedItemComponent? item);
return item;
}
}
public IEnumerable<SharedItemComponent> GetAllHeldItems()
{
foreach (var entity in GetAllHeldEntities())
{
if (_entities.TryGetComponent(entity, out SharedItemComponent? item))
yield return item;
}
}
#endregion
}
}

View File

@@ -1,9 +1,8 @@
using Content.Server.Hands.Components;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.Hands.Systems
{
@@ -14,25 +13,16 @@ namespace Content.Server.Hands.Systems
public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user)
{
if (EntityManager.TryGetComponent<HandsComponent>(user, out var hands))
{
foreach (var handName in hands.ActivePriorityEnumerable())
{
var hand = hands.GetHand(handName);
if (hand.HeldEntity != null)
continue;
if (!_handsSystem.TryGetEmptyHand(user, out var hand))
return false;
var pos = EntityManager.GetComponent<TransformComponent>(hands.Owner).Coordinates;
var pos = EntityManager.GetComponent<TransformComponent>(user).Coordinates;
var virtualItem = EntityManager.SpawnEntity("HandVirtualItem", pos);
var virtualItemComp = EntityManager.GetComponent<HandVirtualItemComponent>(virtualItem);
virtualItemComp.BlockingEntity = blockingEnt;
_handsSystem.PutEntityIntoHand(user, hand, virtualItem, hands);
_handsSystem.DoPickup(user, hand, virtualItem);
return true;
}
}
return false;
}
/// <summary>
/// Deletes all virtual items in a user's hands with
@@ -40,20 +30,12 @@ namespace Content.Server.Hands.Systems
/// </summary>
public void DeleteInHandsMatching(EntityUid user, EntityUid matching)
{
if (!EntityManager.TryGetComponent<HandsComponent>(user, out var hands))
return;
foreach (var handName in hands.ActivePriorityEnumerable())
foreach (var hand in _handsSystem.EnumerateHands(user))
{
var hand = hands.GetHand(handName);
if (!(hand.HeldEntity is { } heldEntity))
continue;
if (EntityManager.TryGetComponent<HandVirtualItemComponent>(heldEntity, out var virt)
&& virt.BlockingEntity == matching)
if (TryComp(hand.HeldEntity, out HandVirtualItemComponent? virt) && virt.BlockingEntity == matching)
{
Delete(virt, user);
return;
}
}
}

View File

@@ -21,7 +21,6 @@ using Content.Shared.Popups;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Input.Binding;
@@ -29,6 +28,9 @@ using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Players;
using Robust.Shared.Utility;
using Content.Shared.Pulling.Components;
using Content.Server.Pulling;
using Content.Shared.Hands.EntitySystems;
namespace Content.Server.Hands.Systems
{
@@ -43,27 +45,24 @@ namespace Content.Server.Hands.Systems
[Dependency] private readonly StrippableSystem _strippableSystem = default!;
[Dependency] private readonly SharedHandVirtualItemSystem _virtualSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly PullingSystem _pullingSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<HandsComponent, ExaminedEvent>(HandleExamined);
SubscribeNetworkEvent<ActivateInHandMsg>(HandleActivateInHand);
SubscribeNetworkEvent<ClientInteractUsingInHandMsg>(HandleInteractUsingInHand);
SubscribeNetworkEvent<UseInHandMsg>(HandleUseInHand);
SubscribeNetworkEvent<MoveItemFromHandMsg>(HandleMoveItemFromHand);
SubscribeLocalEvent<HandsComponent, DisarmedEvent>(OnDisarmed, before: new[] { typeof(StunSystem) });
SubscribeLocalEvent<HandsComponent, PullAttemptMessage>(HandlePullAttempt);
SubscribeLocalEvent<HandsComponent, PullStartedMessage>(HandlePullStarted);
SubscribeLocalEvent<HandsComponent, PullStoppedMessage>(HandlePullStopped);
SubscribeLocalEvent<HandsComponent, EntRemovedFromContainerMessage>(HandleEntityRemoved);
SubscribeLocalEvent<HandsComponent, ComponentGetState>(GetComponentState);
CommandBinds.Builder
.Bind(ContentKeyFunctions.ActivateItemInHand, InputCmdHandler.FromDelegate(s => HandleActivateItem(s)))
.Bind(ContentKeyFunctions.AltActivateItemInHand, InputCmdHandler.FromDelegate(s => HandleActivateItem(s, true)))
.Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem))
.Bind(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack))
.Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt))
@@ -79,15 +78,19 @@ namespace Content.Server.Hands.Systems
private void GetComponentState(EntityUid uid, HandsComponent hands, ref ComponentGetState args)
{
args.State = new HandsComponentState(hands.Hands, hands.ActiveHand);
args.State = new HandsComponentState(hands);
}
private void OnDisarmed(EntityUid uid, HandsComponent component, DisarmedEvent args)
{
if (args.Handled || component.BreakPulls())
if (args.Handled)
return;
if (component.ActiveHand == null || !component.Drop(component.ActiveHand, false))
// Break any pulls
if (TryComp(uid, out SharedPullerComponent? puller) && puller.Pulling is EntityUid pulled && TryComp(pulled, out SharedPullableComponent? pullable))
_pullingSystem.TryStopPull(pullable);
if (_handsSystem.TryDrop(uid, component.ActiveHand!, null, checkActionBlocker: false))
return;
var targetName = Name(args.Target);
@@ -103,9 +106,9 @@ namespace Content.Server.Hands.Systems
}
#region EntityInsertRemove
public override void RemoveHeldEntityFromHand(EntityUid uid, Hand hand, SharedHandsComponent? hands = null)
public override void DoDrop(EntityUid uid, Hand hand, bool doDropInteraction = true, SharedHandsComponent? hands = null)
{
base.RemoveHeldEntityFromHand(uid, hand, hands);
base.DoDrop(uid, hand,doDropInteraction, hands);
// update gui of anyone stripping this entity.
_strippableSystem.SendUpdate(uid);
@@ -114,9 +117,9 @@ namespace Content.Server.Hands.Systems
sprite.RenderOrder = EntityManager.CurrentTick.Value;
}
public override void PutEntityIntoHand(EntityUid uid, Hand hand, EntityUid entity, SharedHandsComponent? hands = null)
public override void DoPickup(EntityUid uid, Hand hand, EntityUid entity, SharedHandsComponent? hands = null)
{
base.PutEntityIntoHand(uid, hand, entity, hands);
base.DoPickup(uid, hand, entity, hands);
// update gui of anyone stripping this entity.
_strippableSystem.SendUpdate(uid);
@@ -124,6 +127,7 @@ namespace Content.Server.Hands.Systems
_logSystem.Add(LogType.Pickup, LogImpact.Low, $"{uid} picked up {entity}");
}
public override void PickupAnimation(EntityUid item, EntityCoordinates initialPosition, Vector2 finalPosition,
EntityUid? exclude)
{
@@ -138,12 +142,10 @@ namespace Content.Server.Hands.Systems
RaiseNetworkEvent(new PickupAnimationEvent(item, initialPosition, finalPosition), filter);
}
protected override void HandleContainerRemoved(EntityUid uid, SharedHandsComponent component, ContainerModifiedMessage args)
private void HandleEntityRemoved(EntityUid uid, SharedHandsComponent component, EntRemovedFromContainerMessage args)
{
if (!Deleted(args.Entity) && TryComp(args.Entity, out HandVirtualItemComponent? @virtual))
_virtualSystem.Delete(@virtual, uid);
base.HandleContainerRemoved(uid, component, args);
}
#endregion
@@ -154,11 +156,10 @@ namespace Content.Server.Hands.Systems
return;
// Cancel pull if all hands full.
if (component.Hands.All(hand => !hand.IsEmpty))
if (!component.IsAnyHandFree())
args.Cancelled = true;
}
private void HandlePullStarted(EntityUid uid, HandsComponent component, PullStartedMessage args)
{
if (args.Puller.Owner != uid)
@@ -177,7 +178,7 @@ namespace Content.Server.Hands.Systems
// Try find hand that is doing this pull.
// and clear it.
foreach (var hand in component.Hands)
foreach (var hand in component.Hands.Values)
{
if (hand.HeldEntity == null
|| !TryComp(hand.HeldEntity, out HandVirtualItemComponent? virtualItem)
@@ -191,34 +192,6 @@ namespace Content.Server.Hands.Systems
#endregion
#region interactions
private void HandleMoveItemFromHand(MoveItemFromHandMsg msg, EntitySessionEventArgs args)
{
if (TryComp(args.SenderSession.AttachedEntity, out SharedHandsComponent? hands))
hands.TryMoveHeldEntityToActiveHand(msg.HandName);
}
private void HandleUseInHand(UseInHandMsg msg, EntitySessionEventArgs args)
{
if (TryComp(args.SenderSession.AttachedEntity, out SharedHandsComponent? hands))
hands.ActivateItem();
}
private void HandleInteractUsingInHand(ClientInteractUsingInHandMsg msg, EntitySessionEventArgs args)
{
if (TryComp(args.SenderSession.AttachedEntity, out SharedHandsComponent? hands))
hands.InteractHandWithActiveHand(msg.HandName);
}
private void HandleActivateInHand(ActivateInHandMsg msg, EntitySessionEventArgs args)
{
if (TryComp(args.SenderSession.AttachedEntity, out SharedHandsComponent? hands))
hands.ActivateHeldEntity(msg.HandName);
}
private void HandleActivateItem(ICommonSession? session, bool altInteract = false)
{
if (TryComp(session?.AttachedEntity, out SharedHandsComponent? hands))
hands.ActivateItem(altInteract);
}
private bool HandleThrowItem(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
{
if (session is not IPlayerSession playerSession)
@@ -228,20 +201,20 @@ namespace Content.Server.Hands.Systems
!Exists(player) ||
player.IsInContainer() ||
!TryComp(player, out SharedHandsComponent? hands) ||
!hands.TryGetActiveHeldEntity(out var throwEnt) ||
hands.ActiveHandEntity is not EntityUid throwEnt ||
!_actionBlockerSystem.CanThrow(player))
return false;
if (EntityManager.TryGetComponent(throwEnt.Value, out StackComponent? stack) && stack.Count > 1 && stack.ThrowIndividually)
if (EntityManager.TryGetComponent(throwEnt, out StackComponent? stack) && stack.Count > 1 && stack.ThrowIndividually)
{
var splitStack = _stackSystem.Split(throwEnt.Value, 1, EntityManager.GetComponent<TransformComponent>(player).Coordinates, stack);
var splitStack = _stackSystem.Split(throwEnt, 1, EntityManager.GetComponent<TransformComponent>(player).Coordinates, stack);
if (splitStack is not {Valid: true})
return false;
throwEnt = splitStack.Value;
}
else if (!hands.Drop(throwEnt.Value))
else if (!TryDrop(player, throwEnt, handsComp: hands))
return false;
var direction = coords.ToMapPos(EntityManager) - Transform(player).WorldPosition;
@@ -251,7 +224,7 @@ namespace Content.Server.Hands.Systems
direction = direction.Normalized * Math.Min(direction.Length, hands.ThrowRange);
var throwStrength = hands.ThrowForceMultiplier;
throwEnt.Value.TryThrow(direction, throwStrength, player);
throwEnt.TryThrow(direction, throwStrength, player);
return true;
}
@@ -287,7 +260,7 @@ namespace Content.Server.Hands.Systems
return;
}
if (hands.ActiveHandIsHoldingEntity())
if (hands.ActiveHand?.HeldEntity != null)
{
storageComponent.PlayerInsertHeldEntity(plyEnt);
}
@@ -302,24 +275,11 @@ namespace Content.Server.Hands.Systems
var lastStoredEntity = Enumerable.Last(storageComponent.StoredEntities);
if (storageComponent.Remove(lastStoredEntity))
{
if (!hands.TryPickupEntityToActiveHand(lastStoredEntity, animateUser: true))
Transform(lastStoredEntity).Coordinates = Transform(plyEnt).Coordinates;
PickupOrDrop(plyEnt, lastStoredEntity, animateUser: true, handsComp: hands);
}
}
}
}
#endregion
//TODO: Actually shows all items/clothing/etc.
private void HandleExamined(EntityUid uid, HandsComponent component, ExaminedEvent args)
{
foreach (var inhand in component.GetAllHeldItems())
{
if (HasComp<HandVirtualItemComponent>(inhand.Owner))
continue;
args.PushText(Loc.GetString("comp-hands-examine", ("user", component.Owner), ("item", inhand.Owner)));
}
}
}
}

View File

@@ -218,7 +218,7 @@ namespace Content.Server.Interaction
// Verify user has a hand, and find what object they are currently holding in their active hand
if (TryComp(user, out HandsComponent? hands))
{
var item = hands.GetActiveHandItem?.Owner;
var item = hands.ActiveHandEntity;
if (item != null && !Deleted(item.Value))
{

View File

@@ -1,12 +1,8 @@
using Content.Server.Hands.Components;
using Content.Server.Holiday;
using Content.Shared.Item;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Roles;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Jobs
@@ -26,17 +22,16 @@ namespace Content.Server.Jobs
if (string.IsNullOrEmpty(Holiday) || string.IsNullOrEmpty(Prototype))
return;
if (!EntitySystem.Get<HolidaySystem>().IsCurrentlyHoliday(Holiday))
var sysMan = IoCManager.Resolve<IEntitySystemManager>();
if (!sysMan.GetEntitySystem<HolidaySystem>().IsCurrentlyHoliday(Holiday))
return;
var entMan = IoCManager.Resolve<IEntityManager>();
var entity = entMan.SpawnEntity(Prototype, entMan.GetComponent<TransformComponent>(mob).Coordinates);
if (!entMan.TryGetComponent(entity, out SharedItemComponent? item) || !entMan.TryGetComponent(mob, out HandsComponent? hands))
return;
hands.PutInHand(item, false);
sysMan.GetEntitySystem<SharedHandsSystem>().PickupOrDrop(mob, entity);
}
}
}

View File

@@ -216,7 +216,7 @@ namespace Content.Server.Kitchen.Components
return false;
}
if (_entities.GetComponent<HandsComponent>(eventArgs.User).GetActiveHandItem?.Owner is not {Valid: true} itemEntity)
if (_entities.GetComponent<HandsComponent>(eventArgs.User).ActiveHandEntity is not {Valid: true} itemEntity)
{
eventArgs.User.PopupMessage(Loc.GetString("microwave-component-interact-using-no-active-hand"));
return false;

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using Content.Server.Chemistry.EntitySystems;
using Content.Server.Hands.Components;
@@ -7,6 +7,7 @@ using Content.Server.Kitchen.Events;
using Content.Server.Power.Components;
using Content.Server.Stack;
using Content.Server.UserInterface;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Item;
using Content.Shared.Kitchen.Components;
@@ -16,10 +17,6 @@ using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Player;
using Robust.Shared.Utility;
@@ -29,6 +26,7 @@ namespace Content.Server.Kitchen.EntitySystems
internal sealed class ReagentGrinderSystem : EntitySystem
{
[Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
private Queue<ReagentGrinderComponent> _uiUpdateQueue = new();
@@ -267,7 +265,7 @@ namespace Content.Server.Kitchen.EntitySystems
!EntityManager.TryGetComponent<SharedItemComponent?>(beaker, out var item))
return;
hands.PutInHandOrDrop(item);
_handsSystem.PickupOrDrop(user.Value, beaker, handsComp: hands, item: item);
component.HeldBeaker = null;
EnqueueUiUpdate(component);

View File

@@ -11,16 +11,13 @@ using Content.Shared.Audio;
using Content.Shared.Damage;
using Content.Shared.Database;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Light;
using Content.Shared.Popups;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Player;
using Robust.Shared.Timing;
@@ -37,6 +34,7 @@ namespace Content.Server.Light.EntitySystems
[Dependency] private readonly LightBulbSystem _bulbSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly AdminLogSystem _logSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
private static readonly TimeSpan ThunkDelay = TimeSpan.FromSeconds(2);
@@ -171,11 +169,7 @@ namespace Content.Server.Light.EntitySystems
return null;
// try to place bulb in hands
if (userUid != null)
{
if (EntityManager.TryGetComponent(userUid.Value, out SharedHandsComponent? hands))
hands.PutInHand(bulb);
}
_handsSystem.PickupOrDrop(userUid, bulb);
UpdateLight(uid, light);
return bulb;

View File

@@ -123,7 +123,7 @@ namespace Content.Server.MachineLinking.System
case SignalPortSelected portSelected:
if (msg.Session.AttachedEntity == default ||
!EntityManager.TryGetComponent(msg.Session.AttachedEntity, out HandsComponent? hands) ||
!hands.TryGetActiveHeldEntity(out var heldEntity) ||
hands.ActiveHandEntity is not EntityUid heldEntity ||
!EntityManager.TryGetComponent(heldEntity, out SignalLinkerComponent? signalLinkerComponent) ||
!signalLinkerComponent.Port.HasValue ||
!signalLinkerComponent.Port.Value.transmitter.Outputs.ContainsPort(signalLinkerComponent.Port
@@ -165,7 +165,7 @@ namespace Content.Server.MachineLinking.System
case SignalPortSelected portSelected:
if (msg.Session.AttachedEntity == default ||
!EntityManager.TryGetComponent(msg.Session.AttachedEntity, out HandsComponent? hands) ||
!hands.TryGetActiveHeldEntity(out var heldEntity) ||
hands.ActiveHandEntity is not EntityUid heldEntity ||
!EntityManager.TryGetComponent(heldEntity, out SignalLinkerComponent? signalLinkerComponent))
return;
LinkerSaveInteraction(attached, signalLinkerComponent, component,

View File

@@ -7,7 +7,6 @@ using Content.Server.DoAfter;
using Content.Server.Hands.Components;
using Content.Server.Nutrition.Components;
using Content.Server.Popups;
using Content.Shared.ActionBlocker;
using Content.Shared.Administration.Logs;
using Content.Shared.Body.Components;
using Content.Shared.Chemistry.Reagent;
@@ -20,7 +19,7 @@ using Robust.Shared.Audio;
using Robust.Shared.Player;
using Robust.Shared.Utility;
using Content.Shared.Inventory;
using Content.Shared.Item;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction.Events;
namespace Content.Server.Nutrition.EntitySystems
@@ -39,6 +38,7 @@ namespace Content.Server.Nutrition.EntitySystems
[Dependency] private readonly SharedAdminLogSystem _logSystem = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
public override void Initialize()
{
@@ -223,18 +223,12 @@ namespace Content.Server.Nutrition.EntitySystems
var finisher = EntityManager.SpawnEntity(component.TrashPrototype, position);
// If the user is holding the item
if (user != null &&
EntityManager.TryGetComponent(user.Value, out HandsComponent? handsComponent) &&
handsComponent.IsHolding(component.Owner))
if (user != null && _handsSystem.IsHolding(user.Value, component.Owner, out var hand))
{
EntityManager.DeleteEntity((component).Owner);
// Put the trash in the user's hand
if (EntityManager.TryGetComponent(finisher, out SharedItemComponent? item) &&
handsComponent.CanPutInHand(item))
{
handsComponent.PutInHand(item);
}
_handsSystem.TryPickup(user.Value, finisher, hand);
return;
}
@@ -329,10 +323,10 @@ namespace Content.Server.Nutrition.EntitySystems
var usedTypes = UtensilType.None;
foreach (var item in hands.GetAllHeldItems())
foreach (var item in _handsSystem.EnumerateHeld(user, hands))
{
// Is utensil?
if (!EntityManager.TryGetComponent(item.Owner, out UtensilComponent? utensil))
if (!EntityManager.TryGetComponent(item, out UtensilComponent? utensil))
continue;
if ((utensil.Types & component.Utensil) != 0 && // Acceptable type?

View File

@@ -5,13 +5,11 @@ using Content.Server.Nutrition.Components;
using Content.Shared.Chemistry.Components;
using Content.Shared.Examine;
using Content.Shared.FixedPoint;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Item;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Player;
namespace Content.Server.Nutrition.EntitySystems
@@ -19,6 +17,8 @@ namespace Content.Server.Nutrition.EntitySystems
internal sealed class SliceableFoodSystem : EntitySystem
{
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
public override void Initialize()
{
@@ -67,9 +67,9 @@ namespace Content.Server.Nutrition.EntitySystems
if (EntityManager.TryGetComponent(user, out HandsComponent? handsComponent))
{
if (ContainerHelpers.IsInContainer(component.Owner))
if (_containerSystem.IsEntityInContainer(component.Owner))
{
handsComponent.PutInHandOrDrop(EntityManager.GetComponent<SharedItemComponent>(sliceUid));
_handsSystem.PickupOrDrop(user, sliceUid, handsComp: handsComponent);
}
}

View File

@@ -1,82 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.Hands.Components;
using Content.Shared.Access.Components;
using Content.Shared.Inventory;
using Content.Shared.PDA;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.PDA
{
public static class PdaExtensions
{
/// <summary>
/// Gets the id that a player is holding in their hands or inventory.
/// Order: Hands > ID slot > PDA in ID slot
/// </summary>
/// <param name="player">The player to check in.</param>
/// <returns>The id card component.</returns>
public static IdCardComponent? GetHeldId(this EntityUid player)
{
IdCardComponent? foundPDAId = null;
var entMan = IoCManager.Resolve<IEntityManager>();
if (entMan.TryGetComponent(player, out HandsComponent? hands))
{
foreach (var item in hands.GetAllHeldItems())
{
if (entMan.TryGetComponent(item.Owner, out PDAComponent? pda) &&
pda.ContainedID != null)
{
foundPDAId = pda.ContainedID;
}
if (entMan.TryGetComponent(item.Owner, out IdCardComponent? card))
{
return card;
}
}
}
if (foundPDAId != null) return foundPDAId;
var invSystem = EntitySystem.Get<InventorySystem>();
if (invSystem.TryGetContainerSlotEnumerator(player, out var enumerator))
{
while (enumerator.MoveNext(out var containerSlot))
{
if(!containerSlot.ContainedEntity.HasValue) continue;
if (entMan.TryGetComponent(containerSlot.ContainedEntity.Value, out PDAComponent? pda) &&
pda.ContainedID != null)
{
foundPDAId = pda.ContainedID;
}
if (entMan.TryGetComponent(containerSlot.ContainedEntity.Value, out IdCardComponent? card))
{
return card;
}
}
}
if (foundPDAId != null) return foundPDAId;
return null;
}
/// <summary>
/// Gets the id that a player is holding in their hands or inventory.
/// Order: Hands > ID slot > PDA in ID slot
/// </summary>
/// <param name="player">The player to check in.</param>
/// <param name="id">The id card component.</param>
/// <returns>true if found, false otherwise.</returns>
public static bool TryGetHeldId(this EntityUid player, [NotNullWhen(true)] out IdCardComponent? id)
{
return (id = player.GetHeldId()) != null;
}
}
}

View File

@@ -11,6 +11,7 @@ using Content.Server.Throwing;
using Content.Server.Tools.Components;
using Content.Shared.Camera;
using Content.Shared.CombatMode;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Item;
using Content.Shared.PneumaticCannon;
@@ -19,11 +20,7 @@ using Content.Shared.StatusEffect;
using Content.Shared.Verbs;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Player;
using Robust.Shared.Random;
@@ -35,6 +32,7 @@ namespace Content.Server.PneumaticCannon
[Dependency] private readonly StunSystem _stun = default!;
[Dependency] private readonly AtmosphereSystem _atmos = default!;
[Dependency] private readonly CameraRecoilSystem _cameraRecoil = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
private HashSet<PneumaticCannonComponent> _currentlyFiring = new();
@@ -320,10 +318,7 @@ namespace Content.Server.PneumaticCannon
if (component.GasTankSlot.Remove(contained))
{
if (EntityManager.TryGetComponent<HandsComponent?>(user, out var hands))
{
hands.PutInHand(contained);
}
_handsSystem.TryPickupAnyHand(user, contained);
user.PopupMessage(Loc.GetString("pneumatic-cannon-component-gas-tank-remove",
("tank", contained), ("cannon", component.Owner)));

View File

@@ -5,6 +5,7 @@ using Content.Shared.Access;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Inventory;
using Content.Shared.Item;
using Content.Shared.PDA;
@@ -32,6 +33,7 @@ namespace Content.Server.Sandbox
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly ItemSlotsSystem _slots = default!;
[Dependency] private readonly GameTicker _ticker = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
private bool _isSandboxEnabled;
@@ -154,7 +156,7 @@ namespace Content.Server.Sandbox
var card = CreateFreshId();
if (!_inventory.TryEquip(attached, card, "id", true, true))
{
hands.PutInHandOrDrop(Comp<SharedItemComponent>(card));
_handsSystem.PickupOrDrop(attached, card, handsComp: hands);
}
}

View File

@@ -1,6 +1,7 @@
using System;
using Content.Server.Hands.Components;
using Content.Server.Popups;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Item;
using Content.Shared.Stacks;
@@ -25,6 +26,7 @@ namespace Content.Server.Stack
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
public static readonly int[] DefaultSplitAmounts = { 1, 5, 10, 20, 30, 50 };
@@ -180,10 +182,7 @@ namespace Content.Server.Stack
if (Split(uid, amount, userTransform.Coordinates, stack) is not {} split)
return;
if (TryComp<HandsComponent>(userUid, out var hands) && TryComp<SharedItemComponent>(split, out var item))
{
hands.PutInHandOrDrop(item);
}
_handsSystem.PickupOrDrop(userUid, split);
_popupSystem.PopupCursor(Loc.GetString("comp-stack-split"), Filter.Entities(userUid));
}

View File

@@ -1,8 +1,6 @@
using Content.Server.Hands.Components;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Standing;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Random;
namespace Content.Server.Standing;
@@ -10,22 +8,26 @@ namespace Content.Server.Standing;
public sealed class StandingStateSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
private void FallOver(EntityUid uid, StandingStateComponent component, DropHandItemsEvent args)
{
var direction = EntityManager.TryGetComponent(uid, out PhysicsComponent? comp) ? comp.LinearVelocity / 50 : Vector2.Zero;
var dropAngle = _random.NextFloat(0.8f, 1.2f);
if (!EntityManager.TryGetComponent(uid, out HandsComponent? hands))
if (!TryComp(uid, out SharedHandsComponent? handsComp))
return;
foreach (var heldItem in hands.GetAllHeldItems())
var worldRotation = EntityManager.GetComponent<TransformComponent>(uid).WorldRotation.ToVec();
foreach (var hand in handsComp.Hands.Values)
{
if (!hands.Drop(heldItem.Owner, false))
if (hand.HeldEntity is not EntityUid held)
continue;
var worldRotation = EntityManager.GetComponent<TransformComponent>(uid).WorldRotation.ToVec();
Throwing.ThrowHelper.TryThrow(heldItem.Owner,
if (!_handsSystem.TryDrop(uid, hand, null, checkActionBlocker: false, handsComp: handsComp))
continue;
Throwing.ThrowHelper.TryThrow(held,
_random.NextAngle().RotateVec(direction / dropAngle + worldRotation / 50),
0.5f * dropAngle * _random.NextFloat(-0.9f, 1.1f),
uid, 0);

View File

@@ -8,6 +8,7 @@ using Content.Server.Hands.Components;
using Content.Server.Interaction;
using Content.Shared.Acts;
using Content.Shared.Coordinates;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
using Content.Shared.Item;
@@ -46,6 +47,7 @@ namespace Content.Server.Storage.Components
public sealed class ServerStorageComponent : SharedStorageComponent, IInteractUsing, IActivate, IStorageComponent, IDestroyAct, IExAct, IAfterInteract
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
private const string LoggerName = "Storage";
@@ -243,22 +245,24 @@ namespace Content.Server.Storage.Components
EnsureInitialCalculated();
if (!_entityManager.TryGetComponent(player, out HandsComponent? hands) ||
hands.GetActiveHandItem == null)
hands.ActiveHandEntity == null)
{
return false;
}
var toInsert = hands.GetActiveHandItem;
var toInsert = hands.ActiveHandEntity;
if (!hands.Drop(toInsert.Owner))
var handSys = _sysMan.GetEntitySystem<SharedHandsSystem>();
if (!handSys.TryDrop(player, toInsert.Value, handsComp: hands))
{
Owner.PopupMessage(player, "Can't insert.");
return false;
}
if (!Insert(toInsert.Owner))
if (!Insert(toInsert.Value))
{
hands.PutInHand(toInsert);
handSys.PickupOrDrop(player, toInsert.Value, handsComp: hands);
Owner.PopupMessage(player, "Can't insert.");
return false;
}
@@ -475,18 +479,7 @@ namespace Content.Server.Storage.Components
break;
}
if (!_entityManager.TryGetComponent(remove.EntityUid, out SharedItemComponent? item) || !_entityManager.TryGetComponent(player, out HandsComponent? hands))
{
break;
}
if (!hands.CanPutInHand(item))
{
break;
}
hands.PutInHand(item);
_sysMan.GetEntitySystem<SharedHandsSystem>().TryPickupAnyHand(player, remove.EntityUid);
break;
}
case InsertEntityMessage _:

View File

@@ -1,8 +1,8 @@
using Content.Server.Clothing.Components;
using Content.Server.Popups;
using Content.Server.Storage.Components;
using Content.Shared.Acts;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Item;
using Robust.Shared.Containers;
using Robust.Shared.Player;
@@ -12,6 +12,7 @@ namespace Content.Server.Storage.EntitySystems
public sealed class SecretStashSystem : EntitySystem
{
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
public override void Initialize()
{
@@ -83,7 +84,7 @@ namespace Content.Server.Storage.EntitySystems
}
// try to move item from hands to stash container
if (!hands.Drop(itemToHideUid, container))
if (!_handsSystem.TryDropIntoContainer(userUid, itemToHideUid, container))
{
return false;
}
@@ -115,13 +116,7 @@ namespace Content.Server.Storage.EntitySystems
return false;
}
// get item inside container
var itemUid = container.ContainedEntity;
if (!EntityManager.TryGetComponent(itemUid, out SharedItemComponent? item))
{
return false;
}
hands.PutInHandOrDrop(item);
_handsSystem.PickupOrDrop(userUid, container.ContainedEntity.Value, handsComp: hands);
// show success message
var successMsg = Loc.GetString("comp-secret-stash-action-get-item-found-something",

View File

@@ -1,10 +1,7 @@
using System.Collections.Generic;
using Content.Server.Storage.Components;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction.Events;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Player;
using Robust.Shared.Random;
@@ -13,6 +10,7 @@ namespace Content.Server.Storage.EntitySystems
public sealed class SpawnItemsOnUseSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
public override void Initialize()
{
@@ -57,10 +55,9 @@ namespace Content.Server.Storage.EntitySystems
EntityManager.DeleteEntity(uid);
}
if (entityToPlaceInHands != null
&& EntityManager.TryGetComponent<SharedHandsComponent?>(args.User, out var hands))
if (entityToPlaceInHands != null)
{
hands.TryPutInAnyHand(entityToPlaceInHands.Value);
_handsSystem.PickupOrDrop(args.User, entityToPlaceInHands.Value);
}
}
}

View File

@@ -1,24 +1,18 @@
using System.Collections.Generic;
using System.Threading;
using Content.Server.Cuffs.Components;
using Content.Server.DoAfter;
using Content.Server.Hands.Components;
using Content.Server.Inventory;
using Content.Server.UserInterface;
using Content.Shared.ActionBlocker;
using Content.Shared.DragDrop;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory;
using Content.Shared.Popups;
using Content.Shared.Strip.Components;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.ViewVariables;
namespace Content.Server.Strip
{
@@ -28,6 +22,7 @@ namespace Content.Server.Strip
public sealed class StrippableComponent : SharedStrippableComponent
{
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
private StrippableSystem _strippableSystem = default!;
public const float StripDelay = 2f;
@@ -84,18 +79,18 @@ namespace Content.Server.Strip
private async void PlaceActiveHandItemInInventory(EntityUid user, string slot)
{
var userHands = _entities.GetComponent<HandsComponent>(user);
var item = userHands.GetActiveHandItem;
var invSystem = EntitySystem.Get<InventorySystem>();
var invSystem = _sysMan.GetEntitySystem<InventorySystem>();
var handSys = _sysMan.GetEntitySystem<SharedHandsSystem>();
bool Check()
{
if (item == null)
if (userHands.ActiveHand?.HeldEntity is not EntityUid held)
{
user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything"));
return false;
}
if (!userHands.CanDrop(userHands.ActiveHand!))
if (!handSys.CanDropHeld(user, userHands.ActiveHand))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop"));
return false;
@@ -110,7 +105,7 @@ namespace Content.Server.Strip
return false;
}
if (!invSystem.CanEquip(user, Owner, item.Owner, slot, out _))
if (!invSystem.CanEquip(user, Owner, held, slot, out _))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-equip-message",("owner", Owner)));
return false;
@@ -134,8 +129,11 @@ namespace Content.Server.Strip
var result = await doAfterSystem.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) return;
userHands.Drop(item!.Owner, false);
invSystem.TryEquip(user, Owner, item.Owner, slot);
if (userHands.ActiveHand?.HeldEntity is EntityUid held
&& handSys.TryDrop(user, userHands.ActiveHand, handsComp: userHands))
{
invSystem.TryEquip(user, Owner, held, slot);
}
UpdateState();
}
@@ -143,38 +141,28 @@ namespace Content.Server.Strip
/// <summary>
/// Places item in user's active hand in one of the entity's hands.
/// </summary>
private async void PlaceActiveHandItemInHands(EntityUid user, string hand)
private async void PlaceActiveHandItemInHands(EntityUid user, string handName)
{
var hands = _entities.GetComponent<HandsComponent>(Owner);
var userHands = _entities.GetComponent<HandsComponent>(user);
var item = userHands.GetActiveHandItem;
var sys = _sysMan.GetEntitySystem<SharedHandsSystem>();
bool Check()
{
if (item == null)
if (userHands.ActiveHandEntity == null)
{
user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything"));
return false;
}
if (!userHands.CanDrop(userHands.ActiveHand!))
if (!sys.CanDropHeld(user, userHands.ActiveHand!))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop"));
return false;
}
if (!hands.HasHand(hand))
{
return false;
}
if (hands.TryGetItem(hand, out var _))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-occupied-message", ("owner", Owner)));
return false;
}
if (!hands.CanPickupEntity(hand, item.Owner, checkActionBlocker: false))
if (!hands.Hands.TryGetValue(handName, out var hand)
|| !sys.CanPickupToHand(Owner, userHands.ActiveHandEntity.Value, hand, checkActionBlocker: false, hands))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-put-message",("owner", Owner)));
return false;
@@ -183,7 +171,7 @@ namespace Content.Server.Strip
return true;
}
var doAfterSystem = EntitySystem.Get<DoAfterSystem>();
var doAfterSystem = _sysMan.GetEntitySystem<DoAfterSystem>();
var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
{
@@ -198,8 +186,11 @@ namespace Content.Server.Strip
var result = await doAfterSystem.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) return;
userHands.Drop(hand);
hands.TryPickupEntity(hand, item!.Owner, checkActionBlocker: false, animateUser: true);
if (userHands.ActiveHandEntity is not EntityUid held)
return;
sys.TryDrop(user, checkActionBlocker: false, handsComp: userHands);
sys.TryPickup(Owner, held, handName, checkActionBlocker: false, animateUser: true, handsComp: hands);
// hand update will trigger strippable update
}
@@ -210,7 +201,7 @@ namespace Content.Server.Strip
{
var inventory = _entities.GetComponent<InventoryComponent>(Owner);
var userHands = _entities.GetComponent<HandsComponent>(user);
var invSystem = EntitySystem.Get<InventorySystem>();
var invSystem = _sysMan.GetEntitySystem<InventorySystem>();
bool Check()
{
@@ -232,7 +223,7 @@ namespace Content.Server.Strip
return true;
}
var doAfterSystem = EntitySystem.Get<DoAfterSystem>();
var doAfterSystem = _sysMan.GetEntitySystem<DoAfterSystem>();
var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
{
@@ -250,7 +241,8 @@ namespace Content.Server.Strip
{
// Raise a dropped event, so that things like gas tank internals properly deactivate when stripping
_entities.EventBus.RaiseLocalEvent(item.Value, new DroppedEvent(user));
userHands.PutInHandOrDrop(item.Value);
_sysMan.GetEntitySystem<SharedHandsSystem>().PickupOrDrop(user, item.Value);
}
UpdateState();
@@ -259,26 +251,24 @@ namespace Content.Server.Strip
/// <summary>
/// Takes an item from a hand and places it in the user's active hand.
/// </summary>
private async void TakeItemFromHands(EntityUid user, string hand)
private async void TakeItemFromHands(EntityUid user, string handName)
{
var hands = _entities.GetComponent<HandsComponent>(Owner);
var userHands = _entities.GetComponent<HandsComponent>(user);
var handSys = _sysMan.GetEntitySystem<SharedHandsSystem>();
bool Check()
{
if (!hands.HasHand(hand))
return false;
if (!hands.TryGetItem(hand, out var heldItem))
if (!hands.Hands.TryGetValue(handName, out var hand) || hand.HeldEntity == null)
{
user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message",("owner", Owner)));
return false;
}
if (_entities.HasComponent<HandVirtualItemComponent>(heldItem.Owner))
if (_entities.HasComponent<HandVirtualItemComponent>(hand.HeldEntity))
return false;
if (!hands.CanDrop(hand, false))
if (!handSys.CanDropHeld(Owner, hand, false))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop-message",("owner", Owner)));
return false;
@@ -287,7 +277,7 @@ namespace Content.Server.Strip
return true;
}
var doAfterSystem = EntitySystem.Get<DoAfterSystem>();
var doAfterSystem = _sysMan.GetEntitySystem<DoAfterSystem>();
var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
{
@@ -301,11 +291,11 @@ namespace Content.Server.Strip
var result = await doAfterSystem.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) return;
if (!hands.TryGetHeldEntity(hand, out var entity))
if (!hands.Hands.TryGetValue(handName, out var hand) || hand.HeldEntity is not EntityUid held)
return;
hands.Drop(hand, false);
userHands.PutInHandOrDrop(entity.Value);
handSys.TryDrop(Owner, hand, checkActionBlocker: false, handsComp: hands);
handSys.PickupOrDrop(user, held, handsComp: userHands);
// hand update will trigger strippable update
}
@@ -315,14 +305,14 @@ namespace Content.Server.Strip
!_entities.TryGetComponent(user, out HandsComponent? userHands))
return;
var placingItem = userHands.GetActiveHandItem != null;
var placingItem = userHands.ActiveHandEntity != null;
switch (obj.Message)
{
case StrippingInventoryButtonPressed inventoryMessage:
if (_entities.TryGetComponent<InventoryComponent?>(Owner, out var inventory))
{
if (EntitySystem.Get<InventorySystem>().TryGetSlotEntity(Owner, inventoryMessage.Slot, out _, inventory))
if (_sysMan.GetEntitySystem<InventorySystem>().TryGetSlotEntity(Owner, inventoryMessage.Slot, out _, inventory))
placingItem = false;
if (placingItem)
@@ -336,7 +326,7 @@ namespace Content.Server.Strip
if (_entities.TryGetComponent<HandsComponent?>(Owner, out var hands))
{
if (hands.TryGetItem(handMessage.Hand, out _))
if (hands.Hands.TryGetValue(handMessage.Hand, out var hand) && !hand.IsEmpty)
placingItem = false;
if (placingItem)

View File

@@ -76,17 +76,15 @@ namespace Content.Server.Strip
if (TryComp(uid, out HandsComponent? handsComp))
{
foreach (var hand in handsComp.HandNames)
foreach (var hand in handsComp.Hands.Values)
{
var owner = handsComp.GetItem(hand)?.Owner;
if (!owner.HasValue || HasComp<HandVirtualItemComponent>(owner.Value))
if (hand.HeldEntity == null || HasComp<HandVirtualItemComponent>(hand.HeldEntity))
{
hands[hand] = "None";
hands[hand.Name] = "None";
continue;
}
hands[hand] = Name(owner.Value);
hands[hand.Name] = Name(hand.HeldEntity.Value);
}
}

View File

@@ -5,6 +5,7 @@ using Content.Server.Traitor.Uplink.Components;
using Content.Server.UserInterface;
using Content.Shared.ActionBlocker;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Inventory;
using Content.Shared.Item;
@@ -28,6 +29,7 @@ namespace Content.Server.Traitor.Uplink
private readonly UplinkListingSytem _listing = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
public override void Initialize()
{
@@ -123,11 +125,7 @@ namespace Content.Server.Traitor.Uplink
return;
}
if (EntityManager.TryGetComponent(player, out HandsComponent? hands) &&
EntityManager.TryGetComponent(entity.Value, out SharedItemComponent? item))
{
hands.PutInHandOrDrop(item);
}
_handsSystem.PickupOrDrop(player, entity.Value);
SoundSystem.Play(Filter.SinglePlayer(message.Session), uplink.BuySuccessSound.GetSound(),
uplink.Owner, AudioParams.Default.WithVolume(-8f));
@@ -149,8 +147,7 @@ namespace Content.Server.Traitor.Uplink
return;
// try to put it into players hands
if (EntityManager.TryGetComponent(player, out SharedHandsComponent? hands))
hands.TryPutInAnyHand(tcUid.Value);
_handsSystem.PickupOrDrop(player, tcUid.Value);
// play buying sound
SoundSystem.Play(Filter.SinglePlayer(args.Session), uplink.BuySuccessSound.GetSound(),
@@ -217,14 +214,10 @@ namespace Content.Server.Traitor.Uplink
}
// Also check hands
if (EntityManager.TryGetComponent(user, out HandsComponent? hands))
foreach (var item in _handsSystem.EnumerateHeld(user))
{
var heldItems = hands.GetAllHeldItems();
foreach (var item in heldItems)
{
if (EntityManager.HasComponent<PDAComponent>(item.Owner))
return item.Owner;
}
if (HasComp<PDAComponent>(item))
return item;
}
return null;

View File

@@ -102,7 +102,7 @@ namespace Content.Server.Verbs
// first get the held item. again.
EntityUid? holding = null;
if (TryComp(user, out SharedHandsComponent? hands) &&
hands.TryGetActiveHeldEntity(out var heldEntity))
hands.ActiveHandEntity is EntityUid heldEntity)
{
holding = heldEntity;
}

View File

@@ -152,17 +152,12 @@ public sealed partial class GunSystem
return false;
}
if (TryComp(ammo, out ItemComponent? item))
{
if (!handsComponent.CanPutInHand(item))
if (!_handsSystem.TryPickup(user, ammo, handsComp: handsComponent))
{
TryInsertAmmo(user, ammo, ammoBox);
return false;
}
handsComponent.PutInHand(item);
}
UpdateAmmoBoxAppearance(ammoBox.Owner, ammoBox);
return true;
}

View File

@@ -36,7 +36,7 @@ public sealed partial class GunSystem
{
if (!TryComp(gun.Owner, out ServerRangedBarrelComponent? barrel)) return;
if (!TryComp(user, out HandsComponent? hands) || hands.GetActiveHand()?.HeldEntity != gun.Owner) return;
if (!TryComp(user, out HandsComponent? hands) || hands.ActiveHand?.HeldEntity != gun.Owner) return;
if (!TryComp(user, out CombatModeComponent? combat) ||
!combat.IsInCombatMode ||

View File

@@ -28,8 +28,8 @@ public sealed partial class GunSystem
if (args.Hands == null ||
!args.CanAccess ||
!args.CanInteract ||
!component.HasMagazine ||
!_blocker.CanPickup(args.User))
component.MagazineContainer.ContainedEntity is not EntityUid mag ||
!_blocker.CanPickup(args.User, mag))
return;
if (component.MagNeedsOpenBolt && !component.BoltOpen)
@@ -37,7 +37,7 @@ public sealed partial class GunSystem
AlternativeVerb verb = new()
{
Text = MetaData(component.MagazineContainer.ContainedEntity!.Value).EntityName,
Text = MetaData(mag).EntityName,
Category = VerbCategory.Eject,
Act = () => RemoveMagazine(args.User, component)
};
@@ -325,10 +325,7 @@ public sealed partial class GunSystem
component.MagazineContainer.Remove(mag.Value);
SoundSystem.Play(Filter.Pvs(component.Owner), component.SoundMagEject.GetSound(), component.Owner, AudioParams.Default.WithVolume(-2));
if (TryComp(user, out HandsComponent? handsComponent))
{
handsComponent.PutInHandOrDrop(EntityManager.GetComponent<SharedItemComponent>(mag.Value));
}
_handsSystem.PickupOrDrop(user, mag.Value);
component.Dirty(EntityManager);
UpdateMagazineAppearance(component);

View File

@@ -70,16 +70,8 @@ public sealed partial class GunSystem
if (TakeAmmo(component) is not {Valid: true} ammo)
return;
var itemComponent = EntityManager.GetComponent<SharedItemComponent>(ammo);
if (!handsComponent.CanPutInHand(itemComponent))
{
Transform(ammo).Coordinates = Transform(args.User).Coordinates;
_handsSystem.PickupOrDrop(args.User, ammo, handsComp: handsComponent);
EjectCasing(ammo);
}
else
{
handsComponent.PutInHand(itemComponent);
}
args.Handled = true;
}

View File

@@ -75,15 +75,10 @@ public sealed partial class GunSystem
return;
}
var itemComponent = EntityManager.GetComponent<SharedItemComponent>(ammo.Value);
if (!handsComponent.CanPutInHand(itemComponent))
if (!_handsSystem.TryPickup(args.User, ammo.Value, handsComp: handsComponent))
{
EjectCasing(ammo.Value);
}
else
{
handsComponent.PutInHand(itemComponent);
}
UpdateSpeedLoaderAppearance(component);
args.Handled = true;

View File

@@ -11,6 +11,7 @@ using Content.Shared.ActionBlocker;
using Content.Shared.Camera;
using Content.Shared.Damage;
using Content.Shared.Examine;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Popups;
@@ -47,6 +48,7 @@ public sealed partial class GunSystem : EntitySystem
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly StunSystem _stun = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
/// <summary>
/// How many sounds are allowed to be played on ejecting multiple casings.
@@ -141,7 +143,7 @@ public sealed partial class GunSystem : EntitySystem
return;
// TODO: Not exactly robust
var gun = handsComponent.GetActiveHand()?.HeldEntity;
var gun = handsComponent.ActiveHand?.HeldEntity;
if (gun == null || !TryComp(gun, out ServerRangedWeaponComponent? weapon))
return;

View File

@@ -4,15 +4,15 @@ using Content.Server.Hands.Systems;
using Content.Server.Weapon.Melee;
using Content.Server.Wieldable.Components;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction.Events;
using Content.Shared.Item;
using Content.Shared.Popups;
using Content.Shared.Verbs;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Player;
using System.Linq;
namespace Content.Server.Wieldable
{
@@ -20,6 +20,7 @@ namespace Content.Server.Wieldable
{
[Dependency] private readonly DoAfterSystem _doAfter = default!;
[Dependency] private readonly HandVirtualItemSystem _virtualItemSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
public override void Initialize()
{
@@ -75,7 +76,8 @@ namespace Content.Server.Wieldable
return false;
}
if (hands.GetFreeHands() < component.FreeHandsRequired)
if (hands.CountFreeHands()
< component.FreeHandsRequired)
{
// TODO FLUENT need function to change 'hands' to 'hand' when there's only 1 required
if (!quiet)
@@ -89,7 +91,7 @@ namespace Content.Server.Wieldable
}
// Is it.. actually in one of their hands?
if (!hands.TryGetHandHoldingEntity(uid, out _))
if (!_handsSystem.IsHolding(user, uid, out _, hands))
{
if (!quiet)
{

View File

@@ -338,7 +338,7 @@ namespace Content.Server.WireHacking
return false;
}
if (handsComponent.GetActiveHand()?.HeldEntity is not { Valid: true } activeHandEntity ||
if (handsComponent.ActiveHand?.HeldEntity is not { Valid: true } activeHandEntity ||
!_entities.TryGetComponent(activeHandEntity, out tool))
{
return false;

View File

@@ -1,7 +1,6 @@
using Content.Server.Clothing.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Robust.Shared.Random;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
@@ -9,6 +8,7 @@ namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
public sealed class SpawnArtifactSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
public override void Initialize()
{
@@ -40,12 +40,7 @@ public sealed class SpawnArtifactSystem : EntitySystem
// if there is an user - try to put spawned item in their hands
// doesn't work for spawners
if (args.Activator != null &&
EntityManager.TryGetComponent(args.Activator.Value, out SharedHandsComponent? hands) &&
EntityManager.HasComponent<ItemComponent>(spawned))
{
hands.TryPutInAnyHand(spawned);
}
_handsSystem.PickupOrDrop(args.Activator, spawned);
}
private void ChooseRandomPrototype(EntityUid uid, SpawnArtifactComponent? component = null)

View File

@@ -1,16 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared.Hands.Components;
using Content.Shared.Inventory;
using Content.Shared.Emag.Systems;
using Content.Shared.PDA;
using Content.Shared.Access.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Prototypes;
using Content.Shared.Hands.EntitySystems;
namespace Content.Shared.Access.Systems
{
@@ -18,6 +13,7 @@ namespace Content.Shared.Access.Systems
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
public override void Initialize()
{
@@ -54,7 +50,7 @@ namespace Content.Shared.Access.Systems
/// <remarks>
/// If no access is found, an empty set is used instead.
/// </remarks>
/// <param name="entity">The entity to be searched for access.</param>
/// <param name="entity">The entity to bor access.</param>
public bool IsAllowed(AccessReaderComponent reader, EntityUid entity)
{
var tags = FindAccessTags(entity);
@@ -78,15 +74,11 @@ namespace Content.Shared.Access.Systems
if (FindAccessTagsItem(uid, out var tags))
return tags;
// maybe access component inside its hands?
if (EntityManager.TryGetComponent(uid, out SharedHandsComponent? hands))
{
if (hands.TryGetActiveHeldEntity(out var heldItem) &&
FindAccessTagsItem(heldItem.Value, out tags))
foreach (var item in _handsSystem.EnumerateHeld(uid))
{
if (FindAccessTagsItem(item, out tags))
return tags;
}
}
// maybe its inside an inventory slot?
if (_inventorySystem.TryGetSlotEntity(uid, "id", out var idUid) && FindAccessTagsItem(idUid.Value, out tags))

View File

@@ -95,12 +95,18 @@ namespace Content.Shared.ActionBlocker
return !ev.Cancelled;
}
public bool CanPickup(EntityUid uid)
public bool CanPickup(EntityUid user, EntityUid item)
{
var ev = new PickupAttemptEvent(uid);
RaiseLocalEvent(uid, ev);
var userEv = new PickupAttemptEvent(user, item);
RaiseLocalEvent(user, userEv, false);
if (userEv.Cancelled)
return false;
var itemEv = new GettingPickedUpAttemptEvent(user, item);
RaiseLocalEvent(item, itemEv, false);
return !itemEv.Cancelled;
return !ev.Cancelled;
}
public bool CanEmote(EntityUid uid)

View File

@@ -1,6 +1,7 @@
using Content.Shared.ActionBlocker;
using Content.Shared.Acts;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Popups;
@@ -8,14 +9,9 @@ using Content.Shared.Sound;
using Content.Shared.Verbs;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Player;
using Robust.Shared.Timing;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace Content.Shared.Containers.ItemSlots
@@ -27,6 +23,7 @@ namespace Content.Shared.Containers.ItemSlots
{
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
public override void Initialize()
@@ -175,11 +172,11 @@ namespace Content.Shared.Containers.ItemSlots
continue;
// Drop the held item onto the floor. Return if the user cannot drop.
if (!hands.Drop(args.Used))
if (!_handsSystem.TryDrop(args.User, args.Used, handsComp: hands))
return;
if (slot.Item != null)
hands.TryPutInAnyHand(slot.Item.Value);
_handsSystem.TryPickupAnyHand(args.User, slot.Item.Value, handsComp: hands);
Insert(uid, slot, args.Used, args.User, excludeUserAudio: args.Predicted);
args.Handled = true;
@@ -287,18 +284,17 @@ namespace Content.Shared.Containers.ItemSlots
if (!Resolve(user, ref hands, false))
return false;
if (!hands.TryGetActiveHeldEntity(out var item))
if (hands.ActiveHand?.HeldEntity is not EntityUid held)
return false;
var heldItem = item.Value;
if (!CanInsert(uid, item.Value, slot))
if (!CanInsert(uid, held, slot))
return false;
// hands.Drop(item) checks CanDrop action blocker
if (hands.Drop(heldItem))
if (_handsSystem.TryDrop(user, hands.ActiveHand))
return false;
Insert(uid, slot, heldItem, user);
Insert(uid, slot, held, user);
return true;
}
#endregion
@@ -374,8 +370,8 @@ namespace Content.Shared.Containers.ItemSlots
if (!TryEject(uid, slot, user, out var item, excludeUserAudio))
return false;
if (user != null && EntityManager.TryGetComponent(user.Value, out SharedHandsComponent? hands))
hands.PutInHand(item.Value);
if (user != null)
_handsSystem.PickupOrDrop(user.Value, item.Value);
return true;
}
@@ -384,25 +380,27 @@ namespace Content.Shared.Containers.ItemSlots
#region Verbs
private void AddEjectVerbs(EntityUid uid, ItemSlotsComponent itemSlots, GetVerbsEvent<AlternativeVerb> args)
{
if (args.Hands == null || !args.CanAccess ||!args.CanInteract ||
!_actionBlockerSystem.CanPickup(args.User))
if (args.Hands == null || !args.CanAccess ||!args.CanInteract)
{
return;
}
foreach (var slot in itemSlots.Slots.Values)
{
if (!CanEject(slot))
continue;
if (slot.EjectOnInteract)
// For this item slot, ejecting/inserting is a primary interaction. Instead of an eject category
// alt-click verb, there will be a "Take item" primary interaction verb.
continue;
if (!CanEject(slot))
continue;
if (!_actionBlockerSystem.CanPickup(args.User, slot.Item!.Value))
continue;
var verbSubject = slot.Name != string.Empty
? Loc.GetString(slot.Name)
: EntityManager.GetComponent<MetaDataComponent>(slot.Item!.Value).EntityName ?? string.Empty;
: EntityManager.GetComponent<MetaDataComponent>(slot.Item.Value).EntityName ?? string.Empty;
AlternativeVerb verb = new();
verb.IconEntity = slot.Item;
@@ -428,13 +426,14 @@ namespace Content.Shared.Containers.ItemSlots
return;
// If there are any slots that eject on left-click, add a "Take <item>" verb.
if (_actionBlockerSystem.CanPickup(args.User))
{
foreach (var slot in itemSlots.Slots.Values)
{
if (!slot.EjectOnInteract || !CanEject(slot))
continue;
if (!_actionBlockerSystem.CanPickup(args.User, slot.Item!.Value))
continue;
var verbSubject = slot.Name != string.Empty
? Loc.GetString(slot.Name)
: EntityManager.GetComponent<MetaDataComponent>(slot.Item!.Value).EntityName ?? string.Empty;
@@ -450,7 +449,6 @@ namespace Content.Shared.Containers.ItemSlots
args.Verbs.Add(takeVerb);
}
}
// Next, add the insert-item verbs
if (args.Using == null || !_actionBlockerSystem.CanDrop(args.User))

View File

@@ -0,0 +1,34 @@
using System.Linq;
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 SharedHandsComponent 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 SharedHandsComponent component) => component.Hands.Values.Count(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<Hand> GetFreeHands(this SharedHandsComponent 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 SharedHandsComponent component) => GetFreeHands(component).Select(hand => hand.Name);
}

View File

@@ -1,39 +1,33 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared.ActionBlocker;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Item;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using static Robust.Shared.GameObjects.SharedSpriteComponent;
namespace Content.Shared.Hands.Components
{
namespace Content.Shared.Hands.Components;
[NetworkedComponent]
public abstract class SharedHandsComponent : Component
{
[Dependency] private readonly IEntityManager _entMan = default!;
/// <summary>
/// The name of the currently active hand.
/// The currently active hand.
/// </summary>
[ViewVariables]
public string? ActiveHand;
public Hand? ActiveHand;
/// <summary>
/// The item currently held in the active hand.
/// </summary>
[ViewVariables]
public EntityUid? ActiveHandEntity => ActiveHand?.HeldEntity;
[ViewVariables]
public List<Hand> Hands = new();
public Dictionary<string, Hand> Hands = new();
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>
public List<string> SortedHands = new();
/// <summary>
/// The amount of throw impulse per distance the player is from the throw target.
@@ -48,667 +42,6 @@ namespace Content.Shared.Hands.Components
[DataField("throwRange")]
[ViewVariables(VVAccess.ReadWrite)]
public float ThrowRange { get; set; } = 8f;
private bool PlayerCanDrop => EntitySystem.Get<ActionBlockerSystem>().CanDrop(Owner);
private bool PlayerCanPickup => EntitySystem.Get<ActionBlockerSystem>().CanPickup(Owner);
public void AddHand(string handName, HandLocation handLocation)
{
if (HasHand(handName))
return;
var container = Owner.EnsureContainer<ContainerSlot>(handName);
container.OccludesLight = false;
Hands.Add(new Hand(handName, handLocation, container));
if (ActiveHand == null)
EntitySystem.Get<SharedHandsSystem>().TrySetActiveHand(Owner, handName, this);
HandCountChanged();
Dirty();
}
public void RemoveHand(string handName)
{
if (!TryGetHand(handName, out var hand))
return;
RemoveHand(hand);
}
private void RemoveHand(Hand hand)
{
DropHeldEntityToFloor(hand);
hand.Container?.Shutdown();
Hands.Remove(hand);
if (ActiveHand == hand.Name)
EntitySystem.Get<SharedHandsSystem>().TrySetActiveHand(Owner, Hands.FirstOrDefault()?.Name, this);
HandCountChanged();
Dirty();
}
public Hand? GetActiveHand()
{
if (ActiveHand == null)
return null;
return GetHandOrNull(ActiveHand);
}
public bool HasHand(string handName)
{
return TryGetHand(handName, out _);
}
public Hand? GetHandOrNull(string handName)
{
return TryGetHand(handName, out var hand) ? hand : null;
}
public Hand GetHand(string handName)
{
if (!TryGetHand(handName, out var hand))
throw new KeyNotFoundException($"Unable to find hand with name {handName}");
return hand;
}
public bool TryGetHand(string handName, [NotNullWhen(true)] out Hand? foundHand)
{
foreach (var hand in Hands)
{
if (hand.Name == handName)
{
foundHand = hand;
return true;
};
}
foundHand = null;
return false;
}
public bool TryGetActiveHand([NotNullWhen(true)] out Hand? activeHand)
{
activeHand = GetActiveHand();
return activeHand != null;
}
#region Held Entities
public bool ActiveHandIsHoldingEntity()
{
if (!TryGetActiveHand(out var hand))
return false;
return hand.HeldEntity != null;
}
public bool TryGetHeldEntity(string handName, [NotNullWhen(true)] out EntityUid? heldEntity)
{
heldEntity = null;
if (!TryGetHand(handName, out var hand))
return false;
heldEntity = hand.HeldEntity;
return heldEntity != null;
}
public bool TryGetActiveHeldEntity([NotNullWhen(true)] out EntityUid? heldEntity)
{
heldEntity = GetActiveHand()?.HeldEntity;
return heldEntity != null;
}
public bool IsHolding(EntityUid entity)
{
foreach (var hand in Hands)
{
if (hand.HeldEntity == entity)
return true;
}
return false;
}
public IEnumerable<EntityUid> GetAllHeldEntities()
{
foreach (var hand in Hands)
{
if (hand.HeldEntity != null)
yield return hand.HeldEntity.Value;
}
}
/// <summary>
/// Returns the number of hands that have no items in them.
/// </summary>
/// <returns></returns>
public int GetFreeHands()
{
int acc = 0;
foreach (var hand in Hands)
{
if (hand.HeldEntity == null)
acc += 1;
}
return acc;
}
public bool TryGetHandHoldingEntity(EntityUid entity, [NotNullWhen(true)] out Hand? handFound)
{
handFound = null;
foreach (var hand in Hands)
{
if (hand.HeldEntity == entity)
{
handFound = hand;
return true;
}
}
return false;
}
#endregion
#region Dropping
/// <summary>
/// Checks all the conditions relevant to a player being able to drop an item.
/// </summary>
public bool CanDrop(string handName, bool checkActionBlocker = true)
{
if (!TryGetHand(handName, out var hand))
return false;
if (!CanRemoveHeldEntityFromHand(hand))
return false;
if (checkActionBlocker && !PlayerCanDrop)
return false;
return true;
}
/// <summary>
/// Tries to drop the contents of the active hand to the target location.
/// </summary>
public bool TryDropActiveHand(EntityCoordinates targetDropLocation, bool doMobChecks = true)
{
if (!TryGetActiveHand(out var hand))
return false;
return TryDropHeldEntity(hand, targetDropLocation, doMobChecks);
}
/// <summary>
/// Tries to drop the contents of a hand to the target location.
/// </summary>
public bool TryDropHand(string handName, EntityCoordinates targetDropLocation, bool checkActionBlocker = true)
{
if (!TryGetHand(handName, out var hand))
return false;
return TryDropHeldEntity(hand, targetDropLocation, checkActionBlocker);
}
/// <summary>
/// Tries to drop a held entity to the target location.
/// </summary>
public bool TryDropEntity(EntityUid entity, EntityCoordinates coords, bool doMobChecks = true)
{
if (!TryGetHandHoldingEntity(entity, out var hand))
return false;
return TryDropHeldEntity(hand, coords, doMobChecks);
}
/// <summary>
/// Attempts to move the contents of a hand into a container that is not another hand, without dropping it on the floor inbetween.
/// </summary>
public bool TryPutHandIntoContainer(string handName, BaseContainer targetContainer, bool checkActionBlocker = true)
{
if (!TryGetHand(handName, out var hand))
return false;
if (!CanPutHeldEntityIntoContainer(hand, targetContainer, checkActionBlocker))
return false;
PutHeldEntityIntoContainer(hand, targetContainer);
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 Drop(EntityUid entity, BaseContainer targetContainer, bool checkActionBlocker = true)
{
if (!TryGetHandHoldingEntity(entity, out var hand))
return false;
if (!CanPutHeldEntityIntoContainer(hand, targetContainer, checkActionBlocker))
return false;
PutHeldEntityIntoContainer(hand, targetContainer);
return true;
}
/// <summary>
/// Tries to drop the contents of a hand directly under the player.
/// </summary>
public bool Drop(string handName, bool checkActionBlocker = true)
{
if (!TryGetHand(handName, out var hand))
return false;
return TryDropHeldEntity(hand, _entMan.GetComponent<TransformComponent>(Owner).Coordinates, checkActionBlocker);
}
/// <summary>
/// Tries to drop a held entity directly under the player.
/// </summary>
public bool Drop(EntityUid entity, bool checkActionBlocker = true)
{
if (!TryGetHandHoldingEntity(entity, out var hand))
return false;
return TryDropHeldEntity(hand, _entMan.GetComponent<TransformComponent>(Owner).Coordinates, checkActionBlocker);
}
/// <summary>
/// Tries to remove the item in the active hand, without dropping it.
/// For transferring the held item to another location, like an inventory slot,
/// which shouldn't trigger the drop interaction
/// </summary>
public bool TryDropNoInteraction()
{
if (!TryGetActiveHand(out var hand))
return false;
if (!CanRemoveHeldEntityFromHand(hand))
return false;
EntitySystem.Get<SharedHandsSystem>().RemoveHeldEntityFromHand(Owner, hand, this);
return true;
}
/// <summary>
/// Checks if the contents of a hand is able to be removed from its container.
/// </summary>
private bool CanRemoveHeldEntityFromHand(Hand hand)
{
if (hand.HeldEntity == null)
return false;
if (!hand.Container!.CanRemove(hand.HeldEntity.Value))
return false;
return true;
}
/// <summary>
/// Drops a hands contents to the target location.
/// </summary>
public void DropHeldEntity(Hand hand, EntityCoordinates targetDropLocation)
{
if (hand.HeldEntity == null)
return;
var heldEntity = hand.HeldEntity.Value;
EntitySystem.Get<SharedHandsSystem>().RemoveHeldEntityFromHand(Owner, hand, this);
EntitySystem.Get<SharedInteractionSystem>().DroppedInteraction(Owner, heldEntity);
_entMan.GetComponent<TransformComponent>(heldEntity).WorldPosition = GetFinalDropCoordinates(targetDropLocation);
}
/// <summary>
/// Calculates the final location a dropped item will end up at, accounting for max drop range and collision along the targeted drop path.
/// </summary>
private Vector2 GetFinalDropCoordinates(EntityCoordinates targetCoords)
{
var origin = _entMan.GetComponent<TransformComponent>(Owner).MapPosition;
var target = targetCoords.ToMap(_entMan);
var dropVector = target.Position - origin.Position;
var requestedDropDistance = dropVector.Length;
if (dropVector.Length > SharedInteractionSystem.InteractionRange)
{
dropVector = dropVector.Normalized * SharedInteractionSystem.InteractionRange;
target = new MapCoordinates(origin.Position + dropVector, target.MapId);
}
var dropLength = EntitySystem.Get<SharedInteractionSystem>().UnobstructedDistance(origin, target, predicate: e => e == Owner);
if (dropLength < requestedDropDistance)
return origin.Position + dropVector.Normalized * dropLength;
return target.Position;
}
/// <summary>
/// Tries to drop a hands contents to the target location.
/// </summary>
private bool TryDropHeldEntity(Hand hand, EntityCoordinates location, bool checkActionBlocker)
{
if (!CanRemoveHeldEntityFromHand(hand))
return false;
if (checkActionBlocker && !PlayerCanDrop)
return false;
DropHeldEntity(hand, location);
return true;
}
/// <summary>
/// Drops the contents of a hand directly under the player.
/// </summary>
private void DropHeldEntityToFloor(Hand hand)
{
DropHeldEntity(hand, _entMan.GetComponent<TransformComponent>(Owner).Coordinates);
}
private bool CanPutHeldEntityIntoContainer(Hand hand, IContainer targetContainer, bool checkActionBlocker)
{
if (hand.HeldEntity == null)
return false;
var heldEntity = hand.HeldEntity.Value;
if (checkActionBlocker && !PlayerCanDrop)
return false;
if (!targetContainer.CanInsert(heldEntity))
return false;
return true;
}
/// <summary>
/// For putting the contents of a hand into a container that is not another hand.
/// </summary>
private void PutHeldEntityIntoContainer(Hand hand, IContainer targetContainer)
{
if (hand.HeldEntity == null)
return;
var heldEntity = hand.HeldEntity.Value;
EntitySystem.Get<SharedHandsSystem>().RemoveHeldEntityFromHand(Owner, hand, this);
if (!targetContainer.Insert(heldEntity))
{
Logger.Error($"{nameof(SharedHandsComponent)} on {Owner} could not insert {heldEntity} into {targetContainer}.");
return;
}
}
#endregion
#region Pickup
public bool CanPickupEntity(string handName, EntityUid entity, bool checkActionBlocker = true)
{
if (!TryGetHand(handName, out var hand))
return false;
if (checkActionBlocker && !PlayerCanPickup)
return false;
if (!CanInsertEntityIntoHand(hand, entity))
return false;
return true;
}
public bool CanPickupEntityToActiveHand(EntityUid entity, bool checkActionBlocker = true)
{
return ActiveHand != null && CanPickupEntity(ActiveHand, entity, checkActionBlocker);
}
/// <summary>
/// Tries to pick up an entity to a specific hand.
/// </summary>
public bool TryPickupEntity(string handName, EntityUid entity, bool checkActionBlocker = true, bool animateUser = false)
{
if (!TryGetHand(handName, out var hand))
return false;
return TryPickupEntity(hand, entity, checkActionBlocker, animateUser);
}
public bool TryPickupEntityToActiveHand(EntityUid entity, bool checkActionBlocker = true, bool animateUser = false)
{
return ActiveHand != null && TryPickupEntity(ActiveHand, entity, checkActionBlocker, animateUser);
}
/// <summary>
/// Checks if an entity can be put into a hand's container.
/// </summary>
protected bool CanInsertEntityIntoHand(Hand hand, EntityUid entity)
{
var handContainer = hand.Container;
if (handContainer == null) return false;
if (!_entMan.HasComponent<SharedItemComponent>(entity))
return false;
if (_entMan.TryGetComponent(entity, out IPhysBody? physics) && physics.BodyType == BodyType.Static)
return false;
if (!handContainer.CanInsert(entity)) return false;
var @event = new AttemptItemPickupEvent();
_entMan.EventBus.RaiseLocalEvent(entity, @event);
if (@event.Cancelled) return false;
return true;
}
private bool TryPickupEntity(Hand hand, EntityUid entity, bool checkActionBlocker = true, bool animateUser = false)
{
if (!CanInsertEntityIntoHand(hand, entity))
return false;
if (checkActionBlocker && !PlayerCanPickup)
return false;
// animation
var handSys = EntitySystem.Get<SharedHandsSystem>();
var coordinateEntity = _entMan.GetComponent<TransformComponent>(Owner).Parent?.Owner ?? Owner;
var initialPosition = EntityCoordinates.FromMap(coordinateEntity, _entMan.GetComponent<TransformComponent>(entity).MapPosition);
var finalPosition = _entMan.GetComponent<TransformComponent>(Owner).LocalPosition;
handSys.PickupAnimation(entity, initialPosition, finalPosition, animateUser ? null : Owner);
handSys.PutEntityIntoHand(Owner, hand, entity, this);
return true;
}
#endregion
#region Hand Interactions
/// <summary>
/// Get the name of the hand that a swap hands would result in.
/// </summary>
public bool TryGetSwapHandsResult([NotNullWhen(true)] out string? nextHand)
{
nextHand = null;
if (!TryGetActiveHand(out var activeHand) || Hands.Count == 1)
return false;
var newActiveIndex = Hands.IndexOf(activeHand) + 1;
var finalHandIndex = Hands.Count - 1;
if (newActiveIndex > finalHandIndex)
newActiveIndex = 0;
nextHand = Hands[newActiveIndex].Name;
return true;
}
/// <summary>
/// Attempts to interact with the item in a hand using the active held item.
/// </summary>
public void InteractHandWithActiveHand(string handName)
{
if (!TryGetActiveHeldEntity(out var activeHeldEntity))
return;
if (!TryGetHeldEntity(handName, out var heldEntity))
return;
if (activeHeldEntity == heldEntity)
return;
EntitySystem.Get<SharedInteractionSystem>()
.InteractUsing(Owner, activeHeldEntity.Value, heldEntity.Value, EntityCoordinates.Invalid);
}
public void ActivateItem(bool altInteract = false)
{
if (!TryGetActiveHeldEntity(out var heldEntity))
return;
var sys = EntitySystem.Get<SharedInteractionSystem>();
if (altInteract)
sys.AltInteract(Owner, heldEntity.Value);
else
sys.UseInHandInteraction(Owner, heldEntity.Value);
}
public void ActivateHeldEntity(string handName)
{
if (!TryGetHeldEntity(handName, out var heldEntity))
return;
EntitySystem.Get<SharedInteractionSystem>()
.InteractionActivate(Owner, heldEntity.Value);
}
/// <summary>
/// Moves an entity from one hand to the active hand.
/// </summary>
public bool TryMoveHeldEntityToActiveHand(string handName, bool checkActionBlocker = true)
{
if (!TryGetHand(handName, out var hand) || !TryGetActiveHand(out var activeHand))
return false;
if (!TryGetHeldEntity(handName, out var heldEntity))
return false;
if (!CanInsertEntityIntoHand(activeHand, heldEntity.Value) || !CanRemoveHeldEntityFromHand(hand))
return false;
if (checkActionBlocker && (!PlayerCanDrop || !PlayerCanPickup))
return false;
EntitySystem.Get<SharedHandsSystem>().RemoveHeldEntityFromHand(Owner, hand, this);
EntitySystem.Get<SharedHandsSystem>().PutEntityIntoHand(Owner, activeHand, heldEntity.Value, this);
return true;
}
#endregion
private void HandCountChanged()
{
_entMan.EventBus.RaiseEvent(EventSource.Local, new HandCountChangedEvent(Owner));
}
/// <summary>
/// Tries to pick up an entity into the active hand. If it cannot, tries to pick up the entity into each other hand.
/// </summary>
public bool PutInHand(SharedItemComponent item, bool checkActionBlocker = true)
{
return PutInHand(item.Owner, checkActionBlocker);
}
/// <summary>
/// Puts an item any hand, preferring the active hand, or puts it on the floor under the player.
/// </summary>
public void PutInHandOrDrop(EntityUid entity, bool checkActionBlocker = true)
{
if (PutInHand(entity, checkActionBlocker))
return;
_entMan.GetComponent<TransformComponent>(entity).AttachParentToContainerOrGrid(_entMan);
_entMan.EventBus.RaiseLocalEvent(entity, new DroppedEvent(Owner));
}
public void PutInHandOrDrop(SharedItemComponent item, bool checkActionBlocker = true)
{
PutInHandOrDrop(item.Owner, checkActionBlocker);
}
/// <summary>
/// Tries to pick up an entity into the active hand. If it cannot, tries to pick up the entity into each other hand.
/// </summary>
public bool PutInHand(EntityUid entity, bool checkActionBlocker = true)
{
return PutInHand(entity, GetActiveHand(), checkActionBlocker);
}
/// <summary>
/// Tries to pick up an entity into the priority hand, if provided. If it cannot, tries to pick up the entity into each other hand.
/// </summary>
public bool TryPutInAnyHand(EntityUid entity, string? priorityHandName = null, bool checkActionBlocker = true)
{
Hand? priorityHand = null;
if (priorityHandName != null)
priorityHand = GetHandOrNull(priorityHandName);
return PutInHand(entity, priorityHand, checkActionBlocker);
}
/// <summary>
/// Tries to pick up an entity into the priority hand, if provided. If it cannot, tries to pick up the entity into each other hand.
/// </summary>
private bool PutInHand(EntityUid entity, Hand? priorityHand = null, bool checkActionBlocker = true)
{
if (priorityHand != null)
{
if (TryPickupEntity(priorityHand, entity, checkActionBlocker))
return true;
}
foreach (var hand in Hands)
{
if (TryPickupEntity(hand, entity, checkActionBlocker))
return true;
}
return false;
}
/// <summary>
/// Checks if any hand can pick up an item.
/// </summary>
public bool CanPutInHand(SharedItemComponent item, bool mobCheck = true)
{
var entity = item.Owner;
if (mobCheck && !PlayerCanPickup)
return false;
foreach (var hand in Hands)
{
if (CanInsertEntityIntoHand(hand, entity))
return true;
}
return false;
}
}
[Serializable, NetSerializable]
@@ -743,63 +76,15 @@ namespace Content.Shared.Hands.Components
[Serializable, NetSerializable]
public sealed class HandsComponentState : ComponentState
{
public List<Hand> Hands { get; }
public string? ActiveHand { get; }
public readonly List<Hand> Hands;
public readonly List<string> HandNames;
public readonly string? ActiveHand;
public HandsComponentState(List<Hand> hands, string? activeHand = null)
public HandsComponentState(SharedHandsComponent handComp)
{
Hands = hands;
ActiveHand = activeHand;
}
}
/// <summary>
/// A message that calls the use interaction on an item in hand, presumed for now the interaction will occur only on the active hand.
/// </summary>
[Serializable, NetSerializable]
public sealed class UseInHandMsg : EntityEventArgs
{
}
/// <summary>
/// A message that calls the activate interaction on the item in the specified hand.
/// </summary>
[Serializable, NetSerializable]
public sealed class ActivateInHandMsg : EntityEventArgs
{
public string HandName { get; }
public ActivateInHandMsg(string handName)
{
HandName = handName;
}
}
/// <summary>
/// Uses the item in the active hand on the item in the specified hand.
/// </summary>
[Serializable, NetSerializable]
public sealed class ClientInteractUsingInHandMsg : EntityEventArgs
{
public string HandName { get; }
public ClientInteractUsingInHandMsg(string handName)
{
HandName = handName;
}
}
/// <summary>
/// Moves an item from one hand to the active hand.
/// </summary>
[Serializable, NetSerializable]
public sealed class MoveItemFromHandMsg : EntityEventArgs
{
public string HandName { get; }
public MoveItemFromHandMsg(string handName)
{
HandName = handName;
Hands = new(handComp.Hands.Values);
HandNames = handComp.SortedHands;
ActiveHand = handComp.ActiveHand?.Name;
}
}
@@ -812,14 +97,3 @@ namespace Content.Shared.Hands.Components
Middle,
Right
}
public sealed class HandCountChangedEvent : EntityEventArgs
{
public HandCountChangedEvent(EntityUid sender)
{
Sender = sender;
}
public EntityUid Sender { get; }
}
}

View File

@@ -0,0 +1,39 @@
using Content.Shared.Hands.Components;
using System.Diagnostics.CodeAnalysis;
namespace Content.Shared.Hands.EntitySystems;
// These functions are mostly unused except for some AI operator stuff
// Nothing stops them from being used in general. If they ever get used elsewhere, then this file probably needs to be renamed.
public abstract partial class SharedHandsSystem : EntitySystem
{
public bool TrySelect(EntityUid uid, EntityUid? entity, SharedHandsComponent? handsComp = null)
{
if (!Resolve(uid, ref handsComp, false))
return false;
if (!IsHolding(uid, entity, out var hand, handsComp))
return false;
SetActiveHand(uid, hand, handsComp);
return true;
}
public bool TrySelect<TComponent>(EntityUid uid, [NotNullWhen(true)] out TComponent? component, SharedHandsComponent? handsComp = null) where TComponent : Component
{
component = null;
if (!Resolve(uid, ref handsComp, false))
return false;
foreach (var hand in handsComp.Hands.Values)
{
if (TryComp(hand.HeldEntity, out component))
return true;
}
return false;
}
public bool TrySelectEmptyHand(EntityUid uid, SharedHandsComponent? handsComp = null) => TrySelect(uid, null, handsComp);
}

View File

@@ -0,0 +1,159 @@
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
using Robust.Shared.Containers;
using Robust.Shared.Map;
namespace Content.Shared.Hands.EntitySystems;
public abstract partial class SharedHandsSystem : EntitySystem
{
/// <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)
{
if (hand.HeldEntity == null)
return false;
if (!hand.Container!.CanRemove(hand.HeldEntity.Value, EntityManager))
return false;
if (checkActionBlocker && !_actionBlocker.CanDrop(uid))
return false;
return true;
}
/// <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, SharedHandsComponent? handsComp = null)
{
if (!Resolve(uid, ref handsComp))
return false;
if (handsComp.ActiveHand == null)
return false;
return TryDrop(uid, handsComp.ActiveHand, targetDropLocation, checkActionBlocker, doDropInteraction, handsComp);
}
/// <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, SharedHandsComponent? handsComp = null)
{
if (!Resolve(uid, ref handsComp))
return false;
if (!IsHolding(uid, entity, out var hand, handsComp))
return false;
return TryDrop(uid, hand, targetDropLocation, checkActionBlocker, doDropInteraction, handsComp);
}
/// <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, SharedHandsComponent? handsComp = null)
{
if (!Resolve(uid, ref handsComp))
return false;
if (!CanDropHeld(uid, hand, checkActionBlocker))
return false;
var entity = hand.HeldEntity!.Value;
DoDrop(uid, hand, doDropInteraction: doDropInteraction, handsComp);
var xform = Transform(uid);
if (targetDropLocation == null)
{
// TODO recursively check upwards for containers
Transform(entity).AttachParentToContainerOrGrid(EntityManager);
return true;
}
var target = targetDropLocation.Value.ToMap(EntityManager);
Transform(entity).WorldPosition = GetFinalDropCoordinates(uid, xform.MapPosition, target);
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, SharedHandsComponent? handsComp = null)
{
if (!Resolve(uid, ref handsComp))
return false;
if (!IsHolding(uid, entity, out var hand, handsComp))
return false;
if (!CanDropHeld(uid, hand, checkActionBlocker))
return false;
if (!targetContainer.CanInsert(entity, EntityManager))
return false;
DoDrop(uid, hand, false, handsComp);
targetContainer.Insert(entity);
return true;
}
/// <summary>
/// Calculates the final location a dropped item will end up at, accounting for max drop range and collision along the targeted drop path.
/// </summary>
private Vector2 GetFinalDropCoordinates(EntityUid user, MapCoordinates origin, MapCoordinates target)
{
var dropVector = target.Position - origin.Position;
var requestedDropDistance = dropVector.Length;
if (dropVector.Length > SharedInteractionSystem.InteractionRange)
{
dropVector = dropVector.Normalized * SharedInteractionSystem.InteractionRange;
target = new MapCoordinates(origin.Position + dropVector, target.MapId);
}
var dropLength = _interactionSystem.UnobstructedDistance(origin, target, predicate: e => e == user);
if (dropLength < requestedDropDistance)
return origin.Position + dropVector.Normalized * dropLength;
return target.Position;
}
/// <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, SharedHandsComponent? handsComp = null)
{
if (!Resolve(uid, ref handsComp))
return;
if (hand.Container?.ContainedEntity == null)
return;
var entity = hand.Container.ContainedEntity.Value;
if (!hand.Container!.Remove(entity, EntityManager))
{
Logger.Error($"{nameof(SharedHandsComponent)} on {uid} could not remove {entity} from {hand.Container}.");
return;
}
Dirty(handsComp);
if (doDropInteraction)
_interactionSystem.DroppedInteraction(uid, entity);
var gotUnequipped = new GotUnequippedHandEvent(uid, entity, hand);
RaiseLocalEvent(entity, gotUnequipped, false);
var didUnequip = new DidUnequipHandEvent(uid, entity, hand);
RaiseLocalEvent(uid, didUnequip);
if (hand == handsComp.ActiveHand)
RaiseLocalEvent(entity, new HandDeselectedEvent(uid), false);
}
}

View File

@@ -0,0 +1,172 @@
using Content.Shared.Examine;
using Content.Shared.Hands.Components;
using Content.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.Map;
using Robust.Shared.Players;
namespace Content.Shared.Hands.EntitySystems;
public abstract partial class SharedHandsSystem : EntitySystem
{
private void InitializeInteractions()
{
SubscribeAllEvent<RequestSetHandEvent>(HandleSetHand);
SubscribeAllEvent<RequestActivateInHandEvent>(HandleActivateItemInHand);
SubscribeAllEvent<RequestHandInteractUsingEvent>(HandleInteractUsingInHand);
SubscribeAllEvent<RequestUseInHandEvent>(HandleUseInHand);
SubscribeAllEvent<RequestMoveHandItemEvent>(HandleMoveItemFromHand);
SubscribeLocalEvent<SharedHandsComponent, ExaminedEvent>(HandleExamined);
CommandBinds.Builder
.Bind(ContentKeyFunctions.UseItemInHand, InputCmdHandler.FromDelegate(HandleUseItem, handle: false))
.Bind(ContentKeyFunctions.AltUseItemInHand, InputCmdHandler.FromDelegate(HandleAltUseInHand, handle: false))
.Bind(ContentKeyFunctions.SwapHands, InputCmdHandler.FromDelegate(SwapHandsPressed, handle: false))
.Bind(ContentKeyFunctions.Drop, new PointerInputCmdHandler(DropPressed))
.Register<SharedHandsSystem>();
}
#region Event and Key-binding Handlers
private void HandleAltUseInHand(ICommonSession? session)
{
if (session?.AttachedEntity != null)
TryUseItemInHand(session.AttachedEntity.Value, true);
}
private void HandleUseItem(ICommonSession? session)
{
if (session?.AttachedEntity != null)
TryUseItemInHand(session.AttachedEntity.Value);
}
private void HandleMoveItemFromHand(RequestMoveHandItemEvent msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity != null)
TryMoveHeldEntityToActiveHand(args.SenderSession.AttachedEntity.Value, msg.HandName);
}
private void HandleUseInHand(RequestUseInHandEvent msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity != null)
TryUseItemInHand(args.SenderSession.AttachedEntity.Value);
}
private void HandleActivateItemInHand(RequestActivateInHandEvent msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity != null)
TryActivateItemInHand(args.SenderSession.AttachedEntity.Value);
}
private void HandleInteractUsingInHand(RequestHandInteractUsingEvent msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity != null)
TryInteractHandWithActiveHand(args.SenderSession.AttachedEntity.Value, msg.HandName);
}
private void SwapHandsPressed(ICommonSession? session)
{
if (!TryComp(session?.AttachedEntity, out SharedHandsComponent? component))
return;
if (component.ActiveHand == null || component.Hands.Count < 2)
return;
var newActiveIndex = component.SortedHands.IndexOf(component.ActiveHand.Name) + 1;
var nextHand = component.SortedHands[newActiveIndex % component.Hands.Count];
TrySetActiveHand(component.Owner, nextHand, component);
}
private bool DropPressed(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
{
if (TryComp(session?.AttachedEntity, out SharedHandsComponent? hands) && hands.ActiveHand != null)
TryDrop(session!.AttachedEntity!.Value, hands.ActiveHand, coords, handsComp: hands);
// always send to server.
return false;
}
#endregion
public bool TryActivateItemInHand(EntityUid uid, SharedHandsComponent? handsComp = null)
{
if (!Resolve(uid, ref handsComp, false))
return false;
if (handsComp.ActiveHandEntity == null)
return false;
return _interactionSystem.InteractionActivate(uid, handsComp.ActiveHandEntity.Value);
}
public bool TryInteractHandWithActiveHand(EntityUid uid, string handName, SharedHandsComponent? handsComp = null)
{
if (!Resolve(uid, ref handsComp, false))
return false;
if (handsComp.ActiveHandEntity == null)
return false;
if (!handsComp.Hands.TryGetValue(handName, out var hand))
return false;
if (hand.HeldEntity == null)
return false;
_interactionSystem.InteractUsing(uid, handsComp.ActiveHandEntity.Value, hand.HeldEntity.Value, Transform(hand.HeldEntity.Value).Coordinates);
return true;
}
public bool TryUseItemInHand(EntityUid uid, bool altInteract = false, SharedHandsComponent? handsComp = null)
{
if (!Resolve(uid, ref handsComp, false))
return false;
if (handsComp.ActiveHandEntity == null)
return false;
if (altInteract)
return _interactionSystem.AltInteract(uid, handsComp.ActiveHandEntity.Value);
else
return _interactionSystem.UseInHandInteraction(uid, handsComp.ActiveHandEntity.Value);
}
/// <summary>
/// Moves an entity from one hand to the active hand.
/// </summary>
public bool TryMoveHeldEntityToActiveHand(EntityUid uid, string handName, bool checkActionBlocker = true, SharedHandsComponent? handsComp = null)
{
if (!Resolve(uid, ref handsComp))
return false;
if (handsComp.ActiveHand == null || !handsComp.ActiveHand.IsEmpty)
return false;
if (!handsComp.Hands.TryGetValue(handName, out var hand))
return false;
if (!CanDropHeld(uid, hand, checkActionBlocker))
return false;
var entity = hand.HeldEntity!.Value;
if (!CanPickupToHand(uid, entity, handsComp.ActiveHand, checkActionBlocker, handsComp))
return false;
DoDrop(uid, hand, false, handsComp);
DoPickup(uid, handsComp.ActiveHand, entity, handsComp);
return true;
}
//TODO: Actually shows all items/clothing/etc.
private void HandleExamined(EntityUid uid, SharedHandsComponent handsComp, ExaminedEvent args)
{
foreach (var inhand in EnumerateHeld(uid, handsComp))
{
if (HasComp<HandVirtualItemComponent>(inhand))
continue;
args.PushText(Loc.GetString("comp-hands-examine", ("user", handsComp.Owner), ("item", inhand)));
}
}
}

View File

@@ -0,0 +1,161 @@
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;
namespace Content.Shared.Hands.EntitySystems;
public abstract partial class SharedHandsSystem : EntitySystem
{
/// <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, SharedHandsComponent? handsComp = null, SharedItemComponent? 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, animateUser, handsComp, item);
}
/// <summary>
/// Attempts to pick up an item into any empty hand. Prioritizes the currently active hand.
/// </summary>
/// <remarks>
/// If one empty hand fails to pick up the item, this will NOT check other hands. If ever hand-specific item
/// restrictions are added, there a might need to be a TryPickupAllHands or something like that.
/// </remarks>
public bool TryPickupAnyHand(EntityUid uid, EntityUid entity, bool checkActionBlocker = true, bool animateUser = false, SharedHandsComponent? handsComp = null, SharedItemComponent? item = null)
{
if (!Resolve(uid, ref handsComp, false))
return false;
if (!TryGetEmptyHand(uid, out var hand, handsComp))
return false;
return TryPickup(uid, entity, hand, checkActionBlocker, animateUser, handsComp, item);
}
public bool TryPickup(EntityUid uid, EntityUid entity, Hand hand, bool checkActionBlocker = true, bool animateUser = false, SharedHandsComponent? handsComp = null, SharedItemComponent? item = null)
{
if (!Resolve(uid, ref handsComp, false))
return false;
if (!Resolve(entity, ref item, false))
return false;
if (!CanPickupToHand(uid, entity, hand, checkActionBlocker, handsComp, item))
return false;
// animation
var xform = Transform(uid);
var coordinateEntity = xform.ParentUid.IsValid() ? xform.ParentUid : uid;
var initialPosition = EntityCoordinates.FromMap(EntityManager, coordinateEntity, Transform(entity).MapPosition);
PickupAnimation(entity, initialPosition, xform.LocalPosition, animateUser ? null : uid);
DoPickup(uid, hand, entity, handsComp);
return true;
}
public bool CanPickupAnyHand(EntityUid uid, EntityUid entity, bool checkActionBlocker = true, SharedHandsComponent? handsComp = null, SharedItemComponent? item = null)
{
if (!Resolve(uid, ref handsComp, false))
return false;
if (!TryGetEmptyHand(uid, out var hand, handsComp))
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, SharedHandsComponent? handsComp = null, SharedItemComponent? item = null)
{
if (!Resolve(uid, ref handsComp, false))
return false;
var handContainer = hand.Container;
if (handContainer == null || handContainer.ContainedEntity != null)
return false;
if (!Resolve(entity, ref item, false))
return false;
if (TryComp(entity, out PhysicsComponent? physics) && physics.BodyType == BodyType.Static)
return false;
if (checkActionBlocker && !_actionBlocker.CanPickup(uid, entity))
return false;
// check can insert (including raising attempt events).
return handContainer.CanInsert(entity, EntityManager);
}
/// <summary>
/// Puts an item any hand, preferring the active hand, or puts it on the floor.
/// </summary>
public void PickupOrDrop(EntityUid? uid, EntityUid entity, bool checkActionBlocker = true, bool animateUser = false, SharedHandsComponent? handsComp = null, SharedItemComponent? item = null)
{
if (uid == null
|| !Resolve(uid.Value, ref handsComp, false)
|| !TryGetEmptyHand(uid.Value, out var hand, handsComp)
|| !TryPickup(uid.Value, entity, hand, checkActionBlocker, animateUser, 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.
Transform(entity).AttachParentToContainerOrGrid(EntityManager);
}
}
/// <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, SharedHandsComponent? hands = null)
{
if (!Resolve(uid, ref hands))
return;
var handContainer = hand.Container;
if (handContainer == null || handContainer.ContainedEntity != null)
return;
if (!handContainer.Insert(entity, EntityManager))
{
Logger.Error($"{nameof(SharedHandsComponent)} on {uid} could not insert {entity} into {handContainer}.");
return;
}
_adminLogSystem.Add(LogType.Pickup, LogImpact.Low, $"{ToPrettyString(uid):user} picked up {ToPrettyString(entity):entity}");
Dirty(hands);
var didEquip = new DidEquipHandEvent(uid, entity, hand);
RaiseLocalEvent(uid, didEquip, false);
var gotEquipped = new GotEquippedHandEvent(uid, entity, hand);
RaiseLocalEvent(entity, gotEquipped);
// TODO this should REALLY be a cancellable thing, not a handled event.
// If one of the interactions resulted in the item being dropped, return early.
if (gotEquipped.Handled)
return;
if (hand == hands.ActiveHand)
RaiseLocalEvent(entity, new HandSelectedEvent(uid), false);
}
public abstract void PickupAnimation(EntityUid item, EntityCoordinates initialPosition, Vector2 finalPosition,
EntityUid? exclude);
}

View File

@@ -0,0 +1,209 @@
using Content.Shared.ActionBlocker;
using Content.Shared.Administration.Logs;
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
using Robust.Shared.Containers;
using Robust.Shared.Input.Binding;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Content.Shared.Hands.EntitySystems;
public abstract partial class SharedHandsSystem : EntitySystem
{
[Dependency] private readonly SharedAdminLogSystem _adminLogSystem = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
public override void Initialize()
{
base.Initialize();
InitializeInteractions();
}
public override void Shutdown()
{
base.Shutdown();
CommandBinds.Unregister<SharedHandsSystem>();
}
public void AddHand(EntityUid uid, string handName, HandLocation handLocation, SharedHandsComponent? handsComp = null)
{
if (!Resolve(uid, ref handsComp, false))
return;
if (handsComp.Hands.ContainsKey(handName))
return;
var container = _containerSystem.EnsureContainer<ContainerSlot>(uid, handName);
container.OccludesLight = false;
var newHand = new Hand(handName, handLocation, container);
handsComp.Hands.Add(handName, newHand);
handsComp.SortedHands.Add(handName);
if (handsComp.ActiveHand == null)
SetActiveHand(uid, newHand, handsComp);
RaiseLocalEvent(uid, new HandCountChangedEvent(uid), false);
Dirty(handsComp);
}
public void RemoveHand(EntityUid uid, string handName, SharedHandsComponent? handsComp = null)
{
if (!Resolve(uid, ref handsComp, false))
return;
if (!handsComp.Hands.Remove(handName, out var hand))
return;
TryDrop(uid, hand, null, false, true, handsComp);
hand.Container?.Shutdown();
handsComp.SortedHands.Remove(hand.Name);
if (handsComp.ActiveHand == hand)
TrySetActiveHand(uid, handsComp.SortedHands.FirstOrDefault(), handsComp);
RaiseLocalEvent(uid, new HandCountChangedEvent(uid), false);
Dirty(handsComp);
}
private void HandleSetHand(RequestSetHandEvent msg, EntitySessionEventArgs eventArgs)
{
if (eventArgs.SenderSession.AttachedEntity == null)
return;
TrySetActiveHand(eventArgs.SenderSession.AttachedEntity.Value, msg.HandName);
}
/// <summary>
/// Get any empty hand. Prioritizes the currently active hand.
/// </summary>
public bool TryGetEmptyHand(EntityUid uid, [NotNullWhen(true)] out Hand? emptyHand, SharedHandsComponent? handComp = null)
{
emptyHand = null;
if (!Resolve(uid, ref handComp, false))
return false;
foreach (var hand in EnumerateHands(uid, handComp))
{
if (hand.IsEmpty)
{
emptyHand = hand;
return true;
}
}
return false;
}
/// <summary>
/// Enumerate over hands, starting with the currently active hand.
/// </summary>
public IEnumerable<Hand> EnumerateHands(EntityUid uid, SharedHandsComponent? handsComp = null)
{
if (!Resolve(uid, ref handsComp, false))
yield break;
if (handsComp.ActiveHand != null)
yield return handsComp.ActiveHand;
foreach (var name in handsComp.SortedHands)
{
if (name != handsComp.ActiveHand?.Name)
yield return handsComp.Hands[name];
}
}
/// <summary>
/// Enumerate over hands, with the active hand being first.
/// </summary>
public IEnumerable<EntityUid> EnumerateHeld(EntityUid uid, SharedHandsComponent? handsComp = null)
{
if (!Resolve(uid, ref handsComp, false))
yield break;
if (handsComp.ActiveHandEntity != null)
yield return handsComp.ActiveHandEntity.Value;
foreach (var name in handsComp.SortedHands)
{
if (name == handsComp.ActiveHand?.Name)
continue;
if (handsComp.Hands[name].HeldEntity is EntityUid held)
yield return held;
}
}
/// <summary>
/// Set the currently active hand and raise hand (de)selection events directed at the held entities.
/// </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, SharedHandsComponent? handComp = null)
{
if (!Resolve(uid, ref handComp))
return false;
if (name == handComp.ActiveHand?.Name)
return false;
Hand? hand = null;
if (name != null && !handComp.Hands.TryGetValue(name, out hand))
return false;
return SetActiveHand(uid, hand, handComp);
}
/// <summary>
/// Set the currently active hand and raise hand (de)selection events directed at the held entities.
/// </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 SetActiveHand(EntityUid uid, Hand? hand, SharedHandsComponent? handComp = null)
{
if (!Resolve(uid, ref handComp))
return false;
if (hand == handComp.ActiveHand)
return false;
if (handComp.ActiveHand?.HeldEntity is EntityUid held)
RaiseLocalEvent(held, new HandDeselectedEvent(uid), false);
if (hand == null)
{
handComp.ActiveHand = null;
return true;
}
handComp.ActiveHand = hand;
if (hand.HeldEntity != null)
RaiseLocalEvent(hand.HeldEntity.Value, new HandSelectedEvent(uid), false);
Dirty(handComp);
return true;
}
public bool IsHolding(EntityUid uid, EntityUid? entity, [NotNullWhen(true)] out Hand? inHand, SharedHandsComponent? handsComp = null)
{
inHand = null;
if (!Resolve(uid, ref handsComp, false))
return false;
foreach (var hand in handsComp.Hands.Values)
{
if (hand.HeldEntity == entity)
{
inHand = hand;
return true;
}
}
return false;
}
}

View File

@@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using Content.Shared.Hands.Components;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using static Robust.Shared.GameObjects.SharedSpriteComponent;
namespace Content.Shared.Hands
{
/// <summary>
@@ -73,15 +70,9 @@ namespace Content.Shared.Hands
/// </summary>
public EntityUid User { get; }
/// <summary>
/// Item in the hand that was deselected.
/// </summary>
public EntityUid Item { get; }
public HandDeselectedEvent(EntityUid user, EntityUid item)
public HandDeselectedEvent(EntityUid user)
{
User = user;
Item = item;
}
}
@@ -96,15 +87,9 @@ namespace Content.Shared.Hands
/// </summary>
public EntityUid User { get; }
/// <summary>
/// Item in the hand that was selected.
/// </summary>
public EntityUid Item { get; }
public HandSelectedEvent(EntityUid user, EntityUid item)
public HandSelectedEvent(EntityUid user)
{
User = user;
Item = item;
}
}
@@ -231,4 +216,64 @@ namespace Content.Shared.Hands
{
public DidUnequipHandEvent(EntityUid user, EntityUid unequipped, Hand hand) : base(user, unequipped, hand) { }
}
/// <summary>
/// Event raised by a client when they want to use the item currently held in their hands.
/// </summary>
[Serializable, NetSerializable]
public sealed class RequestUseInHandEvent : EntityEventArgs
{
}
/// <summary>
/// Event raised by a client when they want to activate the item currently in their hands.
/// </summary>
[Serializable, NetSerializable]
public sealed class RequestActivateInHandEvent : EntityEventArgs
{
public string HandName { get; }
public RequestActivateInHandEvent(string handName)
{
HandName = handName;
}
}
/// <summary>
/// Event raised by a client when they want to use the currently held item on some other held item
/// </summary>
[Serializable, NetSerializable]
public sealed class RequestHandInteractUsingEvent : EntityEventArgs
{
public string HandName { get; }
public RequestHandInteractUsingEvent(string handName)
{
HandName = handName;
}
}
/// <summary>
/// Event raised by a client when they want to move an item held in another hand to their currently active hand
/// </summary>
[Serializable, NetSerializable]
public sealed class RequestMoveHandItemEvent : EntityEventArgs
{
public string HandName { get; }
public RequestMoveHandItemEvent(string handName)
{
HandName = handName;
}
}
public sealed class HandCountChangedEvent : EntityEventArgs
{
public HandCountChangedEvent(EntityUid sender)
{
Sender = sender;
}
public EntityUid Sender { get; }
}
}

View File

@@ -1,196 +0,0 @@
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.Hands.Components;
using Content.Shared.Input;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Input.Binding;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Players;
namespace Content.Shared.Hands
{
public abstract class SharedHandsSystem : EntitySystem
{
[Dependency] private readonly SharedAdminLogSystem _adminLogSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeAllEvent<RequestSetHandEvent>(HandleSetHand);
SubscribeLocalEvent<SharedHandsComponent, EntRemovedFromContainerMessage>(HandleContainerRemoved);
SubscribeLocalEvent<SharedHandsComponent, EntInsertedIntoContainerMessage>(HandleContainerInserted);
CommandBinds.Builder
.Bind(ContentKeyFunctions.Drop, new PointerInputCmdHandler(DropPressed))
.Bind(ContentKeyFunctions.SwapHands, InputCmdHandler.FromDelegate(SwapHandsPressed, handle: false))
.Register<SharedHandsSystem>();
}
public override void Shutdown()
{
base.Shutdown();
CommandBinds.Unregister<SharedHandsSystem>();
}
#region interactions
private void SwapHandsPressed(ICommonSession? session)
{
if (!TryComp(session?.AttachedEntity, out SharedHandsComponent? hands))
return;
if (!hands.TryGetSwapHandsResult(out var nextHand))
return;
TrySetActiveHand(hands.Owner, nextHand, hands);
}
private bool DropPressed(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
{
if (TryComp(session?.AttachedEntity, out SharedHandsComponent? hands))
hands.TryDropActiveHand(coords);
return false;
}
#endregion
#region EntityInsertRemove
/// <summary>
/// Removes the contents of a hand from its container. Assumes that the removal is allowed.
/// </summary>
public virtual void RemoveHeldEntityFromHand(EntityUid uid, Hand hand, SharedHandsComponent? hands = null)
{
if (!Resolve(uid, ref hands))
return;
if (hand.Container?.ContainedEntity == null)
return;
var entity = hand.Container.ContainedEntity.Value;
if (!hand.Container!.Remove(entity))
{
Logger.Error($"{nameof(SharedHandsComponent)} on {uid} could not remove {entity} from {hand.Container}.");
return;
}
hands.Dirty();
var gotUnequipped = new GotUnequippedHandEvent(uid, entity, hand);
RaiseLocalEvent(entity, gotUnequipped, false);
var didUnequip = new DidUnequipHandEvent(uid, entity, hand);
RaiseLocalEvent(uid, didUnequip);
if (hand.Name == hands.ActiveHand)
RaiseLocalEvent(entity, new HandDeselectedEvent(uid, entity), false);
}
/// <summary>
/// Puts an entity into the player's hand, assumes that the insertion is allowed.
/// </summary>
public virtual void PutEntityIntoHand(EntityUid uid, Hand hand, EntityUid entity, SharedHandsComponent? hands = null)
{
if (!Resolve(uid, ref hands))
return;
var handContainer = hand.Container;
if (handContainer == null || handContainer.ContainedEntity != null)
return;
if (!handContainer.Insert(entity))
{
Logger.Error($"{nameof(SharedHandsComponent)} on {uid} could not insert {entity} into {handContainer}.");
return;
}
_adminLogSystem.Add(LogType.Pickup, LogImpact.Low, $"{ToPrettyString(uid):user} picked up {ToPrettyString(entity):entity}");
hands.Dirty();
var didEquip = new DidEquipHandEvent(uid, entity, hand);
RaiseLocalEvent(uid, didEquip, false);
var gotEquipped = new GotEquippedHandEvent(uid, entity, hand);
RaiseLocalEvent(entity, gotEquipped);
// TODO this should REALLY be a cancellable thing, not a handled event.
// If one of the interactions resulted in the item being dropped, return early.
if (gotEquipped.Handled)
return;
if (hand.Name == hands.ActiveHand)
RaiseLocalEvent(entity, new HandSelectedEvent(uid, entity), false);
}
public abstract void PickupAnimation(EntityUid item, EntityCoordinates initialPosition, Vector2 finalPosition,
EntityUid? exclude);
#endregion
protected virtual void HandleContainerRemoved(EntityUid uid, SharedHandsComponent component, ContainerModifiedMessage args)
{
HandleContainerModified(uid, component, args);
}
protected virtual void HandleContainerModified(EntityUid uid, SharedHandsComponent hands, ContainerModifiedMessage args)
{
// client updates hand visuals here.
}
private void HandleContainerInserted(EntityUid uid, SharedHandsComponent component, EntInsertedIntoContainerMessage args)
{
// un-rotate entities. needed for things like directional flashlights
Transform(args.Entity).LocalRotation = 0;
HandleContainerModified(uid, component, args);
}
private void HandleSetHand(RequestSetHandEvent msg, EntitySessionEventArgs eventArgs)
{
if (eventArgs.SenderSession.AttachedEntity == null)
return;
TrySetActiveHand(eventArgs.SenderSession.AttachedEntity.Value, msg.HandName);
}
/// <summary>
/// Set the currently active hand and raise hand (de)selection events directed at the held entities.
/// </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? value, SharedHandsComponent? handComp = null)
{
if (!Resolve(uid, ref handComp))
return false;
if (value == handComp.ActiveHand)
return false;
if (value != null && !handComp.HasHand(value))
{
Logger.Warning($"{nameof(SharedHandsComponent)} on {handComp.Owner} tried to set its active hand to {value}, which was not a hand.");
return false;
}
if (value == null && handComp.Hands.Count != 0)
{
Logger.Error($"{nameof(SharedHandsComponent)} on {handComp.Owner} tried to set its active hand to null, when it still had another hand.");
return false;
}
if (handComp.TryGetActiveHeldEntity(out var entity))
RaiseLocalEvent(entity.Value, new HandDeselectedEvent(uid, entity.Value), false);
handComp.ActiveHand = value;
if (handComp.TryGetActiveHeldEntity(out entity))
RaiseLocalEvent(entity.Value, new HandSelectedEvent(uid, entity.Value), false);
handComp.Dirty();
return true;
}
}
}

View File

@@ -6,8 +6,8 @@ namespace Content.Shared.Input
public static class ContentKeyFunctions
{
public static readonly BoundKeyFunction WideAttack = "WideAttack";
public static readonly BoundKeyFunction ActivateItemInHand = "ActivateItemInHand";
public static readonly BoundKeyFunction AltActivateItemInHand = "AltActivateItemInHand";
public static readonly BoundKeyFunction UseItemInHand = "ActivateItemInHand";
public static readonly BoundKeyFunction AltUseItemInHand = "AltActivateItemInHand";
public static readonly BoundKeyFunction ActivateItemInWorld = "ActivateItemInWorld";
public static readonly BoundKeyFunction AltActivateItemInWorld = "AltActivateItemInWorld";
public static readonly BoundKeyFunction Drop = "Drop";

View File

@@ -216,7 +216,7 @@ namespace Content.Shared.Interaction
// Does the user have hands?
Hand? hand;
if (!TryComp(user, out SharedHandsComponent? hands) || !hands.TryGetActiveHand(out hand))
if (!TryComp(user, out SharedHandsComponent? hands) || hands.ActiveHand == null)
return;
var inRangeUnobstructed = target == null
@@ -224,7 +224,7 @@ namespace Content.Shared.Interaction
: !checkAccess || InRangeUnobstructed(user, target.Value); // permits interactions with wall mounted entities
// empty-hand interactions
if (hand.HeldEntity == null)
if (hands.ActiveHandEntity is not EntityUid held)
{
if (inRangeUnobstructed && target != null)
InteractHand(user, target.Value);
@@ -236,7 +236,7 @@ namespace Content.Shared.Interaction
if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user))
return;
if (target == hand.HeldEntity)
if (target == held)
{
UseInHandInteraction(user, target.Value, checkCanUse: false, checkCanInteract: false);
return;
@@ -246,7 +246,7 @@ namespace Content.Shared.Interaction
{
InteractUsing(
user,
hand.HeldEntity.Value,
held,
target.Value,
coordinates,
checkCanInteract: false,
@@ -257,7 +257,7 @@ namespace Content.Shared.Interaction
InteractUsingRanged(
user,
hand.HeldEntity.Value,
held,
target,
coordinates,
inRangeUnobstructed);

View File

@@ -1,5 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory.Events;
@@ -21,6 +22,7 @@ public abstract partial class InventorySystem
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
private void InitializeEquip()
@@ -52,7 +54,7 @@ public abstract partial class InventorySystem
if (!TryEquip(args.User, uid, slotDef.Name, true, inventory: inv))
continue;
hands.PutInHandOrDrop(slotEntity.Value);
_handsSystem.PickupOrDrop(args.User, slotEntity.Value);
}
else
{
@@ -104,7 +106,7 @@ public abstract partial class InventorySystem
if (!TryComp(actor, out InventoryComponent? inventory) || !TryComp<SharedHandsComponent>(actor, out var hands))
return;
hands.TryGetActiveHeldEntity(out var held);
var held = hands.ActiveHandEntity;
TryGetSlotEntity(actor, ev.Slot, out var itemUid, inventory);
// attempt to perform some interaction
@@ -118,8 +120,8 @@ public abstract partial class InventorySystem
// un-equip to hands
if (itemUid != null)
{
if (hands.CanPickupEntityToActiveHand(itemUid.Value) && TryUnequip(actor, ev.Slot, inventory: inventory))
hands.PutInHand(itemUid.Value, false);
if (_handsSystem.CanPickupAnyHand(actor, itemUid.Value, handsComp: hands) && TryUnequip(actor, ev.Slot, inventory: inventory))
_handsSystem.TryPickup(actor, itemUid.Value, checkActionBlocker: false, handsComp: hands);
return;
}
@@ -135,7 +137,7 @@ public abstract partial class InventorySystem
return;
}
if (hands.TryDropNoInteraction())
if (_handsSystem.TryDrop(actor, hands.ActiveHand!, doDropInteraction: false, handsComp: hands))
TryEquip(actor, actor, held.Value, ev.Slot, predicted: true, inventory: inventory);
}

View File

@@ -1,27 +1,30 @@
using Robust.Shared.GameObjects;
namespace Content.Shared.Item;
namespace Content.Shared.Item
{
/// <summary>
/// Raised on a *mob* when it tries to pickup something
/// </summary>
public sealed class PickupAttemptEvent : CancellableEntityEventArgs
public sealed class PickupAttemptEvent : BasePickupAttemptEvent
{
public PickupAttemptEvent(EntityUid uid)
{
Uid = uid;
}
public EntityUid Uid { get; }
public PickupAttemptEvent(EntityUid user, EntityUid item) : base(user, item) { }
}
/// <summary>
/// Raised on the *item* when tried to be picked up
/// Raised directed at entity being picked up when someone tries to pick it up
/// </summary>
/// <remarks>
/// Doesn't just handle "items" but calling it "PickedUpAttempt" is too close to "Pickup" for the sleep deprived brain.
/// </remarks>
public sealed class AttemptItemPickupEvent : CancellableEntityEventArgs
public sealed class GettingPickedUpAttemptEvent : BasePickupAttemptEvent
{
public GettingPickedUpAttemptEvent(EntityUid user, EntityUid item) : base(user, item) { }
}
[Virtual]
public class BasePickupAttemptEvent : CancellableEntityEventArgs
{
public readonly EntityUid User;
public readonly EntityUid Item;
public BasePickupAttemptEvent(EntityUid user, EntityUid item)
{
User = user;
Item = item;
}
}

View File

@@ -1,17 +1,17 @@
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Inventory.Events;
using Content.Shared.Verbs;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.Localization;
using System;
namespace Content.Shared.Item
{
public abstract class SharedItemSystem : EntitySystem
{
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
public override void Initialize()
{
base.Initialize();
@@ -30,13 +30,7 @@ namespace Content.Shared.Item
if (args.Handled)
return;
if (!TryComp(args.User, out SharedHandsComponent? hands))
return;
if (hands.ActiveHand == null)
return;
args.Handled = hands.TryPickupEntity(hands.ActiveHand, uid, false, animateUser: false);
args.Handled = _handsSystem.TryPickup(args.User, uid, animateUser: false);
}
private void OnHandleState(EntityUid uid, SharedItemComponent component, ref ComponentHandleState args)
@@ -74,11 +68,11 @@ namespace Content.Shared.Item
args.Using != null ||
!args.CanAccess ||
!args.CanInteract ||
!args.Hands.CanPickupEntityToActiveHand(args.Target))
!_handsSystem.CanPickupAnyHand(args.User, args.Target, handsComp: args.Hands, item: component))
return;
InteractionVerb verb = new();
verb.Act = () => args.Hands.TryPickupEntityToActiveHand(args.Target);
verb.Act = () => _handsSystem.TryPickupAnyHand(args.User, args.Target, checkActionBlocker: false, handsComp: args.Hands, item: component);
verb.IconTexture = "/Textures/Interface/VerbIcons/pickup.svg.192dpi.png";
// if the item already in a container (that is not the same as the user's), then change the text.

View File

@@ -1,14 +1,14 @@
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
namespace Content.Shared.Placeable
{
public sealed class PlaceableSurfaceSystem : EntitySystem
{
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
public override void Initialize()
{
base.Initialize();
@@ -52,16 +52,13 @@ namespace Content.Shared.Placeable
if (!surface.IsPlaceable)
return;
if(!EntityManager.TryGetComponent<SharedHandsComponent?>(args.User, out var handComponent))
return;
if(!handComponent.TryDropEntity(args.Used, EntityManager.GetComponent<TransformComponent>(surface.Owner).Coordinates))
if (!_handsSystem.TryDrop(args.User, args.Used))
return;
if (surface.PlaceCentered)
EntityManager.GetComponent<TransformComponent>(args.Used).LocalPosition = EntityManager.GetComponent<TransformComponent>(uid).LocalPosition + surface.PositionOffset;
Transform(args.Used).LocalPosition = Transform(uid).LocalPosition + surface.PositionOffset;
else
EntityManager.GetComponent<TransformComponent>(args.Used).Coordinates = args.ClickLocation;
Transform(args.Used).Coordinates = args.ClickLocation;
args.Handled = true;
}

View File

@@ -80,7 +80,7 @@ namespace Content.Shared.Verbs
EntityUid? @using = null;
if (TryComp(user, out SharedHandsComponent? hands) && (force || _actionBlockerSystem.CanUseHeldEntity(user)))
{
hands.TryGetActiveHeldEntity(out @using);
@using = hands.ActiveHandEntity;
// Check whether the "Held" entity is a virtual pull entity. If yes, set that as the entity being "Used".
// This allows you to do things like buckle a dragged person onto a surgery table, without click-dragging