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 position = targetStorage.GetMouseGridPieceLocation(dragEnt, dragLoc);
var newLocation = new ItemStorageLocation(DraggingRotation, position); var newLocation = new ItemStorageLocation(DraggingRotation, position);
if (!_storage.ItemFitsInGridLocation(dragEnt, sourceStorage, newLocation))
{
window.Reclaim(control.Location, control);
}
else
{
EntityManager.RaisePredictiveEvent(new StorageSetItemLocationEvent( EntityManager.RaisePredictiveEvent(new StorageSetItemLocationEvent(
EntityManager.GetNetEntity(draggingGhost.Entity), EntityManager.GetNetEntity(draggingGhost.Entity),
EntityManager.GetNetEntity(sourceStorage), EntityManager.GetNetEntity(sourceStorage),
@@ -276,6 +282,7 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
window.Reclaim(newLocation, control); window.Reclaim(newLocation, control);
} }
}
// Dragging to new storage // Dragging to new storage
else if (targetStorage?.StorageEntity != null && targetStorage != window) else if (targetStorage?.StorageEntity != null && targetStorage != window)
{ {
@@ -336,6 +343,17 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
if (DraggingGhost == null) if (DraggingGhost == null)
return false; 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(); SetDraggingRotation();
return true; 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] [ByRefEvent]
public record struct ItemToggleActivateAttemptEvent(EntityUid? User) public record struct ItemToggleActivateAttemptEvent(EntityUid? User)
{ {
/// <summary>
/// Should we silently fail.
/// </summary>
public bool Silent = false;
public bool Cancelled = false; public bool Cancelled = false;
public readonly EntityUid? User = User; public readonly EntityUid? User = User;
/// <summary> /// <summary>
/// Pop-up that gets shown to users explaining why the attempt was cancelled. /// Pop-up that gets shown to users explaining why the attempt was cancelled.
/// </summary> /// </summary>
public string? Popup { get; set; } public string? Popup;
} }
/// <summary> /// <summary>
@@ -93,8 +98,18 @@ public record struct ItemToggleActivateAttemptEvent(EntityUid? User)
[ByRefEvent] [ByRefEvent]
public record struct ItemToggleDeactivateAttemptEvent(EntityUid? User) public record struct ItemToggleDeactivateAttemptEvent(EntityUid? User)
{ {
/// <summary>
/// Should we silently fail.
/// </summary>
public bool Silent = false;
public bool Cancelled = false; public bool Cancelled = false;
public readonly EntityUid? User = User; public readonly EntityUid? User = User;
/// <summary>
/// Pop-up that gets shown to users explaining why the attempt was cancelled.
/// </summary>
public string? Popup;
} }
/// <summary> /// <summary>

View File

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

View File

@@ -76,6 +76,23 @@ public sealed class ItemToggleSystem : EntitySystem
var user = args.User; 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() args.Verbs.Add(new ActivationVerb()
{ {
Text = !ent.Comp.Activated ? Loc.GetString(ent.Comp.VerbToggleOn) : Loc.GetString(ent.Comp.VerbToggleOff), 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) if (comp.Activated)
return true; return true;
if (!comp.Predictable && _netManager.IsClient)
return true;
var attempt = new ItemToggleActivateAttemptEvent(user); var attempt = new ItemToggleActivateAttemptEvent(user);
RaiseLocalEvent(uid, ref attempt); 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.Cancelled)
{ {
if (attempt.Silent)
return false;
if (predicted) if (predicted)
_audio.PlayPredicted(comp.SoundFailToActivate, uid, user); _audio.PlayPredicted(comp.SoundFailToActivate, uid, user);
else else
@@ -159,7 +181,6 @@ public sealed class ItemToggleSystem : EntitySystem
} }
Activate((uid, comp), predicted, user); Activate((uid, comp), predicted, user);
return true; return true;
} }
@@ -176,16 +197,31 @@ public sealed class ItemToggleSystem : EntitySystem
if (!comp.Activated) if (!comp.Activated)
return true; return true;
if (!comp.Predictable && _netManager.IsClient) if (!comp.Predictable)
return true; predicted = false;
var attempt = new ItemToggleDeactivateAttemptEvent(user); var attempt = new ItemToggleDeactivateAttemptEvent(user);
RaiseLocalEvent(uid, ref attempt); RaiseLocalEvent(uid, ref attempt);
if (attempt.Cancelled) if (!predicted && _netManager.IsClient)
return false; 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); Deactivate((uid, comp), predicted, user);
return true; return true;
} }

View File

@@ -39,20 +39,24 @@ public abstract class SharedItemSystem : EntitySystem
public void SetSize(EntityUid uid, ProtoId<ItemSizePrototype> size, ItemComponent? component = null) 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; return;
component.Size = size; component.Size = size;
Dirty(uid, component); 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) 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; return;
component.Shape = shape; component.Shape = shape;
Dirty(uid, component); Dirty(uid, component);
var ev = new ItemSizeChangedEvent(uid);
RaiseLocalEvent(uid, ref ev, broadcast: true);
} }
/// <summary> /// <summary>
@@ -236,6 +240,7 @@ public abstract class SharedItemSystem : EntitySystem
{ {
// Set the deactivated shape to the default item's shape before it gets changed. // Set the deactivated shape to the default item's shape before it gets changed.
itemToggleSize.DeactivatedShape ??= new List<Box2i>(GetItemShape(item)); itemToggleSize.DeactivatedShape ??= new List<Box2i>(GetItemShape(item));
Dirty(uid, itemToggleSize);
SetShape(uid, itemToggleSize.ActivatedShape, item); 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. // Set the deactivated size to the default item's size before it gets changed.
itemToggleSize.DeactivatedSize ??= item.Size; itemToggleSize.DeactivatedSize ??= item.Size;
Dirty(uid, itemToggleSize);
SetSize(uid, (ProtoId<ItemSizePrototype>) itemToggleSize.ActivatedSize, item); SetSize(uid, (ProtoId<ItemSizePrototype>) itemToggleSize.ActivatedSize, item);
} }
} }
@@ -258,7 +264,5 @@ public abstract class SharedItemSystem : EntitySystem
SetSize(uid, (ProtoId<ItemSizePrototype>) itemToggleSize.DeactivatedSize, item); 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.Interaction.Components;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Item; using Content.Shared.Item;
using Content.Shared.Item.ItemToggle.Components;
using Content.Shared.Lock; using Content.Shared.Lock;
using Content.Shared.Materials; using Content.Shared.Materials;
using Content.Shared.Placeable; using Content.Shared.Placeable;
@@ -146,13 +147,13 @@ public abstract class SharedStorageSystem : EntitySystem
SubscribeLocalEvent<StorageComponent, BoundUserInterfaceMessageAttempt>(OnBoundUIAttempt); SubscribeLocalEvent<StorageComponent, BoundUserInterfaceMessageAttempt>(OnBoundUIAttempt);
SubscribeLocalEvent<StorageComponent, BoundUIOpenedEvent>(OnBoundUIOpen); SubscribeLocalEvent<StorageComponent, BoundUIOpenedEvent>(OnBoundUIOpen);
SubscribeLocalEvent<StorageComponent, LockToggledEvent>(OnLockToggled); SubscribeLocalEvent<StorageComponent, LockToggledEvent>(OnLockToggled);
SubscribeLocalEvent<MetaDataComponent, StackCountChangedEvent>(OnStackCountChanged);
SubscribeLocalEvent<StorageComponent, EntInsertedIntoContainerMessage>(OnEntInserted); SubscribeLocalEvent<StorageComponent, EntInsertedIntoContainerMessage>(OnEntInserted);
SubscribeLocalEvent<StorageComponent, EntRemovedFromContainerMessage>(OnEntRemoved); SubscribeLocalEvent<StorageComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
SubscribeLocalEvent<StorageComponent, ContainerIsInsertingAttemptEvent>(OnInsertAttempt); SubscribeLocalEvent<StorageComponent, ContainerIsInsertingAttemptEvent>(OnInsertAttempt);
SubscribeLocalEvent<StorageComponent, AreaPickupDoAfterEvent>(OnDoAfter); SubscribeLocalEvent<StorageComponent, AreaPickupDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<StorageComponent, GotReclaimedEvent>(OnReclaimed);
SubscribeLocalEvent<MetaDataComponent, StackCountChangedEvent>(OnStackCountChanged);
SubscribeAllEvent<OpenNestedStorageEvent>(OnStorageNested); SubscribeAllEvent<OpenNestedStorageEvent>(OnStorageNested);
SubscribeAllEvent<StorageTransferItemEvent>(OnStorageTransfer); SubscribeAllEvent<StorageTransferItemEvent>(OnStorageTransfer);
@@ -161,7 +162,7 @@ public abstract class SharedStorageSystem : EntitySystem
SubscribeAllEvent<StorageInsertItemIntoLocationEvent>(OnInsertItemIntoLocation); SubscribeAllEvent<StorageInsertItemIntoLocationEvent>(OnInsertItemIntoLocation);
SubscribeAllEvent<StorageSaveItemLocationEvent>(OnSaveItemLocation); SubscribeAllEvent<StorageSaveItemLocationEvent>(OnSaveItemLocation);
SubscribeLocalEvent<StorageComponent, GotReclaimedEvent>(OnReclaimed); SubscribeLocalEvent<ItemSizeChangedEvent>(OnItemSizeChanged);
CommandBinds.Builder CommandBinds.Builder
.Bind(ContentKeyFunctions.OpenBackpack, InputCmdHandler.FromDelegate(HandleOpenBackpack, handle: false)) .Bind(ContentKeyFunctions.OpenBackpack, InputCmdHandler.FromDelegate(HandleOpenBackpack, handle: false))
@@ -173,6 +174,21 @@ public abstract class SharedStorageSystem : EntitySystem
UpdatePrototypeCache(); 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) private void OnNestedStorageCvar(bool obj)
{ {
NestedStorage = obj; NestedStorage = obj;
@@ -322,6 +338,25 @@ public abstract class SharedStorageSystem : EntitySystem
args.Verbs.Add(verb); 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) public void OpenStorageUI(EntityUid uid, EntityUid actor, StorageComponent? storageComp = null, bool silent = true)
{ {
// Handle recursively opening nested storages. // Handle recursively opening nested storages.
@@ -719,14 +754,23 @@ public abstract class SharedStorageSystem : EntitySystem
private void OnStorageTransfer(StorageTransferItemEvent msg, EntitySessionEventArgs args) 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; return;
var localPlayer = args.SenderSession.AttachedEntity; 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; return;
// Validate the target storage
if (!ValidateInput(args, msg.StorageEnt, msg.ItemEnt, out var player, out var storage, out var item, held: true)) if (!ValidateInput(args, msg.StorageEnt, msg.ItemEnt, out var player, out var storage, out var item, held: true))
return; return;
@@ -1525,6 +1569,8 @@ public abstract class SharedStorageSystem : EntitySystem
} }
} }
private void HandleOpenBackpack(ICommonSession? session) private void HandleOpenBackpack(ICommonSession? session)
{ {
HandleToggleSlotUI(session, "back"); HandleToggleSlotUI(session, "back");

View File

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