Using GetBaseItemFitsInInventory() on placeable containers

I have an NWN:EE script that copies a variable number of items (but potentially many) into a placeable container (a chest) that I initially spawn using CreateObject(). So, the container starts out empty, with one blank page of inventory. The copied items will potentially exceed the 25 pages the container could have. I want to check the container so that, when it’s full, I spawn a new container and continue copying the items into the new one.

I thought this would be a good use for GetBaseItemFitsInInventory(). But, I am finding that it often returns FALSE (implying the container is full) when the base item type would not fit in the existing pages of the container, but the container could still have more pages.

Is there a good way to check whether an next item will fit in a container?

BTW, I have tried checking whether the object returned by CopyItem() was invalid. But, it seems it doesn’t always return an invalid object if the object fails to copy into the specified inventory. (I also checked and it’s not just plopping the copied items on the ground where the full container is). Additionally, I also tried using GetItemPossessor() to check if the newly created item is in the container it was supposed to be created in. But, it is often reported as there, even when it isn’t. I have a suspicion that both of these would work if called them after a delay, but that isn’t practical here.

Solved: I have gotten past this by making the chest-spawning function fill a fresh chest with dummy items to get it to 25 pages, then delete those items. I then make my item-copying function that calls the chest-maker quit after setting up to call itself after a short delay to give the dummy items time to be destroyed. It’s a bit more involved than I had wanted, but it works. Once the chest capacity has all 25 pages, GetBaseItemFitsInInventory() works to see if it will take the next item.

2 Likes

Interesting. I’ve added this to the Lexicon page for the command, didn’t realise it stopped if the pages were not populated!

That seems like a bug. It’s not solved so much as worked-around.

True. Very much a workaround and not a case where the function is working as robustly as it would reasonably be expected to. To be fair, it was probably intended for use with non-expandable inventories, like a PC’s. But given how common it is to put items in placeable containers, this is at least a significant shortcoming.

1 Like

Probably because the function takes into account only existing pages.
Also unfortunately it seems you can’t get the current number of pages of a placeable container. At least it’s not exported to json and most likely also not stored in a save game as the number of pages resets when you load a save.
Perhaps nwnx could do that.

As a work-around you could try this:

int CopyItemToPlaceable(object oItem, object oContainer)
{
   if (!GetHasInventory(oContainer)) return FALSE;   
   int bReturn = TRUE;
   int nBaseItemType = GetBaseItemType(oItem);
   int bFit1 = GetBaseItemFitsInInventory(nBaseItemType, oContainer);
   object oCopy = CopyItem(oItem, oContainer);
   if (!bFit1)
   {
      int bFit2 = GetBaseItemFitsInInventory(nBaseItemType, oContainer);
      // bFit1 = TRUE: item fits into existing pages
      // bFit1 = FALSE and bFit2 = TRUE: item does not fit into existing pages but a new page is created
      // bFit1 = FALSE and bFit2 = FALSE: item does not fit into existing pages and no new page is created 
      if (!bFit2)
      {
         DestroyObject(oCopy);
         bReturn = FALSE;
      }
   }
   return bReturn;
}

Made a short test and it seems to work fine (at least for items not stackable).

For stackable items the situation is slightly more complex because GetItemFitsInInventory() searches for free space in the inventory but stackable items to not necessarily require free space.

Here’s a version that should work with any item and with any target:

// copies oItem to oTarget and returns the number of items copied.
int CopyItemToTarget(object oItem, object oTarget, int bCopyVars=FALSE)
{
    int nItemsCopied = 0;
    if (GetIsObjectValid(oItem) && GetIsObjectValid(oTarget) && GetHasInventory(oTarget))
    {
        int nBaseItemType = GetBaseItemType(oItem);
        int bFit1 = GetBaseItemFitsInInventory(nBaseItemType, oTarget);
        int nStackItem = GetNumStackedItems(oItem);
        nItemsCopied = nStackItem;
        object oCopy = CopyItem(oItem, oTarget, bCopyVars);
        if (!bFit1)
        {
            // oItem does not fit, lets see if a new page is created
            int bFit2 = GetBaseItemFitsInInventory(nBaseItemType, oTarget);
            if (!bFit2)
            {
                // no new page created, lets see if oIten was partially added to an
                // existing stack
                int nStackCopy = GetNumStackedItems(oCopy);
                if (nStackCopy<=nStackItem)
                {
                    // if <: stack size of the copy is smaller than the stack size
                    //       of the original item -> part of the original stack is
                    //       merged with an existing stack but for the rest there
                    //       is no room in the inventory (because no new page is
                    //       created)
                    // if =: stack (or the item for not stackable items) do not fit
                    //       and no new page is created.
                    DestroyObject(oCopy);
                    nItemsCopied = (nStackItem-nStackCopy);
                }
//                else
//                {
                    // if >: oItem is copied on existing stack. Therefor no new
                    //       page is created.
//                }
            }
        }
    }
    return nItemsCopied;
}

1 Like

I’ll have a look at GetBaseItemFitsInInventory() adding a new page if there’s pages left, no promises tho.