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
This commit is contained in:
metalgearsloth
2025-04-18 12:11:31 +10:00
committed by GitHub
parent 5e10bd91e3
commit e1a21728e7
8 changed files with 160 additions and 29 deletions

View File

@@ -269,6 +269,12 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
var position = targetStorage.GetMouseGridPieceLocation(dragEnt, dragLoc);
var newLocation = new ItemStorageLocation(DraggingRotation, position);
if (!_storage.ItemFitsInGridLocation(dragEnt, sourceStorage, newLocation))
{
window.Reclaim(control.Location, control);
}
else
{
EntityManager.RaisePredictiveEvent(new StorageSetItemLocationEvent(
EntityManager.GetNetEntity(draggingGhost.Entity),
EntityManager.GetNetEntity(sourceStorage),
@@ -276,6 +282,7 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
window.Reclaim(newLocation, control);
}
}
// Dragging to new storage
else if (targetStorage?.StorageEntity != null && targetStorage != window)
{
@@ -336,6 +343,17 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
if (DraggingGhost == null)
return false;
var player = _player.LocalEntity;
// If the attached storage is closed then stop dragging
if (player == null ||
!_storage.TryGetStorageLocation(DraggingGhost.Entity, out var container, out _, out _) ||
!_ui.IsUiOpen(container.Owner, StorageComponent.StorageUiKey.Key, player.Value))
{
DraggingGhost.Orphan();
return false;
}
SetDraggingRotation();
return true;
}

View File

@@ -0,0 +1,10 @@
namespace Content.Shared.Item;
/// <summary>
/// Raised directed on an entity when its item size / shape changes.
/// </summary>
[ByRefEvent]
public struct ItemSizeChangedEvent(EntityUid Entity)
{
public EntityUid Entity = Entity;
}

View File

@@ -78,13 +78,18 @@ public sealed partial class ItemToggleComponent : Component
[ByRefEvent]
public record struct ItemToggleActivateAttemptEvent(EntityUid? User)
{
/// <summary>
/// Should we silently fail.
/// </summary>
public bool Silent = false;
public bool Cancelled = false;
public readonly EntityUid? User = User;
/// <summary>
/// Pop-up that gets shown to users explaining why the attempt was cancelled.
/// </summary>
public string? Popup { get; set; }
public string? Popup;
}
/// <summary>
@@ -93,8 +98,18 @@ public record struct ItemToggleActivateAttemptEvent(EntityUid? User)
[ByRefEvent]
public record struct ItemToggleDeactivateAttemptEvent(EntityUid? User)
{
/// <summary>
/// Should we silently fail.
/// </summary>
public bool Silent = false;
public bool Cancelled = false;
public readonly EntityUid? User = User;
/// <summary>
/// Pop-up that gets shown to users explaining why the attempt was cancelled.
/// </summary>
public string? Popup;
}
/// <summary>

View File

@@ -15,24 +15,24 @@ public sealed partial class ItemToggleSizeComponent : Component
/// <summary>
/// Item's size when activated
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
[DataField, AutoNetworkedField]
public ProtoId<ItemSizePrototype>? ActivatedSize = null;
/// <summary>
/// Item's shape when activated
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField]
[DataField, AutoNetworkedField]
public List<Box2i>? ActivatedShape = null;
/// <summary>
/// Item's size when deactivated. If none is mentioned, it uses the item's default size instead.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
[DataField, AutoNetworkedField]
public ProtoId<ItemSizePrototype>? DeactivatedSize = null;
/// <summary>
/// Item's shape when deactivated. If none is mentioned, it uses the item's default shape instead.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField]
[DataField, AutoNetworkedField]
public List<Box2i>? DeactivatedShape = null;
}

View File

@@ -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;
}

View File

@@ -39,20 +39,24 @@ public abstract class SharedItemSystem : EntitySystem
public void SetSize(EntityUid uid, ProtoId<ItemSizePrototype> 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<Box2i>? 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);
}
/// <summary>
@@ -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<Box2i>(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<ItemSizePrototype>) itemToggleSize.ActivatedSize, item);
}
}
@@ -258,7 +264,5 @@ public abstract class SharedItemSystem : EntitySystem
SetSize(uid, (ProtoId<ItemSizePrototype>) itemToggleSize.DeactivatedSize, item);
}
}
Dirty(uid, item);
}
}

View File

@@ -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<StorageComponent, BoundUserInterfaceMessageAttempt>(OnBoundUIAttempt);
SubscribeLocalEvent<StorageComponent, BoundUIOpenedEvent>(OnBoundUIOpen);
SubscribeLocalEvent<StorageComponent, LockToggledEvent>(OnLockToggled);
SubscribeLocalEvent<MetaDataComponent, StackCountChangedEvent>(OnStackCountChanged);
SubscribeLocalEvent<StorageComponent, EntInsertedIntoContainerMessage>(OnEntInserted);
SubscribeLocalEvent<StorageComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
SubscribeLocalEvent<StorageComponent, ContainerIsInsertingAttemptEvent>(OnInsertAttempt);
SubscribeLocalEvent<StorageComponent, AreaPickupDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<StorageComponent, GotReclaimedEvent>(OnReclaimed);
SubscribeLocalEvent<MetaDataComponent, StackCountChangedEvent>(OnStackCountChanged);
SubscribeAllEvent<OpenNestedStorageEvent>(OnStorageNested);
SubscribeAllEvent<StorageTransferItemEvent>(OnStorageTransfer);
@@ -161,7 +162,7 @@ public abstract class SharedStorageSystem : EntitySystem
SubscribeAllEvent<StorageInsertItemIntoLocationEvent>(OnInsertItemIntoLocation);
SubscribeAllEvent<StorageSaveItemLocationEvent>(OnSaveItemLocation);
SubscribeLocalEvent<StorageComponent, GotReclaimedEvent>(OnReclaimed);
SubscribeLocalEvent<ItemSizeChangedEvent>(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<ItemComponent?>(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);
}
/// <summary>
/// Tries to get the storage location of an item.
/// </summary>
public bool TryGetStorageLocation(Entity<ItemComponent?> 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<ItemComponent?>(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");

View File

@@ -1,2 +1,4 @@
item-toggle-activate = Activate
item-toggle-deactivate = Deactivate
item-toggle-size-fail = Doesn't fit.