+++ /dev/null
-using Content.Client.Hands.UI;
-using Content.Client.Items;
-using Content.Shared.Hands;
-using Content.Shared.Hands.Components;
-using JetBrains.Annotations;
-using Robust.Shared.GameObjects;
-
-namespace Content.Client.Hands.Systems
-{
- [UsedImplicitly]
- public sealed class HandVirtualItemSystem : SharedHandVirtualItemSystem
- {
- public override void Initialize()
- {
- base.Initialize();
-
- Subs.ItemStatus<HandVirtualItemComponent>(_ => new HandVirtualItemStatus());
- }
- }
-}
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
+using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Item;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
OnPlayerItemAdded?.Invoke(hand.Name, args.Entity);
- if (HasComp<HandVirtualItemComponent>(args.Entity))
+ if (HasComp<VirtualItemComponent>(args.Entity))
OnPlayerHandBlocked?.Invoke(hand.Name);
}
OnPlayerItemRemoved?.Invoke(hand.Name, args.Entity);
- if (HasComp<HandVirtualItemComponent>(args.Entity))
+ if (HasComp<VirtualItemComponent>(args.Entity))
OnPlayerHandUnblocked?.Invoke(hand.Name);
}
using Content.Shared.IdentityManagement;
using Content.Shared.Input;
using Content.Shared.Inventory;
+using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Strip.Components;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
button.Pressed += SlotPressed;
- if (EntMan.TryGetComponent<HandVirtualItemComponent>(hand.HeldEntity, out var virt))
+ if (EntMan.TryGetComponent<VirtualItemComponent>(hand.HeldEntity, out var virt))
{
button.Blocked = true;
if (EntMan.TryGetComponent<CuffableComponent>(Owner, out var cuff) && _cuffable.GetAllCuffs(cuff).Contains(virt.BlockingEntity))
}
EntityUid? viewEnt;
- if (EntMan.TryGetComponent<HandVirtualItemComponent>(entity, out var virt))
+ if (EntMan.TryGetComponent<VirtualItemComponent>(entity, out var virt))
viewEnt = EntMan.HasComponent<SpriteComponent>(virt.BlockingEntity) ? virt.BlockingEntity : null;
else if (EntMan.HasComponent<SpriteComponent>(entity))
viewEnt = entity;
--- /dev/null
+using Content.Client.Hands.UI;
+using Content.Client.Items;
+using Content.Shared.Inventory.VirtualItem;
+
+namespace Content.Client.Inventory;
+
+public sealed class VirtualItemSystem : SharedVirtualItemSystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ Subs.ItemStatus<VirtualItemComponent>(_ => new HandVirtualItemStatus());
+ }
+}
using Content.Client.UserInterface.Systems.Hotbar.Widgets;
using Content.Shared.Hands.Components;
using Content.Shared.Input;
+using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Timing;
using Robust.Client.Player;
using Robust.Client.UserInterface;
{
var handButton = AddHand(name, hand.Location);
- if (_entities.TryGetComponent(hand.HeldEntity, out HandVirtualItemComponent? virt))
+ if (_entities.TryGetComponent(hand.HeldEntity, out VirtualItemComponent? virt))
{
handButton.SpriteView.SetEntity(virt.BlockingEntity);
handButton.Blocked = true;
if (hand == null)
return;
- if (_entities.TryGetComponent(entity, out HandVirtualItemComponent? virt))
+ if (_entities.TryGetComponent(entity, out VirtualItemComponent? virt))
{
hand.SpriteView.SetEntity(virt.BlockingEntity);
hand.Blocked = true;
using Content.Client.Resources;
using Content.Shared.Hands.Components;
using Content.Shared.IdentityManagement;
+using Content.Shared.Inventory.VirtualItem;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
return;
}
- if (_entityManager.TryGetComponent(_entity, out HandVirtualItemComponent? virtualItem)
+ if (_entityManager.TryGetComponent(_entity, out VirtualItemComponent? virtualItem)
&& _entityManager.EntityExists(virtualItem.BlockingEntity))
{
// Uses identity because we can be blocked by pulling someone
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Hands.Components;
using Content.Shared.Input;
+using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Storage;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
if (_slotGroups.GetValueOrDefault(group)?.GetButton(name) is not { } button)
return;
- button.SpriteView.SetEntity(entity);
- button.StorageButton.Visible = showStorage;
+ if (_entities.TryGetComponent(entity, out VirtualItemComponent? virtb))
+ {
+ button.SpriteView.SetEntity(virtb.BlockingEntity);
+ button.Blocked = true;
+ }
+ else
+ {
+ button.SpriteView.SetEntity(entity);
+ button.Blocked = false;
+ button.StorageButton.Visible = showStorage;
+ }
}
public bool RegisterSlotGroupContainer(ItemSlotButtonContainer slotContainer)
{
// The only prototypes that should get ignored are those that REQUIRE setup to get a sprite. At that point it is
// the responsibility of the spawner to ensure that a valid sprite is set.
- "HandVirtualItem"
+ "VirtualItem"
};
[Test]
+++ /dev/null
-using Content.Shared.Hands;
-using Content.Shared.Hands.Components;
-using Content.Shared.Hands.EntitySystems;
-using JetBrains.Annotations;
-using System.Diagnostics.CodeAnalysis;
-
-namespace Content.Server.Hands.Systems
-{
- [UsedImplicitly]
- public sealed class HandVirtualItemSystem : SharedHandVirtualItemSystem
- {
-
- }
-}
using System.Numerics;
+using Content.Server.Inventory;
using Content.Server.Pulling;
using Content.Server.Stack;
using Content.Server.Stunnable;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Input;
+using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Physics.Pull;
using Content.Shared.Pulling.Components;
using Content.Shared.Stacks;
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly StackSystem _stackSystem = default!;
- [Dependency] private readonly HandVirtualItemSystem _virtualItemSystem = default!;
+ [Dependency] private readonly VirtualItemSystem _virtualItemSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly PullingSystem _pullingSystem = default!;
foreach (var hand in component.Hands.Values)
{
if (hand.HeldEntity == null
- || !TryComp(hand.HeldEntity, out HandVirtualItemComponent? virtualItem)
+ || !TryComp(hand.HeldEntity, out VirtualItemComponent? virtualItem)
|| virtualItem.BlockingEntity != args.Pulled.Owner)
continue;
--- /dev/null
+using Content.Shared.Hands;
+using Content.Shared.Inventory.VirtualItem;
+
+namespace Content.Server.Inventory;
+
+public sealed class VirtualItemSystem : SharedVirtualItemSystem
+{
+
+}
using Content.Server.Hands.Systems;
+using Content.Server.Inventory;
using Content.Shared.Hands;
using Content.Shared.Item;
public sealed class MultiHandedItemSystem : SharedMultiHandedItemSystem
{
- [Dependency] private readonly HandVirtualItemSystem _virtualItem = default!;
+ [Dependency] private readonly VirtualItemSystem _virtualItem = default!;
protected override void OnEquipped(EntityUid uid, MultiHandedItemComponent component, GotEquippedHandEvent args)
{
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory;
+using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Popups;
using Content.Shared.Strip;
using Content.Shared.Strip.Components;
return;
// is the target a handcuff?
- if (TryComp(hand.HeldEntity, out HandVirtualItemComponent? virt)
+ if (TryComp(hand.HeldEntity, out VirtualItemComponent? virt)
&& TryComp(target, out CuffableComponent? cuff)
&& _cuffable.GetAllCuffs(cuff).Contains(virt.BlockingEntity))
{
return false;
}
- if (HasComp<HandVirtualItemComponent>(hand.HeldEntity))
+ if (HasComp<VirtualItemComponent>(hand.HeldEntity))
return false;
if (!_handsSystem.CanDropHeld(target, hand, false))
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.Hands.Components;
+using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Verbs;
namespace Content.Server.Verbs
}
// if this is a virtual pull, get the held entity
- if (holding != null && TryComp(holding, out HandVirtualItemComponent? pull))
+ if (holding != null && TryComp(holding, out VirtualItemComponent? pull))
holding = pull.BlockingEntity;
var verbText = $"{verb.Category?.Text} {verb.Text}".Trim();
using Content.Shared.Interaction.Components;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory.Events;
+using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Item;
using Content.Shared.Mobs.Systems;
using Content.Shared.Movement.Events;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
- [Dependency] private readonly SharedHandVirtualItemSystem _handVirtualItem = default!;
+ [Dependency] private readonly SharedVirtualItemSystem _virtualItem = default!;
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
if (args.Container.ID != component.Container?.ID)
return;
- _handVirtualItem.DeleteInHandsMatching(uid, args.Entity);
+ _virtualItem.DeleteInHandsMatching(uid, args.Entity);
UpdateCuffState(uid, component);
}
break;
}
- if (_handVirtualItem.TrySpawnVirtualItemInHand(handcuff, uid, out var virtItem1))
+ if (_virtualItem.TrySpawnVirtualItemInHand(handcuff, uid, out var virtItem1))
EnsureComp<UnremoveableComponent>(virtItem1.Value);
- if (_handVirtualItem.TrySpawnVirtualItemInHand(handcuff, uid, out var virtItem2))
+ if (_virtualItem.TrySpawnVirtualItemInHand(handcuff, uid, out var virtItem2))
EnsureComp<UnremoveableComponent>(virtItem2.Value);
}
+++ /dev/null
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Hands.Components;
-
-[RegisterComponent]
-[NetworkedComponent]
-[AutoGenerateComponentState(true)]
-public sealed partial class HandVirtualItemComponent : Component
-{
- /// <summary>
- /// The entity blocking this hand.
- /// </summary>
- [DataField("blockingEntity"), AutoNetworkedField]
- public EntityUid BlockingEntity;
-}
using System.Numerics;
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
+using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Tag;
using Robust.Shared.Containers;
using Robust.Shared.Map;
var didUnequip = new DidUnequipHandEvent(uid, args.Entity, hand);
RaiseLocalEvent(uid, didUnequip);
- if (TryComp(args.Entity, out HandVirtualItemComponent? @virtual))
- _virtualSystem.Delete((args.Entity, @virtual), uid);
+ if (TryComp(args.Entity, out VirtualItemComponent? @virtual))
+ _virtualSystem.DeleteVirtualItem((args.Entity, @virtual), uid);
}
private bool ShouldIgnoreRestrictions(EntityUid user)
using Content.Shared.Hands.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Input;
+using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Localizations;
using Robust.Shared.Input.Binding;
using Robust.Shared.Map;
private void HandleExamined(EntityUid uid, HandsComponent handsComp, ExaminedEvent args)
{
var held = EnumerateHeld(uid, handsComp)
- .Where(x => !HasComp<HandVirtualItemComponent>(x)).ToList();
+ .Where(x => !HasComp<VirtualItemComponent>(x)).ToList();
using (args.PushGroup(nameof(HandsComponent)))
{
+++ /dev/null
-using Content.Shared.Hands.Components;
-
-namespace Content.Shared.Hands.EntitySystems;
-
-public abstract partial class SharedHandsSystem
-{
- private void InitializeVirtual()
- {
- SubscribeLocalEvent<HandVirtualItemComponent, AfterAutoHandleStateEvent>(OnVirtualAfter);
- }
-
- private void OnVirtualAfter(EntityUid uid, HandVirtualItemComponent component, ref AfterAutoHandleStateEvent args)
- {
- // update hands GUI with new entity.
- if (ContainerSystem.IsEntityInContainer(uid))
- _items.VisualsChanged(uid);
- }
-}
using Content.Shared.Administration.Logs;
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
+using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Item;
using Content.Shared.Storage.EntitySystems;
using Robust.Shared.Containers;
[Dependency] private readonly SharedItemSystem _items = default!;
[Dependency] private readonly SharedStorageSystem _storage = default!;
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
- [Dependency] private readonly SharedHandVirtualItemSystem _virtualSystem = default!;
+ [Dependency] private readonly SharedVirtualItemSystem _virtualSystem = default!;
protected event Action<Entity<HandsComponent>?>? OnHandSetActive;
InitializeInteractions();
InitializeDrop();
InitializePickup();
- InitializeVirtual();
InitializeRelay();
}
+++ /dev/null
-using System.Diagnostics.CodeAnalysis;
-using Content.Shared.Hands.Components;
-using Content.Shared.Hands.EntitySystems;
-using Content.Shared.Interaction;
-using Content.Shared.Inventory.Events;
-using Robust.Shared.Network;
-
-namespace Content.Shared.Hands;
-
-public abstract class SharedHandVirtualItemSystem : EntitySystem
-{
- [Dependency] private readonly INetManager _net = default!;
- [Dependency] private readonly SharedHandsSystem _hands = default!;
- [Dependency] private readonly SharedTransformSystem _transform = default!;
-
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent<HandVirtualItemComponent, BeingEquippedAttemptEvent>(OnBeingEquippedAttempt);
- SubscribeLocalEvent<HandVirtualItemComponent, BeforeRangedInteractEvent>(HandleBeforeInteract);
- }
-
- public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user)
- {
- return TrySpawnVirtualItemInHand(blockingEnt, user, out _);
- }
-
- public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem)
- {
- if (_net.IsClient || !_hands.TryGetEmptyHand(user, out var hand))
- {
- virtualItem = null;
- return false;
- }
-
- var pos = Transform(user).Coordinates;
- virtualItem = Spawn("HandVirtualItem", pos);
- var virtualItemComp = EntityManager.GetComponent<HandVirtualItemComponent>(virtualItem.Value);
- virtualItemComp.BlockingEntity = blockingEnt;
- Dirty(virtualItemComp);
- _hands.DoPickup(user, hand, virtualItem.Value);
- return true;
- }
-
-
- /// <summary>
- /// Deletes all virtual items in a user's hands with
- /// the specified blocked entity.
- /// </summary>
- public void DeleteInHandsMatching(EntityUid user, EntityUid matching)
- {
- // Client can't currently predict deleting network entities atm and this might happen due to the
- // hands leaving PVS for example, in which case we wish to ignore it.
- if (_net.IsClient)
- return;
-
- foreach (var hand in _hands.EnumerateHands(user))
- {
- if (TryComp(hand.HeldEntity, out HandVirtualItemComponent? virt) && virt.BlockingEntity == matching)
- {
- Delete((hand.HeldEntity.Value, virt), user);
- }
- }
- }
-
- private void OnBeingEquippedAttempt(EntityUid uid, HandVirtualItemComponent component, BeingEquippedAttemptEvent args)
- {
- args.Cancel();
- }
-
- private static void HandleBeforeInteract(
- EntityUid uid,
- HandVirtualItemComponent component,
- BeforeRangedInteractEvent args)
- {
- // No interactions with a virtual item, please.
- args.Handled = true;
- }
-
- /// <summary>
- /// Queues a deletion for a virtual item and notifies the blocking entity and user.
- /// </summary>
- public void Delete(Entity<HandVirtualItemComponent> item, EntityUid user)
- {
- var userEv = new VirtualItemDeletedEvent(item.Comp.BlockingEntity, user);
- RaiseLocalEvent(user, userEv);
- var targEv = new VirtualItemDeletedEvent(item.Comp.BlockingEntity, user);
- RaiseLocalEvent(item.Comp.BlockingEntity, targEv);
-
- if (TerminatingOrDeleted(item))
- return;
-
- _transform.DetachParentToNull(item, Transform(item));
- if (_net.IsServer)
- QueueDel(item);
- }
-}
--- /dev/null
+using System.Diagnostics.CodeAnalysis;
+using Content.Shared.Hands;
+using Content.Shared.Hands.EntitySystems;
+using Content.Shared.Interaction;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Item;
+using Robust.Shared.Containers;
+using Robust.Shared.Network;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Inventory.VirtualItem;
+
+/// <summary>
+/// In charge of managing virtual items.
+/// Virtual items are used to block a <see cref="SlotButton"/>
+/// or a <see cref="HandButton"/> with a non-existent item that
+/// is a visual copy of another for whatever use
+/// </summary>
+/// <remarks>
+/// The slot visuals are managed by <see cref="HandsUiController"/>
+/// and <see cref="InventoryUiController"/>, see the <see cref="VirtualItemComponent"/>
+/// references there for more information
+/// </remarks>
+public abstract class SharedVirtualItemSystem : EntitySystem
+{
+ [Dependency] private readonly INetManager _netManager = default!;
+ [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
+ [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
+ [Dependency] private readonly SharedItemSystem _itemSystem = default!;
+ [Dependency] private readonly InventorySystem _inventorySystem = default!;
+ [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
+
+ [ValidatePrototypeId<EntityPrototype>]
+ private const string VirtualItem = "VirtualItem";
+
+ public override void Initialize()
+ {
+ SubscribeLocalEvent<VirtualItemComponent, AfterAutoHandleStateEvent>(OnAfterAutoHandleState);
+
+ SubscribeLocalEvent<VirtualItemComponent, BeingEquippedAttemptEvent>(OnBeingEquippedAttempt);
+ SubscribeLocalEvent<VirtualItemComponent, BeingUnequippedAttemptEvent>(OnBeingUnequippedAttempt);
+
+ SubscribeLocalEvent<VirtualItemComponent, BeforeRangedInteractEvent>(OnBeforeRangedInteract);
+ }
+
+ /// <summary>
+ /// Updates the GUI buttons with the new entity.
+ /// </summary>
+ private void OnAfterAutoHandleState(Entity<VirtualItemComponent> ent, ref AfterAutoHandleStateEvent args)
+ {
+ if (_containerSystem.IsEntityInContainer(ent))
+ _itemSystem.VisualsChanged(ent);
+ }
+
+ private void OnBeingEquippedAttempt(Entity<VirtualItemComponent> ent, ref BeingEquippedAttemptEvent args)
+ {
+ // No interactions with a virtual item, please.
+ args.Cancel();
+ }
+
+ private void OnBeingUnequippedAttempt(Entity<VirtualItemComponent> ent, ref BeingUnequippedAttemptEvent args)
+ {
+ // No interactions with a virtual item, please.
+ args.Cancel();
+ }
+
+ private void OnBeforeRangedInteract(Entity<VirtualItemComponent> ent, ref BeforeRangedInteractEvent args)
+ {
+ // No interactions with a virtual item, please.
+ args.Handled = true;
+ }
+
+ #region Hands
+ /// <summary>
+ /// Spawns a virtual item in a empty hand
+ /// </summary>
+ /// <param name="blockingEnt">The entity we will make a virtual entity copy of</param>
+ /// <param name="user">The entity that we want to insert the virtual entity</param>
+ public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user)
+ {
+ return TrySpawnVirtualItemInHand(blockingEnt, user, out _);
+ }
+
+ /// <inheritdoc cref="TrySpawnVirtualItemInHand(Robust.Shared.GameObjects.EntityUid,Robust.Shared.GameObjects.EntityUid)"/>
+ public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem)
+ {
+ if (!TrySpawnVirtualItem(blockingEnt, user, out virtualItem) || !_handsSystem.TryGetEmptyHand(user, out var hand))
+ return false;
+
+ _handsSystem.DoPickup(user, hand, virtualItem.Value);
+ return true;
+ }
+
+ /// <summary>
+ /// Scan the user's hands until we find the virtual entity, if the
+ /// virtual entity is a copy of the matching entity, delete it
+ /// </summary>
+ public void DeleteInHandsMatching(EntityUid user, EntityUid matching)
+ {
+ // Client can't currently predict deleting networked entities so we use this workaround, another
+ // problem can popup when the hands leave PVS for example and this avoids that too
+ if (_netManager.IsClient)
+ return;
+
+ foreach (var hand in _handsSystem.EnumerateHands(user))
+ {
+ if (TryComp(hand.HeldEntity, out VirtualItemComponent? virt) && virt.BlockingEntity == matching)
+ {
+ DeleteVirtualItem((hand.HeldEntity.Value, virt), user);
+ }
+ }
+ }
+ #endregion
+
+ #region Inventory
+
+ /// <summary>
+ /// Spawns a virtual item inside a inventory slot
+ /// </summary>
+ /// <param name="blockingEnt">The entity we will make a virtual entity copy of</param>
+ /// <param name="user">The entity that we want to insert the virtual entity</param>
+ /// <param name="slot">The slot to which we will insert the virtual entity (could be the "shoes" slot, for example)</param>
+ public bool TrySpawnVirtualItemInInventory(EntityUid blockingEnt, EntityUid user, string slot, bool force = false)
+ {
+ return TrySpawnVirtualItemInInventory(blockingEnt, user, slot, force, out _);
+ }
+
+ /// <inheritdoc cref="TrySpawnVirtualItemInInventory(Robust.Shared.GameObjects.EntityUid,Robust.Shared.GameObjects.EntityUid,string,bool)"/>
+ public bool TrySpawnVirtualItemInInventory(EntityUid blockingEnt, EntityUid user, string slot, bool force, [NotNullWhen(true)] out EntityUid? virtualItem)
+ {
+ if (!TrySpawnVirtualItem(blockingEnt, user, out virtualItem))
+ return false;
+
+ _inventorySystem.TryEquip(user, virtualItem.Value, slot, force: force);
+ return true;
+ }
+
+ /// <summary>
+ /// Scan the user's inventory slots until we find a virtual entity, when
+ /// that's done check if the found virtual entity is a copy of our matching entity,
+ /// if it is, delete it
+ /// </summary>
+ /// <param name="slotName">Set this param if you have the name of the slot, it avoids unnecessary queries</param>
+ public void DeleteInSlotMatching(EntityUid user, EntityUid matching, string? slotName = null)
+ {
+ // Client can't currently predict deleting networked entities so we use this workaround, another
+ // problem can popup when the hands leave PVS for example and this avoids that too
+ if (_netManager.IsClient)
+ return;
+
+ if (slotName != null)
+ {
+ if (!_inventorySystem.TryGetSlotEntity(user, slotName, out var slotEnt))
+ return;
+
+ if (TryComp(slotEnt, out VirtualItemComponent? virt) && virt.BlockingEntity == matching)
+ DeleteVirtualItem((slotEnt.Value, virt), user);
+
+ return;
+ }
+
+ if (!_inventorySystem.TryGetSlots(user, out var slotDefinitions))
+ return;
+
+ foreach (var slot in slotDefinitions)
+ {
+ if (!_inventorySystem.TryGetSlotEntity(user, slot.Name, out var slotEnt))
+ continue;
+
+ if (TryComp(slotEnt, out VirtualItemComponent? virt) && virt.BlockingEntity == matching)
+ DeleteVirtualItem((slotEnt.Value, virt), user);
+ }
+ }
+ #endregion
+
+ /// <summary>
+ /// Spawns a virtual item and setups the component without any special handling
+ /// </summary>
+ /// <param name="blockingEnt">The entity we will make a virtual entity copy of</param>
+ /// <param name="user">The entity that we want to insert the virtual entity</param>
+ public bool TrySpawnVirtualItem(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem)
+ {
+ if (_netManager.IsClient)
+ {
+ virtualItem = null;
+ return false;
+ }
+
+ var pos = Transform(user).Coordinates;
+ virtualItem = Spawn(VirtualItem, pos);
+ var virtualItemComp = Comp<VirtualItemComponent>(virtualItem.Value);
+ virtualItemComp.BlockingEntity = blockingEnt;
+ Dirty(virtualItem.Value, virtualItemComp);
+ return true;
+ }
+
+ /// <summary>
+ /// Queues a deletion for a virtual item and notifies the blocking entity and user.
+ /// </summary>
+ public void DeleteVirtualItem(Entity<VirtualItemComponent> item, EntityUid user)
+ {
+ var userEv = new VirtualItemDeletedEvent(item.Comp.BlockingEntity, user);
+ RaiseLocalEvent(user, userEv);
+
+ var targEv = new VirtualItemDeletedEvent(item.Comp.BlockingEntity, user);
+ RaiseLocalEvent(item.Comp.BlockingEntity, targEv);
+
+ if (TerminatingOrDeleted(item))
+ return;
+
+ _transformSystem.DetachParentToNull(item, Transform(item));
+ if (_netManager.IsServer)
+ QueueDel(item);
+ }
+}
--- /dev/null
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Inventory.VirtualItem;
+
+/// <inheritdoc cref="SharedVirtualItemSystem"/>
+[RegisterComponent]
+[NetworkedComponent]
+[AutoGenerateComponentState(true)]
+public sealed partial class VirtualItemComponent : Component
+{
+ /// <summary>
+ /// The entity blocking this slot.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public EntityUid BlockingEntity;
+}
using Content.Shared.Buckle;
using Content.Shared.Buckle.Components;
using Content.Shared.Hands;
+using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Item;
using Content.Shared.Light.Components;
using Content.Shared.Movement.Components;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
- [Dependency] private readonly SharedHandVirtualItemSystem _virtualItemSystem = default!;
+ [Dependency] private readonly SharedVirtualItemSystem _virtualItemSystem = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly SharedJointSystem _joints = default!;
[Dependency] private readonly SharedBuckleSystem _buckle = default!;
using Content.Shared.ActionBlocker;
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
+using Content.Shared.Inventory.VirtualItem;
using Robust.Shared.Containers;
namespace Content.Shared.Verbs
// This allows you to do things like buckle a dragged person onto a surgery table, without click-dragging
// their sprite.
- if (TryComp(@using, out HandVirtualItemComponent? pull))
+ if (TryComp(@using, out VirtualItemComponent? pull))
{
@using = pull.BlockingEntity;
}
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction.Events;
+using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Item;
using Content.Shared.Popups;
using Content.Shared.Timing;
public sealed class WieldableSystem : EntitySystem
{
- [Dependency] private readonly SharedHandVirtualItemSystem _virtualItemSystem = default!;
+ [Dependency] private readonly SharedVirtualItemSystem _virtualItemSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
# This item is stored in the hand slot while it is blocked, to represent what is blocking that hand.
- type: entity
- id: HandVirtualItem
+ id: VirtualItem
name: VIRTUAL ITEM YOU SHOULD NOT SEE THIS
noSpawn: true
components:
- type: Item
- - type: HandVirtualItem
+ - type: VirtualItem