message: Fix and over-document spaceLeftForItem
diff: @@ -102,42 +102,33 @@ export function spaceLeftForItem(store: DimStore, item: DimItem, stores: DimStor
  throw new Error("item needs a 'type' field");
  }
  - // Account-wide buckets (mods, etc) are only on the first character
  - if (item.bucket.accountWide && !store.current) {
  - return 0;
  - }
  - if (!item.bucket) {
  - return 0;
  - }
  -
  - let openStacks = 0;
  + // Calculate how many full stacks (slots, where multiple items in a stack
  + // count as 1) are occupied in the bucket this item would go into.
  + let occupiedStacks = 0;
  if (store.isVault) {
  - if (store.isDestiny2()) {
  if (!item.bucket.vaultBucket) {
  return 0;
  }
  const vaultBucket = item.bucket.vaultBucket;
  - const usedSpace = item.bucket.vaultBucket
  + occupiedStacks = item.bucket.vaultBucket
  ? count(store.items, (i) => Boolean(i.bucket.vaultBucket?.hash === vaultBucket.hash))
  : 0;
  - openStacks = Math.max(0, capacityForItem(store, item) - usedSpace);
  } else {
  - const sort = item.bucket?.sort;
  - if (!sort) {
  - throw new Error("item needs a 'sort' field");
  + if (!item.bucket) {
  + return 0;
  }
  - openStacks = Math.max(
  - 0,
  - capacityForItem(store, item) - count(store.items, (i) => i.bucket.sort === sort)
  - );
  + // Account-wide buckets (mods, etc) are only on the first character
  + if (item.bucket.accountWide && !store.current) {
  + return 0;
  }
  - } else {
  - const occupiedStacks = store.buckets[item.bucket.hash]
  - ? store.buckets[item.bucket.hash].length
  - : 10;
  - openStacks = Math.max(0, capacityForItem(store, item) - occupiedStacks);
  + occupiedStacks = store.buckets[item.bucket.hash] ? store.buckets[item.bucket.hash].length : 10;
  + }
  +
  + // The open stacks are just however many you *could* fit, minus how many are occupied
  + const openStacks = Math.max(0, capacityForItem(store, item) - occupiedStacks);
  - // Some things can't have multiple stacks.
  + // Some things can't have multiple stacks (unique stacks) and must be handled
  + // specially. These won't ever be in the vault.
  if (item.uniqueStack) {
  // If the item lives in an account-wide bucket (like modulus reports)
  // we need to check out how much space is left in that bucket, which is
  @@ -163,12 +154,19 @@ export function spaceLeftForItem(store: DimStore, item: DimItem, stores: DimStor
  ? item.maxStackSize
  : 0;
  }
  - }
  +
  + // Convert back from stacks to individual items, keeping in mind that we may
  + // be able to move some amount into an existing stack.
  const maxStackSize = item.maxStackSize || 1;
  if (maxStackSize === 1) {
  + // Stacks and individual items are the same, no conversion required
  return openStacks;
  } else {
  + // Get the existing amount in individual pieces, not stacks
  let existingAmount = amountOfItem(store, item);
  + // This helps us figure out the remainder that gets added back in from a
  + // partial stack - it'll end up negative, and when subtracted from the open
  + // stacks' worth, it will *add* in the remainder.
  while (existingAmount > 0) {
  existingAmount -= maxStackSize;
  }
  