diff --git a/Content.Client/Storage/UI/StorageWindow.cs b/Content.Client/Storage/UI/StorageWindow.cs index eca118d762..55ac04679f 100644 --- a/Content.Client/Storage/UI/StorageWindow.cs +++ b/Content.Client/Storage/UI/StorageWindow.cs @@ -5,7 +5,9 @@ using Robust.Client.UserInterface.CustomControls; using Content.Client.Items.Components; using Content.Client.Stylesheets; using Content.Client.UserInterface.Controls; +using Content.Shared.IdentityManagement; using Content.Shared.Item; +using Content.Shared.Stacks; using Robust.Client.UserInterface; using static Robust.Client.UserInterface.Controls.BoxContainer; using static Content.Shared.Storage.SharedStorageComponent; @@ -28,7 +30,7 @@ namespace Content.Client.Storage.UI public StorageWindow(IEntityManager entityManager) { _entityManager = entityManager; - SetSize = (200, 320); + SetSize = (240, 320); Title = Loc.GetString("comp-storage-window-title"); RectClipContent = true; @@ -112,6 +114,9 @@ namespace Content.Client.Storage.UI _entityManager.TryGetComponent(entity, out SpriteComponent? sprite); _entityManager.TryGetComponent(entity, out ItemComponent? item); + _entityManager.TryGetComponent(entity, out StackComponent? stack); + var count = stack?.Count ?? 1; + var size = item?.Size; button.AddChild(new BoxContainer { @@ -131,12 +136,13 @@ namespace Content.Client.Storage.UI { HorizontalExpand = true, ClipText = true, - Text = _entityManager.GetComponent(entity).EntityName + Text = _entityManager.GetComponent(Identity.Entity(entity, _entityManager)).EntityName + + (count > 1 ? $" x {count}" : string.Empty), }, new Label { Align = Label.AlignMode.Right, - Text = item?.Size.ToString() ?? Loc.GetString("comp-storage-no-item-size"), + Text = size.ToString() ?? Loc.GetString("comp-storage-no-item-size"), } } }); diff --git a/Content.Server/Stack/StackSystem.cs b/Content.Server/Stack/StackSystem.cs index 4e2d099500..f7dd3a8583 100644 --- a/Content.Server/Stack/StackSystem.cs +++ b/Content.Server/Stack/StackSystem.cs @@ -1,7 +1,10 @@ +using Content.Server.Storage.Components; +using Content.Server.Storage.EntitySystems; using Content.Shared.Popups; using Content.Shared.Stacks; using Content.Shared.Verbs; using JetBrains.Annotations; +using Robust.Server.Containers; using Robust.Shared.Map; using Robust.Shared.Prototypes; @@ -14,6 +17,8 @@ namespace Content.Server.Stack [UsedImplicitly] public sealed class StackSystem : SharedStackSystem { + [Dependency] private readonly ContainerSystem _container = default!; + [Dependency] private readonly StorageSystem _storage = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; public static readonly int[] DefaultSplitAmounts = { 1, 5, 10, 20, 30, 50 }; @@ -160,6 +165,12 @@ namespace Content.Server.Stack if (Split(uid, amount, userTransform.Coordinates, stack) is not {} split) return; + if (_container.TryGetContainingContainer(uid, out var container) && + TryComp(container.Owner, out var storage)) + { + _storage.UpdateStorageUI(container.Owner, storage); + } + HandsSystem.PickupOrDrop(userUid, split); PopupSystem.PopupCursor(Loc.GetString("comp-stack-split"), userUid); diff --git a/Content.Server/Storage/EntitySystems/StorageSystem.cs b/Content.Server/Storage/EntitySystems/StorageSystem.cs index fc05ba9121..829101dccf 100644 --- a/Content.Server/Storage/EntitySystems/StorageSystem.cs +++ b/Content.Server/Storage/EntitySystems/StorageSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Administration.Managers; using Content.Server.Ghost.Components; using Content.Server.Interaction; using Content.Server.Popups; +using Content.Server.Stack; using Content.Server.Storage.Components; using Content.Shared.ActionBlocker; using Content.Shared.Administration; @@ -37,10 +38,8 @@ using static Content.Shared.Storage.SharedStorageComponent; namespace Content.Server.Storage.EntitySystems { - [UsedImplicitly] public sealed partial class StorageSystem : EntitySystem { - [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IAdminManager _admin = default!; [Dependency] private readonly ContainerSystem _containerSystem = default!; @@ -57,6 +56,7 @@ namespace Content.Server.Storage.EntitySystems [Dependency] private readonly SharedCombatModeSystem _combatMode = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; /// @@ -423,7 +423,7 @@ namespace Content.Server.Storage.EntitySystems _appearance.SetData(uid, StackVisuals.Hide, !storageComp.IsOpen); } - private void RecalculateStorageUsed(ServerStorageComponent storageComp) + public void RecalculateStorageUsed(ServerStorageComponent storageComp) { storageComp.StorageUsed = 0; storageComp.SizeCache.Clear(); @@ -438,11 +438,20 @@ namespace Content.Server.Storage.EntitySystems if (!itemQuery.TryGetComponent(entity, out var itemComp)) continue; - storageComp.StorageUsed += itemComp.Size; - storageComp.SizeCache.Add(entity, itemComp.Size); + var size = itemComp.Size; + storageComp.StorageUsed += size; + storageComp.SizeCache.Add(entity, size); } } + public int GetAvailableSpace(EntityUid uid, ServerStorageComponent? component = null) + { + if (!Resolve(uid, ref component)) + return 0; + + return component.StorageCapacityMax - component.StorageUsed; + } + /// /// Move entities from one storage to another. /// @@ -508,7 +517,7 @@ namespace Content.Server.Storage.EntitySystems return false; } - if (TryComp(insertEnt, out ItemComponent? itemComp) && + if (!HasComp(insertEnt) && TryComp(insertEnt, out ItemComponent? itemComp) && itemComp.Size > storageComp.StorageCapacityMax - storageComp.StorageUsed) { reason = "comp-storage-insufficient-capacity"; @@ -525,9 +534,63 @@ namespace Content.Server.Storage.EntitySystems /// true if the entity was inserted, false otherwise public bool Insert(EntityUid uid, EntityUid insertEnt, ServerStorageComponent? storageComp = null, bool playSound = true) { - if (!Resolve(uid, ref storageComp) || !CanInsert(uid, insertEnt, out _, storageComp) || storageComp.Storage?.Insert(insertEnt) == false) + if (!Resolve(uid, ref storageComp) || !CanInsert(uid, insertEnt, out _, storageComp) || storageComp.Storage == null) return false; + /* + * 1. If the inserted thing is stackable then try to stack it to existing stacks + * 2. If anything remains insert whatever is possible. + * 3. If insertion is not possible then leave the stack as is. + * At either rate still play the insertion sound + * + * For now we just treat items as always being the same size regardless of stack count. + */ + + // If it's stackable then prefer to stack it + var stackQuery = GetEntityQuery(); + + if (stackQuery.TryGetComponent(insertEnt, out var insertStack)) + { + var toInsertCount = insertStack.Count; + + foreach (var ent in storageComp.Storage.ContainedEntities) + { + if (!stackQuery.TryGetComponent(ent, out var containedStack) || !insertStack.StackTypeId.Equals(containedStack.StackTypeId)) + continue; + + if (!_stack.TryAdd(insertEnt, ent, insertStack, containedStack)) + continue; + + var remaining = insertStack.Count; + toInsertCount -= toInsertCount - remaining; + + if (remaining > 0) + continue; + + break; + } + + // Still stackable remaining + if (insertStack.Count > 0) + { + // Try to insert it as a new stack. + if (TryComp(insertEnt, out ItemComponent? itemComp) && + itemComp.Size > storageComp.StorageCapacityMax - storageComp.StorageUsed || + !storageComp.Storage.Insert(insertEnt)) + { + // If we also didn't do any stack fills above then just end + // otherwise play sound and update UI anyway. + if (toInsertCount == insertStack.Count) + return false; + } + } + } + // Non-stackable but no insertion for reasons. + else if (!storageComp.Storage.Insert(insertEnt)) + { + return false; + } + if (playSound && storageComp.StorageInsertSound is not null) _audio.PlayPvs(storageComp.StorageInsertSound, uid); @@ -643,7 +706,7 @@ namespace Content.Server.Storage.EntitySystems } } - private void UpdateStorageUI(EntityUid uid, ServerStorageComponent storageComp) + public void UpdateStorageUI(EntityUid uid, ServerStorageComponent storageComp) { if (storageComp.Storage == null) return; diff --git a/Content.Shared/Stacks/SharedStackSystem.cs b/Content.Shared/Stacks/SharedStackSystem.cs index b34498677c..0882777c4a 100644 --- a/Content.Shared/Stacks/SharedStackSystem.cs +++ b/Content.Shared/Stacks/SharedStackSystem.cs @@ -105,7 +105,7 @@ namespace Content.Shared.Stacks if (!Resolve(recipient, ref recipientStack, false) || !Resolve(donor, ref donorStack, false)) return false; - if (recipientStack.StackTypeId == null || !recipientStack.StackTypeId.Equals(donorStack.StackTypeId)) + if (string.IsNullOrEmpty(recipientStack.StackTypeId) || !recipientStack.StackTypeId.Equals(donorStack.StackTypeId)) return false; transfered = Math.Min(donorStack.Count, GetAvailableSpace(recipientStack)); @@ -270,7 +270,7 @@ namespace Content.Shared.Stacks if (component.MaxCountOverride != null) return component.MaxCountOverride.Value; - if (component.StackTypeId == null) + if (string.IsNullOrEmpty(component.StackTypeId)) return 1; var stackProto = _prototype.Index(component.StackTypeId); @@ -289,6 +289,38 @@ namespace Content.Shared.Stacks return GetMaxCount(component) - component.Count; } + /// + /// Tries to add one stack to another. May have some leftover count in the inserted entity. + /// + public bool TryAdd(EntityUid insertEnt, EntityUid targetEnt, StackComponent? insertStack = null, StackComponent? targetStack = null) + { + if (!Resolve(insertEnt, ref insertStack) || !Resolve(targetEnt, ref targetStack)) + return false; + + var count = insertStack.Count; + return TryAdd(insertEnt, targetEnt, count, insertStack, targetStack); + } + + /// + /// Tries to add one stack to another. May have some leftover count in the inserted entity. + /// + public bool TryAdd(EntityUid insertEnt, EntityUid targetEnt, int count, StackComponent? insertStack = null, StackComponent? targetStack = null) + { + if (!Resolve(insertEnt, ref insertStack) || !Resolve(targetEnt, ref targetStack)) + return false; + + var available = GetAvailableSpace(targetStack); + + if (available <= 0) + return false; + + var change = Math.Min(available, count); + + SetCount(targetEnt, targetStack.Count + change, targetStack); + SetCount(insertEnt, insertStack.Count - change, insertStack); + return true; + } + private void OnStackStarted(EntityUid uid, StackComponent component, ComponentStartup args) { if (!TryComp(uid, out AppearanceComponent? appearance)) diff --git a/Resources/Prototypes/Entities/Objects/Materials/ore.yml b/Resources/Prototypes/Entities/Objects/Materials/ore.yml index 3690cf7963..136405704e 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/ore.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/ore.yml @@ -9,6 +9,7 @@ sprite: Objects/Materials/ore.rsi - type: Item sprite: Objects/Materials/ore.rsi + size: 30 - type: ItemStatus - type: Tag tags: diff --git a/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml b/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml index 43339e2442..4c0dd01330 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml @@ -17,9 +17,9 @@ stackType: Cable - type: Sprite sprite: Objects/Tools/cable-coils.rsi - netsync: false - type: Item sprite: Objects/Tools/cable-coils.rsi + size: 10 - type: CablePlacer - type: Clickable - type: StaticPrice @@ -39,7 +39,6 @@ - type: Sprite state: coilhv-30 - type: Item - size: 10 heldPrefix: coilhv - type: CablePlacer cablePrototypeID: CableHV @@ -59,8 +58,6 @@ components: - type: Sprite state: coilhv-10 - - type: Item - size: 3 - type: Stack count: 1 @@ -76,7 +73,6 @@ - type: Sprite state: coilmv-30 - type: Item - size: 10 heldPrefix: coilmv - type: CablePlacer cablePrototypeID: CableMV @@ -96,8 +92,6 @@ components: - type: Sprite state: coilmv-10 - - type: Item - size: 3 - type: Stack count: 1 @@ -111,7 +105,6 @@ - type: Sprite state: coillv-30 - type: Item - size: 10 heldPrefix: coillv - type: CablePlacer cablePrototypeID: CableApcExtension @@ -131,7 +124,5 @@ components: - type: Sprite state: coillv-10 - - type: Item - size: 3 - type: Stack count: 1