var position = targetStorage.GetMouseGridPieceLocation(dragEnt, dragLoc);
var newLocation = new ItemStorageLocation(DraggingRotation, position);
- EntityManager.RaisePredictiveEvent(new StorageSetItemLocationEvent(
- EntityManager.GetNetEntity(draggingGhost.Entity),
- EntityManager.GetNetEntity(sourceStorage),
- newLocation));
+ if (!_storage.ItemFitsInGridLocation(dragEnt, sourceStorage, newLocation))
+ {
+ window.Reclaim(control.Location, control);
+ }
+ else
+ {
+ EntityManager.RaisePredictiveEvent(new StorageSetItemLocationEvent(
+ EntityManager.GetNetEntity(draggingGhost.Entity),
+ EntityManager.GetNetEntity(sourceStorage),
+ newLocation));
- window.Reclaim(newLocation, control);
+ window.Reclaim(newLocation, control);
+ }
}
// Dragging to new storage
else if (targetStorage?.StorageEntity != null && targetStorage != window)
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;
}
--- /dev/null
+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;
+}
[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>
[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>
/// <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;
}
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),
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
}
Activate((uid, comp), predicted, user);
-
return true;
}
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 (!predicted && _netManager.IsClient)
+ return 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;
+ }
- if (!comp.Predictable) predicted = false;
Deactivate((uid, comp), predicted, user);
return true;
}
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>
{
// 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);
}
{
// 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);
}
}
SetSize(uid, (ProtoId<ItemSizePrototype>) itemToggleSize.DeactivatedSize, item);
}
}
-
- Dirty(uid, item);
}
}
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;
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);
SubscribeAllEvent<StorageInsertItemIntoLocationEvent>(OnInsertItemIntoLocation);
SubscribeAllEvent<StorageSaveItemLocationEvent>(OnSaveItemLocation);
- SubscribeLocalEvent<StorageComponent, GotReclaimedEvent>(OnReclaimed);
+ SubscribeLocalEvent<ItemSizeChangedEvent>(OnItemSizeChanged);
CommandBinds.Builder
.Bind(ContentKeyFunctions.OpenBackpack, InputCmdHandler.FromDelegate(HandleOpenBackpack, handle: false))
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;
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.
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);
+
+ // 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.Value, handsComp: handsComp, animate: false))
+ 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;
}
}
+
+
private void HandleOpenBackpack(ICommonSession? session)
{
HandleToggleSlotUI(session, "back");
item-toggle-activate = Activate
item-toggle-deactivate = Deactivate
+
+item-toggle-size-fail = Doesn't fit.