From e1a21728e79436fd5ce60e7277d1d1841a4af57e Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Fri, 18 Apr 2025 12:11:31 +1000 Subject: [PATCH] Storage fixes (#36533) * Storage fixes - Add size event. - Fix reclaim not running always. * Block the toggles * Standardise and popup explaining * Disable item toggles in bags * Fix verb popping up even if we can't activate * Conflicts * Validate * Stop drags if source closes * Really fixes * Real fix * Revert this --- .../Systems/Storage/StorageUIController.cs | 28 +++++++-- Content.Shared/Item/ItemSizeChangedEvent.cs | 10 ++++ .../Components/ItemToggleComponent.cs | 17 +++++- .../Components/ItemToggleSizeComponent.cs | 8 +-- .../Item/ItemToggle/ItemToggleSystem.cs | 54 ++++++++++++++--- Content.Shared/Item/SharedItemSystem.cs | 12 ++-- .../EntitySystems/SharedStorageSystem.cs | 58 +++++++++++++++++-- Resources/Locale/en-US/items/toggle.ftl | 2 + 8 files changed, 160 insertions(+), 29 deletions(-) create mode 100644 Content.Shared/Item/ItemSizeChangedEvent.cs diff --git a/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs b/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs index f0c8cfeb21..ca2bc5729b 100644 --- a/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs +++ b/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs @@ -269,12 +269,19 @@ public sealed class StorageUIController : UIController, IOnSystemChanged +/// Raised directed on an entity when its item size / shape changes. +/// +[ByRefEvent] +public struct ItemSizeChangedEvent(EntityUid Entity) +{ + public EntityUid Entity = Entity; +} diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs index 110ae80626..cb6470f5d6 100644 --- a/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs @@ -78,13 +78,18 @@ public sealed partial class ItemToggleComponent : Component [ByRefEvent] public record struct ItemToggleActivateAttemptEvent(EntityUid? User) { + /// + /// Should we silently fail. + /// + public bool Silent = false; + public bool Cancelled = false; public readonly EntityUid? User = User; /// /// Pop-up that gets shown to users explaining why the attempt was cancelled. /// - public string? Popup { get; set; } + public string? Popup; } /// @@ -93,8 +98,18 @@ public record struct ItemToggleActivateAttemptEvent(EntityUid? User) [ByRefEvent] public record struct ItemToggleDeactivateAttemptEvent(EntityUid? User) { + /// + /// Should we silently fail. + /// + public bool Silent = false; + public bool Cancelled = false; public readonly EntityUid? User = User; + + /// + /// Pop-up that gets shown to users explaining why the attempt was cancelled. + /// + public string? Popup; } /// diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleSizeComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleSizeComponent.cs index b7b47a0732..2870f9ef01 100644 --- a/Content.Shared/Item/ItemToggle/Components/ItemToggleSizeComponent.cs +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleSizeComponent.cs @@ -15,24 +15,24 @@ public sealed partial class ItemToggleSizeComponent : Component /// /// Item's size when activated /// - [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + [DataField, AutoNetworkedField] public ProtoId? ActivatedSize = null; /// /// Item's shape when activated /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField, AutoNetworkedField] public List? ActivatedShape = null; /// /// Item's size when deactivated. If none is mentioned, it uses the item's default size instead. /// - [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + [DataField, AutoNetworkedField] public ProtoId? DeactivatedSize = null; /// /// Item's shape when deactivated. If none is mentioned, it uses the item's default shape instead. /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField, AutoNetworkedField] public List? DeactivatedShape = null; } diff --git a/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs index f6752a67f6..819975ecda 100644 --- a/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs +++ b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs @@ -76,6 +76,23 @@ public sealed class ItemToggleSystem : EntitySystem var user = args.User; + if (ent.Comp.Activated) + { + var ev = new ItemToggleActivateAttemptEvent(args.User); + RaiseLocalEvent(ent.Owner, ref ev); + + if (ev.Cancelled) + return; + } + else + { + var ev = new ItemToggleDeactivateAttemptEvent(args.User); + RaiseLocalEvent(ent.Owner, ref ev); + + if (ev.Cancelled) + return; + } + args.Verbs.Add(new ActivationVerb() { Text = !ent.Comp.Activated ? Loc.GetString(ent.Comp.VerbToggleOn) : Loc.GetString(ent.Comp.VerbToggleOff), @@ -133,15 +150,20 @@ public sealed class ItemToggleSystem : EntitySystem if (comp.Activated) return true; - if (!comp.Predictable && _netManager.IsClient) - return true; - var attempt = new ItemToggleActivateAttemptEvent(user); RaiseLocalEvent(uid, ref attempt); - if (!comp.Predictable) predicted = false; + if (!comp.Predictable) + predicted = false; + + if (!predicted && _netManager.IsClient) + return false; + if (attempt.Cancelled) { + if (attempt.Silent) + return false; + if (predicted) _audio.PlayPredicted(comp.SoundFailToActivate, uid, user); else @@ -159,7 +181,6 @@ public sealed class ItemToggleSystem : EntitySystem } Activate((uid, comp), predicted, user); - return true; } @@ -176,16 +197,31 @@ public sealed class ItemToggleSystem : EntitySystem if (!comp.Activated) return true; - if (!comp.Predictable && _netManager.IsClient) - return true; + if (!comp.Predictable) + predicted = false; var attempt = new ItemToggleDeactivateAttemptEvent(user); RaiseLocalEvent(uid, ref attempt); - if (attempt.Cancelled) + if (!predicted && _netManager.IsClient) return false; - if (!comp.Predictable) predicted = false; + if (attempt.Cancelled) + { + if (attempt.Silent) + return false; + + if (attempt.Popup != null && user != null) + { + if (predicted) + _popup.PopupClient(attempt.Popup, uid, user.Value); + else + _popup.PopupEntity(attempt.Popup, uid, user.Value); + } + + return false; + } + Deactivate((uid, comp), predicted, user); return true; } diff --git a/Content.Shared/Item/SharedItemSystem.cs b/Content.Shared/Item/SharedItemSystem.cs index 8ba26fd349..18a98c7a0c 100644 --- a/Content.Shared/Item/SharedItemSystem.cs +++ b/Content.Shared/Item/SharedItemSystem.cs @@ -39,20 +39,24 @@ public abstract class SharedItemSystem : EntitySystem public void SetSize(EntityUid uid, ProtoId size, ItemComponent? component = null) { - if (!Resolve(uid, ref component, false)) + if (!Resolve(uid, ref component, false) || component.Size == size) return; component.Size = size; Dirty(uid, component); + var ev = new ItemSizeChangedEvent(uid); + RaiseLocalEvent(uid, ref ev, broadcast: true); } public void SetShape(EntityUid uid, List? shape, ItemComponent? component = null) { - if (!Resolve(uid, ref component, false)) + if (!Resolve(uid, ref component, false) || component.Shape == shape) return; component.Shape = shape; Dirty(uid, component); + var ev = new ItemSizeChangedEvent(uid); + RaiseLocalEvent(uid, ref ev, broadcast: true); } /// @@ -236,6 +240,7 @@ public abstract class SharedItemSystem : EntitySystem { // Set the deactivated shape to the default item's shape before it gets changed. itemToggleSize.DeactivatedShape ??= new List(GetItemShape(item)); + Dirty(uid, itemToggleSize); SetShape(uid, itemToggleSize.ActivatedShape, item); } @@ -243,6 +248,7 @@ public abstract class SharedItemSystem : EntitySystem { // Set the deactivated size to the default item's size before it gets changed. itemToggleSize.DeactivatedSize ??= item.Size; + Dirty(uid, itemToggleSize); SetSize(uid, (ProtoId) itemToggleSize.ActivatedSize, item); } } @@ -258,7 +264,5 @@ public abstract class SharedItemSystem : EntitySystem SetSize(uid, (ProtoId) itemToggleSize.DeactivatedSize, item); } } - - Dirty(uid, item); } } diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 3bf51f7957..e9ed89f560 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -16,6 +16,7 @@ using Content.Shared.Interaction; using Content.Shared.Interaction.Components; using Content.Shared.Inventory; using Content.Shared.Item; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Lock; using Content.Shared.Materials; using Content.Shared.Placeable; @@ -146,13 +147,13 @@ public abstract class SharedStorageSystem : EntitySystem SubscribeLocalEvent(OnBoundUIAttempt); SubscribeLocalEvent(OnBoundUIOpen); SubscribeLocalEvent(OnLockToggled); - SubscribeLocalEvent(OnStackCountChanged); - SubscribeLocalEvent(OnEntInserted); SubscribeLocalEvent(OnEntRemoved); SubscribeLocalEvent(OnInsertAttempt); - SubscribeLocalEvent(OnDoAfter); + SubscribeLocalEvent(OnReclaimed); + + SubscribeLocalEvent(OnStackCountChanged); SubscribeAllEvent(OnStorageNested); SubscribeAllEvent(OnStorageTransfer); @@ -161,7 +162,7 @@ public abstract class SharedStorageSystem : EntitySystem SubscribeAllEvent(OnInsertItemIntoLocation); SubscribeAllEvent(OnSaveItemLocation); - SubscribeLocalEvent(OnReclaimed); + SubscribeLocalEvent(OnItemSizeChanged); CommandBinds.Builder .Bind(ContentKeyFunctions.OpenBackpack, InputCmdHandler.FromDelegate(HandleOpenBackpack, handle: false)) @@ -173,6 +174,21 @@ public abstract class SharedStorageSystem : EntitySystem UpdatePrototypeCache(); } + private void OnItemSizeChanged(ref ItemSizeChangedEvent ev) + { + var itemEnt = new Entity(ev.Entity, null); + + if (!TryGetStorageLocation(itemEnt, out var container, out var storage, out var loc)) + { + return; + } + + if (!ItemFitsInGridLocation((itemEnt.Owner, itemEnt.Comp), (container.Owner, storage), loc)) + { + ContainerSystem.Remove(itemEnt.Owner, container, force: true); + } + } + private void OnNestedStorageCvar(bool obj) { NestedStorage = obj; @@ -322,6 +338,25 @@ public abstract class SharedStorageSystem : EntitySystem args.Verbs.Add(verb); } + /// + /// Tries to get the storage location of an item. + /// + public bool TryGetStorageLocation(Entity itemEnt, [NotNullWhen(true)] out BaseContainer? container, out StorageComponent? storage, out ItemStorageLocation loc) + { + loc = default; + storage = null; + + if (!ContainerSystem.TryGetContainingContainer(itemEnt, out container) || + !TryComp(container.Owner, out storage) || + !_itemQuery.Resolve(itemEnt, ref itemEnt.Comp, false)) + { + return false; + } + + loc = storage.StoredItems[itemEnt]; + return true; + } + public void OpenStorageUI(EntityUid uid, EntityUid actor, StorageComponent? storageComp = null, bool silent = true) { // Handle recursively opening nested storages. @@ -719,14 +754,23 @@ public abstract class SharedStorageSystem : EntitySystem private void OnStorageTransfer(StorageTransferItemEvent msg, EntitySessionEventArgs args) { - if (!TryGetEntity(msg.ItemEnt, out var itemEnt)) + if (!TryGetEntity(msg.ItemEnt, out var itemUid) || !TryComp(itemUid, out ItemComponent? itemComp)) return; var localPlayer = args.SenderSession.AttachedEntity; + var itemEnt = new Entity(itemUid.Value, itemComp); - if (!TryComp(localPlayer, out HandsComponent? handsComp) || !_sharedHandsSystem.TryPickup(localPlayer.Value, itemEnt.Value, handsComp: handsComp, animate: false)) + // Validate the source storage + if (!TryGetStorageLocation(itemEnt, out var container, out _, out _) || + !ValidateInput(args, GetNetEntity(container.Owner), out _, out _)) + { + return; + } + + if (!TryComp(localPlayer, out HandsComponent? handsComp) || !_sharedHandsSystem.TryPickup(localPlayer.Value, itemEnt, handsComp: handsComp, animate: false)) return; + // Validate the target storage if (!ValidateInput(args, msg.StorageEnt, msg.ItemEnt, out var player, out var storage, out var item, held: true)) return; @@ -1525,6 +1569,8 @@ public abstract class SharedStorageSystem : EntitySystem } } + + private void HandleOpenBackpack(ICommonSession? session) { HandleToggleSlotUI(session, "back"); diff --git a/Resources/Locale/en-US/items/toggle.ftl b/Resources/Locale/en-US/items/toggle.ftl index bcf5c161a6..7f5dfea7c6 100644 --- a/Resources/Locale/en-US/items/toggle.ftl +++ b/Resources/Locale/en-US/items/toggle.ftl @@ -1,2 +1,4 @@ item-toggle-activate = Activate item-toggle-deactivate = Deactivate + +item-toggle-size-fail = Doesn't fit.