Skip to content

Item Container Inventory Scripting Help

DrekiSlayerDrekiSlayer Member Posts: 5
edited April 2021 in Builders - Scripting
I am looking for a script function like getitemparent() or isitemincontainer(). Specifically with some of my scripts when I am doing inventory loops I want to exclude all items that are contained within container objects.

Here is a code example of where I would like to know if an individual item is in a container or not.


void TakeAllItemsLike(object oCreature, string sMatch)
{

object oItem = GetFirstItemInInventory(oCreature);
string sTAG = GetTag(oItem);

while (GetIsObjectValid(oItem))
{
sTAG = GetTag(oItem);
if (TestStringAgainstPattern("**"+sMatch+"**", GetStringUpperCase(sTAG)))
{
//Would like to check and only do this if the item is not in a specific container
DestroyObject(oItem);
}

oItem = GetNextItemInInventory(oCreature);
}

}


Thanks for any assistance.

Comments

  • KamirynKamiryn Member Posts: 74
    As far as I know there's no isitemincontainer() function. Howewer it's not difficult to write one:
    int GetIsItemInInventory(object oItem, object oInventory)
    {
    	object oItemQ = GetFirstItemInInventory(oInventory);
    	while (GetIsObjectValid(oItemQ))
    	{
    		if (oItemQ==oItem)	return TRUE;
    		oItemQ = GetNextItemInInventory(oInventory);
    	}
    	return FALSE;
    }
    

    Then GetIsItemInInventory(oItem, iContainer) would return TRUE if oItem is in oContainer.

    The problem however is that this function would have to use GetFirst/NextItemInInventory() and I think you can't nest GetFirst/NextItemInInventory() calls. So you can't use that function unfortunately in a GetFirst/NextItemInInventoty() loop.

    Instead you would have to cycle through the items in your container first and write them down somewhere. Maybe in an array. Unfortunately while there are SetLocalArrayInt() / GetLocalArrayInt() and SetLocalArrayString() / GetLocalArrayString() functions already defined (in "nw_o0_itemmaker") there are no SetLocalArrayObject() / GetLocalArrayObject() functions. You would have to do that yourself (it's easy however, look at "nw_o0_itemmaker" to see how Bioware did it for the Int/String versions):
    void SetLocalArrayObj(object oidObject, string sVarName, int nVarNum, object oValue)
    {
        string sFullVarName = sVarName + IntToString(nVarNum) ;
        SetLocalObject(oidObject, sFullVarName, oValue);
    }
    
    object GetLocalArrayObj(object oidObject, string sVarName, int nVarNum)
    {
        string sFullVarName = sVarName + IntToString(nVarNum) ;
        return GetLocalObject(oidObject, sFullVarName);
    }
    

    Then write a function SearchLocalArrayObject() that performs a search for an object in your object array:
    int SearchLocalArrayObj(object oidObject, string sVarName, int nSize, object oValue)
    {
        int nPos;
        for (nPos=0; nPos<nSize; nPos++)
        {
            if (GetLocalArrayObj(oidObject, sVarName, nPos)==oValue)
            {
                return nPos;
            }
        }
        return -1;
    }
    

    This could be a little bit improved by automatically storing the array's size somewhere so you don't have to keep track of it yourself. I leave that up to you ;).

    Store the inventory of a container with
    int StoreContainerInventory(object oidObject, string sVarName, object oContainer)
    {
            object oItem = GetFirstItemInInventory(oContainer);
            int nCount=0;
            while (GetIsObjectValid(oItem))
            {
                SetLocalArrayObj(oContainer, sVarName, nCount++, oItem);
                oItem = GetNextItemInInventory(oContainer);
            }
    	return nCount; // returns no of items in the container = size of the array
    }
    

    and to check whether an item oItem is in a container oContainer you could use:
    int GetIsItemInContainer(object oidObject, string sVarName, object oItem, object oContainer, int nSize)
    {
    	int nPos = SearchLocalArrayObj(oidObject, sVarName, nSize, oItem);
    	return (nPos!=-1);
    }
    

    Hope it helps.



  • DrekiSlayerDrekiSlayer Member Posts: 5
    Very creative idea Kamiryn I have used arrays before even in NWN however this is still to much overhead for what would be a nice to have feature vs a need to have. I don't want to muck with storing sperate inventory's in arrays, my module already has enough stuff that can break. I will just change up what I want to do and come at it from a different angle.
  • ForSeriousForSerious Member Posts: 466
    I think the solution is much easier than expected. By default, looping through an inventory will not look in the inventories of containers. You will need to specifically check in all containers, but while you're doing that, just ignore the safe container:
    //Note this will not destroy any containers.
    void TakeAllItemsLike(object oCreature, string sMatch)
    {
        object oItem = GetFirstItemInInventory(oCreature);
        string sTAG;
        while(GetIsObjectValid(oItem))
        {
            sTAG = GetTag(oItem);
            if(GetHasInventory(oItem) && sTAG != "<TheTagOfTheSafeContainer>")
            {
                object oDeepItem = GetFirstItemInInventory(oItem);
                while(GetIsObjectValid(oDeepItem))
                {
                    sTAG = GetTag(oDeepItem);
                    if(TestStringAgainstPattern("**"+sMatch+"**", GetStringUpperCase(sTAG)))
                    {
                        DestroyObject(oDeepItem);
                    }
                    oDeepItem = GetNextItemInInventory(oItem);
                }
            }
            else
            {
                if(TestStringAgainstPattern("**"+sMatch+"**", GetStringUpperCase(sTAG)))
                {
                    //Would like to check and only do this if the item is not in a specific container
                    DestroyObject(oItem);
                }
            }
            oItem = GetNextItemInInventory(oCreature);
        }
    }
    

    I haven't tried to compile this, but it should be a good start.
  • KamirynKamiryn Member Posts: 74
    I'm pretty sure that GetFirst/NextItemInInventory() does not exclude items in containers.
  • ForSeriousForSerious Member Posts: 466
    Guess I made some redundant code by assuming that. I mean, how else are you going to know if the item is in a container?

    Alrighty: The most helpful thing I can find is from here in the lexicon: "When using ActionTakeItem on a container within a container the game will crash. Suggested workaround is to place the container on the floor (preferably in the top right corner where it cannot be seen) and then perform ActionTakeItem on it."
    I don't see where it suggests how to go about doing that, but if you can take the safe container out of the inventory before deleting matched items, moving it back in after should be just as easy.
  • KamirynKamiryn Member Posts: 74
    edited September 2021
    I am looking for a script function like getitemparent() or isitemincontainer().

    With build 8193.31 and the new json datatype it's now possible to write something like getitemparent() or isitemincontainer():

    https://neverwintervault.org/project/nwnee/script/scripting-functions-using-json-datatype

    If you're still interested you can find among other functions

    // returns TRUE if oItem in the specified container
    // or in any container if no container is specified
    int MK_GetIsItemInContainer(object oItem, object oContainer=OBJECT_INVALID)

    and

    // returns the container that holds oItem or
    // OBJECT_INVALID if oItem is not in a container
    object MK_GetContainer(object oItem)

    in the provided include file that should do what you're looking for. Both functions do not use GetFirst/NextItemInInventory() so you can use them in GetFirst/NextItemInInventory() loops.

    Post edited by Kamiryn on
Sign In or Register to comment.