Script for placeable inventory

Alright! So I have this idea for a quest where the player will need to get like 4 fragment pieces from rubble around an area. I am nto a very good scripter so if anyone could help me out with this that would be appreciated. But basically I want it to be randomized so that the player will have to search to till they find the one with the item in it.

So basically like this:

Player needs 4 fragments

there are 10 chests and they will need to check every chest in order to find them all but the fragments are never in the same container.

If anyone can help em out with this I would greatly appreciate it :slight_smile:

I tried with a script, but my brain isn’t always that logical, so this won’t quite work (plus I have no idea if it compiles in NWN1 as I did it in NWN2). In my script I tried with a random element that a chest might or might not have an item at all. The way it is now is that you might not find a single fragment if you look in all the chests. Urrgh, I should probably go to sleep now as it’s past midnight here where I live. I post my embryo of a script here. Someone with a more logical brain will probably figure this out quite easily. Anyway, maybe it can help you somewhat. It’s a start perhaps:

void main()
{

object oPC = GetLastOpenedBy();

if (!GetIsPC(oPC)) return;

int random = Random(2);

	if(!GetLocalInt(OBJECT_SELF,"Opened"))
	{

		SetLocalInt(OBJECT_SELF,"Opened",1);
					
		if (!GetGlobalInt("fragment1found") && random == 2)
		{
		SetGlobalInt("fragment1found",1);
		CreateItemOnObject("fragment1", OBJECT_SELF, 1, "fragment1", 1);
		}

		else if (!GetGlobalInt("fragment2found") && random == 2)
		{
		SetGlobalInt("fragment2found",1);
		CreateItemOnObject("fragment1", OBJECT_SELF, 1, "fragment2", 1);
		}

		else if (!GetGlobalInt("fragment3found") && random == 2)
		{
		SetGlobalInt("fragment3found",1);
		CreateItemOnObject("fragment1", OBJECT_SELF, 1, "fragment3", 1);
		}

		else if (!GetGlobalInt("fragment4found") && random == 2)
		{
		SetGlobalInt("fragment4found",1);
		CreateItemOnObject("fragment1", OBJECT_SELF, 1, "fragment4", 1);
		}

		else if (random == 1)
		{
		FloatingTextStringOnCreature("You don't find anything.", oPC, FALSE);
		}

	}	
		
} 

Sorry for not being able to solve this for you right away. Maybe if I gave it a few hours more I could figure something out…but now I need sleep.

@Dragonqueeny,

This is a little more complicated than you may first realize. I have done something similar to this in my NWN1 Soul Shaker module, but am not at my main computer to copy the script. However, I have also done other stuff in NWN2 similar.

I cannot recall if I did this easier than I am about to explain for my NWN1 module, but basically, you need to determine four random chests (from the ten) in which to place the fragments, eliminating each chest as you use it before determining the next random chest from those that remain.

I prefer to use a letter string to help determine the chests that remain, made from the first ten letters of the alphabet and then remove a letter representing a given chest. Then get a random letter from the string that remains, which determines the next chest and so on.

When I get back to my main PC, I will check my code to see if I had something more simple than this in my Soul Shaker module. If not, I will explain in more detail later.

Too late to say more just now. But, hopefully, that gives you an idea of how to approach it, even if the scripting is not yet clear to you.
Thanks, Lance.

@Lance_Botelle

Yeah, I thought about this as I went to bed last night, and realized too that this would be pretty complicated, since there’s a lot of randomness going on, plus that you would have to make sure that there are 4 fragments found in total when you’ve opened every chest.

Actually it sounds quite trivial. I guess, you’ve somewhere a trigger which initializes all containers and should distribute the items among them?

1 Like

@andgalf,

The biggest issue is to ensure none of the ten chests are duplicated within the randomness. I hope my script will help illustrate how I do it … when I get it posted. :slight_smile:

@Mmat,

As I say above, “This is a little more complicated than you may first realize.” The firing of the script is, as you say, “quite trivial”, but it is the contents of the script itself where it gets a little more complicated, as you need to ensure a chest from the ten is not randomly chosen again, unless that is not an issue.

However, I have the impression that the OP wanted the four fragment parts randomly distributed between the ten chests, without any repetition within each chest. As I say, I have done this type of thing already and was looking for a script to post to demonstrate what I have done. However, my script is full of other stuff and it may take a while to slimline it. (I checked my NWN1 module and discovered I did not do this type of script there, so I am editing one of my NWN2 scripts that I hope will work.)

That said, you may have a quick method I am missing, in which case, please share your code for all. :slight_smile:

EDIT: You could make it easier by having a single check on each chest opening, but would likely bias the weight of where the fragments were found. i.e. Do a 25% chance of find (for instance), with a certainty of finding if the number of fragments found is not reached by the time we get to the last few chests, and no chance if we have enough, but that is not the “correct” way of doing it.

Another method (having just found the code I was looking for in my NWN1 module) is to divide the ten chests into roughly four lots and randomise between each four sets, but again that is still biased, but does give a fairly reasonable result.

Or … Roll a single random number and simply add/subtract a set number from that to give us four “random” chests", but again that is an artificial way of doing it too. However, artificial or not, it is still a valid method and it all depends upon how much you would like any potential randomness to vary … and how “real” or “biased” you mind the randomness to be.

@Dragonqueeny

I was going to assume your fragment pieces were the “same”, but as that is not clear, I will also take into account that you may be needing four different fragment pieces to be randomised across the ten chests. Having them all the same would probably made the scripting a little easier, but allowing different fragments - or even different items to the “set” may make the script more flexible for any future usage.

Many thanks, Lance.

Hi Lance,
I will share my code this afternoon, after work

No the pieces are the same. They will need 4 of the same piece but the containers where they are in are randomized. Or that is what I had in mind. :slight_smile:

@Dragonqueeny,

OK. As it happens, I will continue to write the script with a potential multiple item resref, but it will still do what you need it to do. You would set less variables, that is all.

here’s my take :

// assumptions :
//  - the tags of the 10 chests are "CHEST0" - "CHEST9"
//  - the 4 fragments have templates whose resrefs are 'fragment0' to 'fragment3'

void main()
{
    string  chests  = "0123456789";     // these are the chests we'll use
    int     nchests = 4;                // how many chests we're filling
    int     cnum;                       // which of the nchests chests we're working on
    int     id;                         // identify the specific chest we've chosen
    object  chest;                      // the actual chest object chosen
    string  tag;                        // the tag of the chest chosen

    for (cnum = 0; cnum < nchests; cnum++) {

        id = Random(10 - cnum);         // pick a chest, any chest
        tag = "CHEST" + GetSubString(chests, id, 1);
        if (GetIsObjectValid(chest = GetObjectByTag(tag))) {

            // put the fragment in the selected chest
            object frag = CreateItemOnObject("fragment" + IntToString(cnum), chest);
            if (!GetIsObjectValid(frag))
                SendMessageToPC(GetFirstPC(), "Oh Noooz! no fragment with resref 'fragment" + IntToString(cnum) + "'!");

            // update our truth set
            if (id) {
                chests = GetStringLeft(chests, id) + GetStringRight(chests, GetStringLength(chests) - id - 1);
            } else {
                chests = GetStringRight(chests, GetStringLength(chests) - 1);
            }

        } else {
            SendMessageToPC(GetFirstPC(), "Oh Noooz! no chest with tag '" + tag + "'!");
        }
    }
}
1 Like

Hi All,

Here is my script, which I have taken liberty to add some flexibility to allow my own use moving forward …

To use, you must place this script of a trigger on enter, and add three variables to it:-

  1. The tag of the placeable being used for items found goes in “PLACETAG”.
  2. The number of items to be found goes in “QUANTITY”
  3. The ResRef of the item to be created goes in “RR1”.

NB: You can stipulate other items in RR2, RR3, etc, up to a maximum of ten items, as long as you have placed ten placeables. (You can leave these higher RRx references off in your example.)

Then ensure you have the ten chests with the SAME tag, and is placed in the “PLACETAG” on trigger.
Ensure your QUANTITY is set to 4 on the trigger for your example. (Vary figure if required.)
Ensure that “RR1” on trigger has the resref of the item to be created in chests.

There are benefits to this script, as you can place the same tagged placeable (up to ten) and vary the item being randomly dropped. Either all the same or different resrefs.

I have to go offline now, but will check the other posts later.

Thanks, Lance.

////////////////////////////////////////////////////////////////////////////////////
// CREATE X OBJECTS ON Y PLACEABLES AT RANDOM (Y CALCULATED FROM ENVIRONMENT COUNT - MAX TEN)
// I.E. JUST PLACE THE PLACEABLES WITH THE SAME TAG TO RANDOMIZE BETWEEN. (MAXIMUM TEN SETUP)
// SETUP: USE THIS SCRIPT ON A TRIGGER WITH VARIABLE STRUCTURE AS FOLLOWS:-
// 1) A STRING VARIABLE CALLED "PLACETAG" WITH THE TAG OF THE PLACEABLE BEING USED FOR RANDOM FIND.
// 2) AN INTEGER VARIABLE CALLED "QUANTITY" WITH THE TOTAL NUMBER OF ITEMS TO FIND IN THE SET = X.
// 3) STRING VARIABLES IN FORMAT "RR1" "RR2" "RR3", ETC CONTAINING RESREF OF OBJECT(S) TO CREATE.
// NB: IF RR2 and above are not stipulated, then RR1 will be used for all object creations
// up to the number of objects listed in "QUANTITY". ITEM TO CREATE IS A SINGLE ITEM OF ITS TYPE
// ANY CREATURE PASSING THROUGH THIS TRIGGER WILL SETUP THE RANDOM PLACED OBJECTS
////////////////////////////////////////////////////////////////////////////////////

// GET A NUMBER FROM A LETTER
int GetAProximityInteger(string sLetter);
int GetAProximityInteger(string sLetter)
{
	sLetter = GetStringUpperCase(sLetter);
	
	int iNumber = 0;
	
	if(sLetter == "A"){iNumber = 1;} 
	else if(sLetter == "B"){iNumber = 2;} 
	else if(sLetter == "C"){iNumber = 3;} 
	else if(sLetter == "D"){iNumber = 4;} 
	else if(sLetter == "E"){iNumber = 5;} 
	else if(sLetter == "F"){iNumber = 6;} 
	else if(sLetter == "G"){iNumber = 7;} 
	else if(sLetter == "H"){iNumber = 8;} 
	else if(sLetter == "I"){iNumber = 9;} 
	else if(sLetter == "J"){iNumber = 10;}

	return iNumber;
}

// RETURN A RANDOM LETTER FOR THOSE THAT REMAIN IN DESIGNATED SERIES
string GetNewRandomLetter(object oTrigger);
string GetNewRandomLetter(object oTrigger)
{
	string sRETURN;
	
	string sPickFrom = GetLocalString(oTrigger, "LettersRemain");		// E.g. Could start with "abcd"
	int iLettersRemain = GetStringLength(sPickFrom);					// E.g. 4 in this case
	int ThisLetter = Random(iLettersRemain);							// E.g. 0-3 possible here.
	sRETURN = GetSubString(sPickFrom, ThisLetter, 1);					// E.g. 0 would return "a"
	
	// CREATE & STORE REMAINING ALPHABET STRING
	int nRightCount = iLettersRemain - ThisLetter - 1;					// E.g. 4 - 0 - 1 = 3 (-1 is GetSubString 0 correction)
	int nLeftCount = iLettersRemain - nRightCount - 1;					// E.g. 4 - 3 - 1 = 0 (-1 is for this allocation)
	
	string sRHRemain = GetStringRight(sPickFrom, nRightCount);			// E.g. "bcd" in this case
	string sLHRemain = GetStringLeft(sPickFrom, nLeftCount);			// E.g. Nothing to return in this case.	
	string sRemainingLetters = sLHRemain + sRHRemain;	
	
	SetLocalString(oTrigger, "LettersRemain", sRemainingLetters);  		// Now stores "bcd" as possible remianing letters to choose from.
	
	return sRETURN;														// E.g. Returns "a"
}

////////////////////////////////////////////////////////////////////////////////////
// CREATE ITEMS REQUIRED ON RANDOM PLACEABLES
////////////////////////////////////////////////////////////////////////////////////
void CreateItemsOnPlaceables(object oTrigger, int iQTY, int iY, string sPlaceTag);
void CreateItemsOnPlaceables(object oTrigger, int iQTY, int iY, string sPlaceTag)
{
	string sRR1 = GetLocalString(oTrigger, "RR1"); string sRR2 = GetLocalString(oTrigger, "RR2");
	string sRR3 = GetLocalString(oTrigger, "RR3"); string sRR4 = GetLocalString(oTrigger, "RR4");
	string sRR5 = GetLocalString(oTrigger, "RR5"); string sRR6 = GetLocalString(oTrigger, "RR6");
	string sRR7 = GetLocalString(oTrigger, "RR7"); string sRR8 = GetLocalString(oTrigger, "RR8");
	string sRR9 = GetLocalString(oTrigger, "RR9"); string sRR10 = GetLocalString(oTrigger, "RR10");
	
	int iCount = 1;
	
	while(iCount <= iQTY)
	{
		// GET THE ITEM RESREF FOR THIS PLACEABLE
		string sRR = "RR" + IntToString(iCount); 
		sRR = GetLocalString(oTrigger, sRR);
		if(sRR == ""){sRR = GetLocalString(oTrigger, "RR1");}
		
		// NOW GET A RANDOM PLACEABLE FROM THOSE AVAILABLE (BASE ON RANDOM LETTER) TO CREATE ITEM			
		string sLetter = GetNewRandomLetter(oTrigger);
		int iProximity = GetAProximityInteger(sLetter);		
		object oPlaceableToUse = GetNearestObjectByTag(sPlaceTag, oTrigger, iProximity);
		
		// DEBUG FEEDBACK
		// SendMessageToPC(GetFirstPC(), ">>> CREATING: " + sRR + " ON " + sPlaceTag + " AT PROXIMITY " + IntToString(iProximity) + "");
		
		// CREATE APPROPRIATE RANDOM ITEM ON PLACEABLE
		CreateItemOnObject(sRR, oPlaceableToUse, 1, "", 0);
		
		iCount = iCount + 1;
	}
	
	DelayCommand(2.0, DestroyObject(oTrigger, 0.0, 0));
}


void main()
{		
	// WAIT UNTIL WE HAVE A VALID PC IN THE GAME FOR FEEDBACK (IN CASE CREATURE TRIGGERS)
	object oPC = GetFirstPC(); if(oPC == OBJECT_INVALID){return;}	
	
	// THE TRIGGER OBJECT ITSELF
	object oTrigger = OBJECT_SELF;
	
	// INITIALIZE (GET NUMBER PLACEABLES INVOLVED)
	if(GetLocalInt(oTrigger, "LBDONE")){return;}
	
	// GATHER INFO FROM THE TRIGGER VARS
	string sPlaceTag = GetLocalString(oTrigger, "PLACETAG");
	int iQTY = GetLocalInt(oTrigger, "QUANTITY");
	string sRR1 = GetLocalString(oTrigger, "RR1");
	
	// CALCULATE PLACEABLE OBJECTS BEING USED (Y)
	int iY = 0; int iNth = 0;
	
	object oPlaceable = GetObjectByTag(sPlaceTag, iNth);
	
	while(oPlaceable != OBJECT_INVALID)
	{
		iY = iY + 1;
		iNth = iNth + 1;	
		oPlaceable = GetObjectByTag(sPlaceTag, iNth);
	}
	
	// DEBUG FEEDBACK
	int iSTOP = 0;
	
	if(sPlaceTag == ""){SendMessageToPC(oPC, "<<< PLACEABLE TAG NOT SETUP ON TRIGGER >>>"); iSTOP = 1;}
	if(iQTY == 0){SendMessageToPC(oPC, "<<< ITEM QUANTITY NOT SETUP ON TRIGGER >>>");  iSTOP = 1;}
	if(sRR1 == ""){SendMessageToPC(oPC, "<<< AT LEAST ONE RESREF REQUIRED FOR TRIGGER >>>");  iSTOP = 1;}
	if(iY == 0){SendMessageToPC(oPC, "<<< NO PLACEABLE OBJECTS WITH TAG " + sPlaceTag + " SET FOR TRIGGER >>>");  iSTOP = 1;}	
	else if(iY < 2){SendMessageToPC(oPC, "<<< A MINIMUM OF 2 OBJECTS MUST BE USED WITH RANDOM TRIGGER >>>");  iSTOP = 1;}	
	
	// INCORRECT LOGIC POSSIBILITY
	if(iQTY > iY){SendMessageToPC(oPC, "<<< CANNOT HAVE MORE ITEM OBJECTS THAN PLACEABLES >>>");  iSTOP = 1;}
	
	if(iSTOP == 1){return;}
	
	/////////////////////////////////////////////////////////////////////////////////////////////
	// NOW MARK AS DONE, INITIALIZE AND SETUP THE ITEMS ON THE PLACEABLE OBJECTS
	/////////////////////////////////////////////////////////////////////////////////////////////
	
	SetLocalInt(oTrigger, "LBDONE", 1);
	
	// SETUP A TEMP STRING VAR FOR CALCULATIONS BASED ON Y (MAX 10)
	string sLetterString = "AB";	
	if(iY == 3){sLetterString = "ABC";}
	else if(iY == 4){sLetterString = "ABCD";}
	else if(iY == 5){sLetterString = "ABCDE";}
	else if(iY == 6){sLetterString = "ABCDEF";}
	else if(iY == 7){sLetterString = "ABCDEFG";}
	else if(iY == 8){sLetterString = "ABCDEFGH";}
	else if(iY == 9){sLetterString = "ABCDEFGHI";}
	else if(iY == 10){sLetterString= "ABCDEFGHIJ";}
	
	SetLocalString(oTrigger, "LettersRemain", sLetterString);
	
	DelayCommand(0.1, CreateItemsOnPlaceables(oTrigger, iQTY, iY, sPlaceTag));	
}
1 Like

Hello,

I see, there are already complete solutions there, but I promised …

I assume, that there are 10 containers (Label “Barney1” through “Barney10”). There are 4 equal items (here a diamond) to distribute ramdomly into the containers.

Put this in an event (on_use, on_open, on_enter, on_whatever) to initialize the containers:

void PlaceItem (int i, int n)
    {
      object r = GetObjectByTag ("Barney"+IntToString(i));
      CreateItemOnObject ("nw_it_gem005", r);
      location l = GetLocation (r);
      CreateObject (OBJECT_TYPE_PLACEABLE, "plc_solwhite", l, FALSE, "Chrismasbeam" + IntToString(n));
    }

    void main()
    {
      object oPC = GetLastUsedBy();
      // object oPC = GetEnteringObject();
      // object oPC = GetLastOpenedBy();
      if (!GetIsPC (oPC)) return;
      if (GetLocalInt (oPC, "InitIsDone")) return;
      SetLocalInt (oPC, "InitIsDone", 1);

      int i1=d10(); PlaceItem (i1, 1);
      int i2=d10(); { while (i2==i1) i2=d10(); PlaceItem (i2, 2); }
      int i3=d10(); { while (i3==i1 || i3==i2) i3=d10(); PlaceItem (i3, 3); }
      int i4=d10(); { while (i4==i1 || i4==i2 || i4==i3) i4=d10(); PlaceItem (i4, 4); }
    }

The Following will reset the thing (Needs some addition code to reset the variable on the PC):

void main()
    {
      object r, o;
      int n;
      for (n=1; n<11; n++)
        {
          r = GetObjectByTag ("Barney" + IntToString(n));
          o = GetFirstItemInInventory (r);
          if (GetIsObjectValid(o)) DestroyObject (o);
        }

      for (n=1; n<5; n++)
        {
          r = GetObjectByTag ("Chrismasbeam" + IntToString(n));
          if (GetIsObjectValid(r))  DestroyObject (r);
        }
    }

Of course, the code could be much shorter, but it’s christmas …

2 Likes

I see some nice solutions here :+1:.

But indeed this isn’t a trivial task. It reduces to obtaining a sample (non-repeating selection) from a population of objects.

My try is below. It started as a proof of concept but ended up as a universal function. It should also work with creatures.

Click to read algorithm description.

The basic approach one can think of is: 1) generate random object via GetObjectByTag; 2) see if it contains the item; 3) re-roll or spawn item. This is fine as long as number of items to spawn << number of containers. Otherwise you may end up with bad rolls and TMI the script (probability of rolling an empty container decreases as they are filled with items).

To avoid the issues mentioned above (i.e. allow easy spawn of 90 items in 100 chests if you wish), I went with random permutation method, which yields a linear time algorithm (no random-based loop exit condition):

  1. Count number of placeables with given tag
  2. Store local variable in each placeable, linking to itself
  3. Swap those links between placeables (“array shuffle”)
  4. Sample first n placeables (n = number of items to spawn) and use their links to get randomly-selected targets
  5. Spawn items, clear variables

Beware: It assumes all target containers have the same tag.

Click to see the code - paint some barrels then run it.
// By NWShacker, 2020-12-22 (updated 2020-12-23)

// Spawns iItemCount number of items with ResRef sItemResRef
// in randomly selected containers with tag sContainerTag
// (containers are selected without repetition); iStackSize
// and sNewTag are passed directly to CreateItemOnObject()
void NWSH_SpawnItemsInContainers(string sContainerTag, string sItemResRef, int iItemCount, int iStackSize=1, string sNewTag="")
{
    object oContainer1;
    object oContainer2;
    object oTemp;
    string sVariable;
    int iContainers;
    int iContainer1;
    int iContainer2;

    // local variable to create container "array"
    sVariable = "container_" + sItemResRef;

    // get number of containers (count objects with this tag)
    do
    {
        oContainer1 = GetObjectByTag(sContainerTag, iContainers++);
        SetLocalObject(oContainer1, sVariable, oContainer1);
    }
    while(GetIsObjectValid(oContainer1));
    iContainers -= 1;

    // trivial case: number of containers does not exceed number of items
    if(iContainers <= iItemCount)
    {
        for(iContainer1 = 0; iContainer1 < iContainers; iContainer1++)
        {
            oContainer1 = GetObjectByTag(sContainerTag, iContainer1);
            CreateItemOnObject(sItemResRef, oContainer1, iStackSize, sNewTag);
            DeleteLocalObject(oContainer1, sVariable);
        }
        return;
    }

    // shuffle container "array"
    for(iContainer1 = 0; iContainer1 < iContainers; iContainer1++)
    {
        iContainer2 = iContainer1 + Random(iContainers - iContainer1);
        oContainer1 = GetObjectByTag(sContainerTag, iContainer1);
        oContainer2 = GetObjectByTag(sContainerTag, iContainer2);
        oTemp = GetLocalObject(oContainer1, sVariable);
        SetLocalObject(oContainer1, sVariable, GetLocalObject(oContainer2, sVariable));
        SetLocalObject(oContainer2, sVariable, oTemp);
    }

    // sample container "array"
    for(iContainer1 = 0; iContainer1 < iItemCount; iContainer1++)
    {
        oContainer1 = GetObjectByTag(sContainerTag, iContainer1);
        oContainer2 = GetLocalObject(oContainer1, sVariable);
        CreateItemOnObject(sItemResRef, oContainer2, iStackSize, sNewTag);
        // ** DEBUG DELETE THIS **
        AssignCommand(oContainer2, SpeakString("SPAWNING ITEM!"));
        // ** DEBUG DELETE THIS **
    }

    // local variable cleanup
    for(iContainer1 = 0; iContainer1 < iContainers; iContainer1++)
    {
        oContainer1 = GetObjectByTag(sContainerTag, iContainer1);
        DeleteLocalObject(oContainer1, sVariable);
    }
}

void main()
{
    NWSH_SpawnItemsInContainers(
        "Barrel",                    // container tag (barrel)
        "nw_it_mpotion021",          // item resref (ale)
        4);                          // 4 items to spawn
}
Click to see a visual proof (4 random barrels out of 9).

EDIT: small code fixes.

3 Likes

Okay this script I absolutely love!

so if I wanted to clear the inventory of the chests after an x amount of time?

Scan all chests with GetObjectByTag and call your inventory clear procedure on each. Call such script manually or with DelayCommand.

If you want however those chests that had items spawned in to auto-clear - click here.

This is an excerpt from that code. oContainer2 in this loop is the object in which item is spawned. You can attach any code to it. For example:

    // sample container "array"
    for(iContainer1 = 0; iContainer1 < iItemCount; iContainer1++)
    {
        oContainer1 = GetObjectByTag(sContainerTag, iContainer1);
        oContainer2 = GetLocalObject(oContainer1, sVariable);
        CreateItemOnObject(sItemResRef, oContainer2, iStackSize, sNewTag);
        // ** DEBUG DELETE THIS **
        AssignCommand(oContainer2, SpeakString("SPAWNING ITEM!"));
        // ** DEBUG DELETE THIS **

        AssignCommand(oContainer2, DelayCommand(60.0,
            YourInventoryClearFunctionHere()));
    }

I don’t recommend this approach, though.

First one gives you full control and won’t cause any race conditions (unless you don’t plan to call NWSH_SpawnItemsInContainers more than once).

Well from what I noticed the from the script when I was testing it (I loved it btw) The only downside would be that it would not be deleted after X amount of time and since I aimed it for a PW kinda want to have it reset after a while or there might be 99999 of these things in some containers

You didn’t mention item destruction before, so the original script doesn’t do that.

But try this:

Item delete function
// Destroys all items with tag sItemTag in all containers with
// tag sContainerTag; if sItemTag is "", all items are destroyed
void NWSH_DestroyItemsInContainers(string sContainerTag, string sItemTag="")
{
    object oContainer;
    object oItem;
    int iContainer;

    do
    {
        oContainer = GetObjectByTag(sContainerTag, iContainer++);
        oItem = GetFirstItemInInventory(oContainer);
        while(GetIsObjectValid(oItem))
        {
            if(sItemTag == "" || GetTag(oItem) == sItemTag)
            {
                DestroyObject(oItem);
                // ** DEBUG DELETE THIS **
                AssignCommand(oContainer, SpeakString("DESTROY ITEM!"));
                // ** DEBUG DELETE THIS **
            }
            oItem = GetNextItemInInventory(oContainer);
        }
    }
    while(GetIsObjectValid(oContainer));
}
New main function
void main()
{
    // life
    NWSH_SpawnItemsInContainers(
        "Barrel",                     // container tag (barrel)
        "nw_it_mpotion021",           // item resref (ale)
        4);                           // 4 items to spawn

    // death
    AssignCommand(GetModule(), DelayCommand(10.0,
    NWSH_DestroyItemsInContainers(
        "Barrel",                     // container tag (barrel)
        "NW_IT_MPOTION021")));        // item tag (ale)
}

Don’t call this script when an item deletion is currently scheduled. Although you can easily rewrite this to call clear & spawn in a loop to avoid this issue.

Yes I know I didn’t, I just didn’t thought the request through completely :slight_smile:

This gives me an error at line 73