From 108f001731b9394f98d9ef712b9b777e5b3f8abc Mon Sep 17 00:00:00 2001 From: AJCM-git <60196617+AJCM-git@users.noreply.github.com> Date: Sun, 14 Jan 2024 06:18:47 -0400 Subject: [PATCH] Virtual items cleanup (#23912) * Virtual items cleanup * Detail * Review --------- Co-authored-by: metalgearsloth --- .../Hands/Systems/HandVirtualItemSystem.cs | 20 -- Content.Client/Hands/Systems/HandsSystem.cs | 5 +- .../Inventory/StrippableBoundUserInterface.cs | 5 +- Content.Client/Inventory/VirtualItemSystem.cs | 15 ++ .../Systems/Hands/HandsUIController.cs | 5 +- .../Controls/ItemStatusPanel.xaml.cs | 3 +- .../Inventory/InventoryUIController.cs | 14 +- .../Tests/Sprite/ItemSpriteTest.cs | 2 +- .../Hands/Systems/HandVirtualItemSystem.cs | 14 -- Content.Server/Hands/Systems/HandsSystem.cs | 6 +- Content.Server/Inventory/VirtualItemSystem.cs | 9 + Content.Server/Item/MultiHandedItemSystem.cs | 3 +- Content.Server/Strip/StrippableSystem.cs | 5 +- Content.Server/Verbs/VerbSystem.cs | 3 +- Content.Shared/Cuffs/SharedCuffableSystem.cs | 9 +- .../Components/HandVirtualItemComponent.cs | 15 -- .../EntitySystems/SharedHandsSystem.Drop.cs | 5 +- .../SharedHandsSystem.Interactions.cs | 3 +- .../SharedHandsSystem.Virtual.cs | 18 -- .../Hands/EntitySystems/SharedHandsSystem.cs | 4 +- .../Hands/SharedHandVirtualItemSystem.cs | 98 -------- .../VirtualItem/SharedVirtualItemSystem.cs | 215 ++++++++++++++++++ .../VirtualItem/VirtualItemComponent.cs | 16 ++ Content.Shared/Vehicle/SharedVehicleSystem.cs | 3 +- Content.Shared/Verbs/SharedVerbSystem.cs | 3 +- Content.Shared/Wieldable/WieldableSystem.cs | 3 +- .../Entities/Virtual/virtual_item.yml | 4 +- 27 files changed, 310 insertions(+), 195 deletions(-) delete mode 100644 Content.Client/Hands/Systems/HandVirtualItemSystem.cs create mode 100644 Content.Client/Inventory/VirtualItemSystem.cs delete mode 100644 Content.Server/Hands/Systems/HandVirtualItemSystem.cs create mode 100644 Content.Server/Inventory/VirtualItemSystem.cs delete mode 100644 Content.Shared/Hands/Components/HandVirtualItemComponent.cs delete mode 100644 Content.Shared/Hands/EntitySystems/SharedHandsSystem.Virtual.cs delete mode 100644 Content.Shared/Hands/SharedHandVirtualItemSystem.cs create mode 100644 Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs create mode 100644 Content.Shared/Inventory/VirtualItem/VirtualItemComponent.cs diff --git a/Content.Client/Hands/Systems/HandVirtualItemSystem.cs b/Content.Client/Hands/Systems/HandVirtualItemSystem.cs deleted file mode 100644 index 496b8cea1f..0000000000 --- a/Content.Client/Hands/Systems/HandVirtualItemSystem.cs +++ /dev/null @@ -1,20 +0,0 @@ -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(_ => new HandVirtualItemStatus()); - } - } -} diff --git a/Content.Client/Hands/Systems/HandsSystem.cs b/Content.Client/Hands/Systems/HandsSystem.cs index b403c66dd7..9ea094a73a 100644 --- a/Content.Client/Hands/Systems/HandsSystem.cs +++ b/Content.Client/Hands/Systems/HandsSystem.cs @@ -6,6 +6,7 @@ using Content.Client.Verbs.UI; 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; @@ -252,7 +253,7 @@ namespace Content.Client.Hands.Systems OnPlayerItemAdded?.Invoke(hand.Name, args.Entity); - if (HasComp(args.Entity)) + if (HasComp(args.Entity)) OnPlayerHandBlocked?.Invoke(hand.Name); } @@ -270,7 +271,7 @@ namespace Content.Client.Hands.Systems OnPlayerItemRemoved?.Invoke(hand.Name, args.Entity); - if (HasComp(args.Entity)) + if (HasComp(args.Entity)) OnPlayerHandUnblocked?.Invoke(hand.Name); } diff --git a/Content.Client/Inventory/StrippableBoundUserInterface.cs b/Content.Client/Inventory/StrippableBoundUserInterface.cs index 9fbb64309f..028c035e8a 100644 --- a/Content.Client/Inventory/StrippableBoundUserInterface.cs +++ b/Content.Client/Inventory/StrippableBoundUserInterface.cs @@ -13,6 +13,7 @@ using Content.Shared.Hands.Components; 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; @@ -159,7 +160,7 @@ namespace Content.Client.Inventory button.Pressed += SlotPressed; - if (EntMan.TryGetComponent(hand.HeldEntity, out var virt)) + if (EntMan.TryGetComponent(hand.HeldEntity, out var virt)) { button.Blocked = true; if (EntMan.TryGetComponent(Owner, out var cuff) && _cuffable.GetAllCuffs(cuff).Contains(virt.BlockingEntity)) @@ -224,7 +225,7 @@ namespace Content.Client.Inventory } EntityUid? viewEnt; - if (EntMan.TryGetComponent(entity, out var virt)) + if (EntMan.TryGetComponent(entity, out var virt)) viewEnt = EntMan.HasComponent(virt.BlockingEntity) ? virt.BlockingEntity : null; else if (EntMan.HasComponent(entity)) viewEnt = entity; diff --git a/Content.Client/Inventory/VirtualItemSystem.cs b/Content.Client/Inventory/VirtualItemSystem.cs new file mode 100644 index 0000000000..721aa11eb9 --- /dev/null +++ b/Content.Client/Inventory/VirtualItemSystem.cs @@ -0,0 +1,15 @@ +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(_ => new HandVirtualItemStatus()); + } +} diff --git a/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs b/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs index b82bcad470..1bcdd89af6 100644 --- a/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs +++ b/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs @@ -5,6 +5,7 @@ using Content.Client.UserInterface.Systems.Hands.Controls; 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; @@ -117,7 +118,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered(hand.HeldEntity)) + if (HasComp(hand.HeldEntity)) return false; if (!_handsSystem.CanDropHeld(target, hand, false)) diff --git a/Content.Server/Verbs/VerbSystem.cs b/Content.Server/Verbs/VerbSystem.cs index e304c6b4af..114c62a0ce 100644 --- a/Content.Server/Verbs/VerbSystem.cs +++ b/Content.Server/Verbs/VerbSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Administration; using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.Hands.Components; +using Content.Shared.Inventory.VirtualItem; using Content.Shared.Verbs; namespace Content.Server.Verbs @@ -96,7 +97,7 @@ 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(); diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index 088323fcc5..99657c87aa 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -18,6 +18,7 @@ using Content.Shared.Interaction; 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; @@ -53,7 +54,7 @@ namespace Content.Shared.Cuffs [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!; @@ -149,7 +150,7 @@ namespace Content.Shared.Cuffs if (args.Container.ID != component.Container?.ID) return; - _handVirtualItem.DeleteInHandsMatching(uid, args.Entity); + _virtualItem.DeleteInHandsMatching(uid, args.Entity); UpdateCuffState(uid, component); } @@ -427,10 +428,10 @@ namespace Content.Shared.Cuffs break; } - if (_handVirtualItem.TrySpawnVirtualItemInHand(handcuff, uid, out var virtItem1)) + if (_virtualItem.TrySpawnVirtualItemInHand(handcuff, uid, out var virtItem1)) EnsureComp(virtItem1.Value); - if (_handVirtualItem.TrySpawnVirtualItemInHand(handcuff, uid, out var virtItem2)) + if (_virtualItem.TrySpawnVirtualItemInHand(handcuff, uid, out var virtItem2)) EnsureComp(virtItem2.Value); } diff --git a/Content.Shared/Hands/Components/HandVirtualItemComponent.cs b/Content.Shared/Hands/Components/HandVirtualItemComponent.cs deleted file mode 100644 index e456013188..0000000000 --- a/Content.Shared/Hands/Components/HandVirtualItemComponent.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Hands.Components; - -[RegisterComponent] -[NetworkedComponent] -[AutoGenerateComponentState(true)] -public sealed partial class HandVirtualItemComponent : Component -{ - /// - /// The entity blocking this hand. - /// - [DataField("blockingEntity"), AutoNetworkedField] - public EntityUid BlockingEntity; -} diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs index 42201ab6af..e071cdc693 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs @@ -1,6 +1,7 @@ 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; @@ -28,8 +29,8 @@ public abstract partial class SharedHandsSystem 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) diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs index c5dd7c6e35..32339eb03a 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs @@ -3,6 +3,7 @@ using Content.Shared.Examine; 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; @@ -183,7 +184,7 @@ public abstract partial class SharedHandsSystem : EntitySystem private void HandleExamined(EntityUid uid, HandsComponent handsComp, ExaminedEvent args) { var held = EnumerateHeld(uid, handsComp) - .Where(x => !HasComp(x)).ToList(); + .Where(x => !HasComp(x)).ToList(); using (args.PushGroup(nameof(HandsComponent))) { diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Virtual.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Virtual.cs deleted file mode 100644 index b83ec8c128..0000000000 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Virtual.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Content.Shared.Hands.Components; - -namespace Content.Shared.Hands.EntitySystems; - -public abstract partial class SharedHandsSystem -{ - private void InitializeVirtual() - { - SubscribeLocalEvent(OnVirtualAfter); - } - - private void OnVirtualAfter(EntityUid uid, HandVirtualItemComponent component, ref AfterAutoHandleStateEvent args) - { - // update hands GUI with new entity. - if (ContainerSystem.IsEntityInContainer(uid)) - _items.VisualsChanged(uid); - } -} diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs index f0a1bcb29c..699fb64ee8 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.ActionBlocker; 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; @@ -20,7 +21,7 @@ public abstract partial class SharedHandsSystem [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?>? OnHandSetActive; @@ -31,7 +32,6 @@ public abstract partial class SharedHandsSystem InitializeInteractions(); InitializeDrop(); InitializePickup(); - InitializeVirtual(); InitializeRelay(); } diff --git a/Content.Shared/Hands/SharedHandVirtualItemSystem.cs b/Content.Shared/Hands/SharedHandVirtualItemSystem.cs deleted file mode 100644 index d2fb86f338..0000000000 --- a/Content.Shared/Hands/SharedHandVirtualItemSystem.cs +++ /dev/null @@ -1,98 +0,0 @@ -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(OnBeingEquippedAttempt); - SubscribeLocalEvent(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(virtualItem.Value); - virtualItemComp.BlockingEntity = blockingEnt; - Dirty(virtualItemComp); - _hands.DoPickup(user, hand, virtualItem.Value); - return true; - } - - - /// - /// Deletes all virtual items in a user's hands with - /// the specified blocked entity. - /// - 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; - } - - /// - /// Queues a deletion for a virtual item and notifies the blocking entity and user. - /// - public void Delete(Entity 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); - } -} diff --git a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs new file mode 100644 index 0000000000..e45530e458 --- /dev/null +++ b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs @@ -0,0 +1,215 @@ +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; + +/// +/// In charge of managing virtual items. +/// Virtual items are used to block a +/// or a with a non-existent item that +/// is a visual copy of another for whatever use +/// +/// +/// The slot visuals are managed by +/// and , see the +/// references there for more information +/// +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] + private const string VirtualItem = "VirtualItem"; + + public override void Initialize() + { + SubscribeLocalEvent(OnAfterAutoHandleState); + + SubscribeLocalEvent(OnBeingEquippedAttempt); + SubscribeLocalEvent(OnBeingUnequippedAttempt); + + SubscribeLocalEvent(OnBeforeRangedInteract); + } + + /// + /// Updates the GUI buttons with the new entity. + /// + private void OnAfterAutoHandleState(Entity ent, ref AfterAutoHandleStateEvent args) + { + if (_containerSystem.IsEntityInContainer(ent)) + _itemSystem.VisualsChanged(ent); + } + + private void OnBeingEquippedAttempt(Entity ent, ref BeingEquippedAttemptEvent args) + { + // No interactions with a virtual item, please. + args.Cancel(); + } + + private void OnBeingUnequippedAttempt(Entity ent, ref BeingUnequippedAttemptEvent args) + { + // No interactions with a virtual item, please. + args.Cancel(); + } + + private void OnBeforeRangedInteract(Entity ent, ref BeforeRangedInteractEvent args) + { + // No interactions with a virtual item, please. + args.Handled = true; + } + + #region Hands + /// + /// Spawns a virtual item in a empty hand + /// + /// The entity we will make a virtual entity copy of + /// The entity that we want to insert the virtual entity + 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 (!TrySpawnVirtualItem(blockingEnt, user, out virtualItem) || !_handsSystem.TryGetEmptyHand(user, out var hand)) + return false; + + _handsSystem.DoPickup(user, hand, virtualItem.Value); + return true; + } + + /// + /// Scan the user's hands until we find the virtual entity, if the + /// virtual entity is a copy of the matching entity, delete it + /// + 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 + + /// + /// Spawns a virtual item inside a inventory slot + /// + /// The entity we will make a virtual entity copy of + /// The entity that we want to insert the virtual entity + /// The slot to which we will insert the virtual entity (could be the "shoes" slot, for example) + public bool TrySpawnVirtualItemInInventory(EntityUid blockingEnt, EntityUid user, string slot, bool force = false) + { + return TrySpawnVirtualItemInInventory(blockingEnt, user, slot, force, out _); + } + + /// + 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; + } + + /// + /// 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 + /// + /// Set this param if you have the name of the slot, it avoids unnecessary queries + 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 + + /// + /// Spawns a virtual item and setups the component without any special handling + /// + /// The entity we will make a virtual entity copy of + /// The entity that we want to insert the virtual entity + 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(virtualItem.Value); + virtualItemComp.BlockingEntity = blockingEnt; + Dirty(virtualItem.Value, virtualItemComp); + return true; + } + + /// + /// Queues a deletion for a virtual item and notifies the blocking entity and user. + /// + public void DeleteVirtualItem(Entity 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); + } +} diff --git a/Content.Shared/Inventory/VirtualItem/VirtualItemComponent.cs b/Content.Shared/Inventory/VirtualItem/VirtualItemComponent.cs new file mode 100644 index 0000000000..cda1be5b99 --- /dev/null +++ b/Content.Shared/Inventory/VirtualItem/VirtualItemComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Inventory.VirtualItem; + +/// +[RegisterComponent] +[NetworkedComponent] +[AutoGenerateComponentState(true)] +public sealed partial class VirtualItemComponent : Component +{ + /// + /// The entity blocking this slot. + /// + [DataField, AutoNetworkedField] + public EntityUid BlockingEntity; +} diff --git a/Content.Shared/Vehicle/SharedVehicleSystem.cs b/Content.Shared/Vehicle/SharedVehicleSystem.cs index 3775ab1ec1..50143ae551 100644 --- a/Content.Shared/Vehicle/SharedVehicleSystem.cs +++ b/Content.Shared/Vehicle/SharedVehicleSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Audio; 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; @@ -37,7 +38,7 @@ public abstract partial class SharedVehicleSystem : EntitySystem [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!; diff --git a/Content.Shared/Verbs/SharedVerbSystem.cs b/Content.Shared/Verbs/SharedVerbSystem.cs index e6286c47cc..9dda910f3f 100644 --- a/Content.Shared/Verbs/SharedVerbSystem.cs +++ b/Content.Shared/Verbs/SharedVerbSystem.cs @@ -1,6 +1,7 @@ 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 @@ -97,7 +98,7 @@ 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; } diff --git a/Content.Shared/Wieldable/WieldableSystem.cs b/Content.Shared/Wieldable/WieldableSystem.cs index 0f6422241c..54b030b9eb 100644 --- a/Content.Shared/Wieldable/WieldableSystem.cs +++ b/Content.Shared/Wieldable/WieldableSystem.cs @@ -2,6 +2,7 @@ using Content.Shared.Hands; 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; @@ -19,7 +20,7 @@ namespace Content.Shared.Wieldable; 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!; diff --git a/Resources/Prototypes/Entities/Virtual/virtual_item.yml b/Resources/Prototypes/Entities/Virtual/virtual_item.yml index f397f9a61a..ed74243550 100644 --- a/Resources/Prototypes/Entities/Virtual/virtual_item.yml +++ b/Resources/Prototypes/Entities/Virtual/virtual_item.yml @@ -1,8 +1,8 @@ # 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