Class ItemStackable
Represents the stackable component of an item, managing stack size, addition, removal, and unstacking logic.
public class ItemStackable : IStackable<Item>
- Inheritance
-
ItemStackable
- Implements
- Inherited Members
Remarks
STACK MATHS CONTRACT — read before editing.
Every quantity here is a uint. There is no such thing as a negative stack, so every subtraction must be provably non-negative before it runs; an underflow does not produce a small number, it produces ~4.29 billion items and mints currency out of nothing.
This class previously used UIntExtensions.AbsoluteSubtract to compute "how much is left over
after the merge". That helper returns |a - b|, NOT a saturating subtract, so it is only correct in
the overflow branch (incoming > capacity). In the ordinary fits-entirely branch it returned the
unused capacity instead of zero, which inflated the donor stack and underflowed the destination:
MaxStackSize 10, destination 1, source 2 produced a destination of 4,294,967,292. The call sites are
fixed here rather than changing AbsoluteSubtract — the helper lives in the shared utility
library, its name promises absolute difference, and other callers rely on that meaning.
Constructors
ItemStackable(Item, uint)
Constructs a stackable component for an item with the given amount.
public ItemStackable(Item item, uint amount)
Parameters
Fields
Amount
The current amount in the stack.
public uint Amount
Field Value
Properties
IsStackFull
Returns true if the stack is full (reached or exceeded max stack size).
public bool IsStackFull { get; }
Property Value
Remarks
Compares with >= rather than == on purpose. A stack that was corrupted by an
earlier overflow (or by a template whose MaxStackSize was lowered after the item was saved)
sits above the cap; with an equality test such a stack reported "not full" forever and kept
absorbing more items. The inequality lets an already-corrupted stack re-cap instead of growing.
RemainingCapacity
Returns the free room left in this stack, saturating at zero.
A stack sitting at or above MaxStackSize reports no remaining capacity rather than
underflowing into a ~4 billion result.
public uint RemainingCapacity { get; }
Property Value
Methods
AddToStack(Item)
Adds the other item to this stack and sets the other stack's size to the remainder, if any. Returns false on failure.
public bool AddToStack(Item other)
Parameters
otherItemThe item to add to the stack.
Returns
- bool
True if the item was added, false otherwise.
Remarks
Conserves quantity exactly: transferred is clamped to whichever of the incoming amount
and the remaining capacity is smaller, so this.Amount + other.Amount is identical before
and after the call. Neither side can underflow.
CanAddToStack(Item)
Returns true only if the entire other item can be added to this stack. Checks for matching template, seed, and stack capacity.
public bool CanAddToStack(Item other)
Parameters
otherItemThe item to add to the stack.
Returns
- bool
True if the item can be fully added, false otherwise.
Remove(uint)
Removes the specified amount from the stack. Destroys the item if the stack reaches zero.
public void Remove(uint amount)
Parameters
amountuintThe amount to remove.
Remarks
Saturating. Removing more than the stack holds empties the stack and logs, rather than
wrapping the uint around into a ~4 billion stack that also sails past the
Amount == 0 destroy check.
TryUnstack(uint, out Item)
Attempts to split amount off this stack into a new item instance.
public bool TryUnstack(uint amount, out Item instance)
Parameters
amountuintThe amount to unstack.
instanceItemThe new item instance, the original item, or null on failure.
Returns
- bool
True if unstacking was successful, false otherwise.
Remarks
Requesting the whole stack (or more) is not a split — the original instance is handed back untouched so the caller can move it wholesale, and this stack is left alone.
A genuine split allocates a real new Item carrying the same template and the
same generation seed, so the two halves remain mutually stackable. The new instance has
ID = 0 and Slot = -1: it is not yet a database row and not yet in a container.
The caller owns placing it and persisting it. The previous implementation decremented this
stack, set instance = null, and still returned true — silently destroying the
split-off quantity for any caller that trusted the result.