Help me figure out this script?

I was trying to write a script that runs from dialogue about if the PC and the party have at least 5 potions of invisibility. The script compiles but I don’t quite know what I’m doing here, and it doesn’t work ingame, so apparently I’m doing something wrong. Either the whole script is wrong (quite likely) or it doesn’t work because the invisibility potions are stackable items. I don’t know. Could someone help? This is my script:

int ItemsValid()
{

	object oPC = GetFirstPC();
	object oFM = GetFirstFactionMember(oPC, FALSE);

    int bHasPotion = FALSE;
	int bHasAllPotions = FALSE;
	int i = 0;
   
	object oItem;
    string sTag;
	
	while(GetIsObjectValid(oFM))
	{
	
		while (GetIsObjectValid(oItem))
	   	{
        	sTag = GetTag(oItem);
        	if (sTag == "NW_IT_MPOTION008")
        	{
            bHasPotion = TRUE;
			i++;
        	}
        

        oItem = GetNextItemInInventory();
    	}
	
	oFM = GetNextFactionMember(oPC, FALSE);
	}

	if(i==5)
	{
	
	bHasAllPotions = TRUE;
	
	}
		
		
    return bHasAllPotions; 
}

void main()
{

	object oPC = GetFirstPC();

	if (!ItemsValid())
	{

	FloatingTextStringOnCreature("You don't have enough invisibility potions.", oPC, FALSE);
	return;

	}
	
	else
	{

	FloatingTextStringOnCreature("Good! You have enough potions.", oPC, FALSE);
	return;

	}
	
}
1 Like

Maybe try with if (i>=5)
Do you want each party member to carry at least one potion of invisibility, or at least 5 potions in total? In the latter case, you’ll need to check the quantity.

I want it to be 5 potions in total.

Isn’t the quantity what I check for in if(i==5) ? :hushed:

Well, I tried with if (i>=5) but that didn’t work either (and in my test I have exactly 5 potions in the inventory of the PC).

void main()
{
	object oPC = GetPCSpeaker();
	int nNum, nStack;
	object oFM = GetFirstFactionMember(oPC, FALSE);
	
	while (GetIsObjectValid(oFM))
	{
		object oItem = GetFirstItemInInventory(oFM);
		
		while (GetIsObjectValid(oItem))
		{
			if (GetTag(oItem) == "NW_IT_MPOTION008")
			{
				nNum = GetNumStackedItems(oItem);
				nStack += nNum;
			}
			
			oItem = GetNextItemInInventory(oFM);
		}
		
		oFM = GetNextFactionMember(oPC, FALSE);
	}

	if (nStack >= 5) FloatingTextStringOnCreature("Good! You have enough potions.", oPC, FALSE);
		
	else FloatingTextStringOnCreature("You don't have enough invisibility potions.", oPC, FALSE);
}
2 Likes

Oh, so I have to use the GetNumStackedItems function because it’s a stackable item? That’s the fault I made?

Edit: I don’t quite understand the script here since I’ve always had difficulty understanding scripting. I’m sorry for being stupid here but…The int nNum, nStack thing. What does that mean? Does that mean they both have the same integer at first or what? Sorry for being so thickheaded.

For each character, nNum counts the number of potions each time a stack is found and then adds it to nStack, which means nStack counts the total number of potions for the party.

The variables are created, and both set to the default value (0 for integers).

HI you can use this

1 Like

I know what the function does. It says so in the Script Assist so I don’t need the lexicon for that. It was the thing that you can declare two integers after another with the comma (I don’t know if I’m expressing myself correctly here, but anyway).

Speaking with my brother he explained these things to me with the whole script, so I think I understand a bit better now. Another mistake I’d made in my first script is that I hadn’t declared what oItem was, like travus did with: object oItem = GetFirstItemInInventory(oFM);

Alright, I’ve tested travus’ script ingame now and it works as I expected it to.

Now I have a follow up question regarding this. I want to check to see if the PC and the whole party have 5 potions of this sort and then take away 5 potions. If the PC and party have more than 5 I only would like to remove 5 of them. Tried it with a script but it didn’t seem to work. The game removes all the potions and not just 5 if you, for instance have 7 of them. Here’s the part of the script:

	object oFM = GetFirstFactionMember(oPC, FALSE);
	
	int nNum;
	
	while (GetIsObjectValid(oFM))
	{
	
		object oItem = GetFirstItemInInventory(oFM);
	
		while (GetIsObjectValid(oItem))
		{
			if (GetTag(oItem) == "NW_IT_MPOTION008" && nNum <6)
			{
			
				TakeNumItems(oFM,"NW_IT_MPOTION008",1);
				nNum = nNum+1;
				
		
			}
			
			oItem = GetNextItemInInventory(oFM);
	
		
		}
		
		oFM = GetNextFactionMember(oPC, FALSE);
	
	}

Edit2: Nevermind. I managed to solve it in the post below here.

Edit: So the question is, how do you remove just 1 item in a stack of several, through scripting? I just saw that according to the lexicon the TakeNumItems takes the whole stack: “This function doesn’t handle stacks of items correctly, it will simply delete the required number of stacks of items regardless of how many items each stack holds.”

Alright. I managed to solve it in another way. I stored the number of potions in a global int. Then when destroying and taking the potions away, I after that created the amount of potions left in the inventory of the PC. Thus, if the player has more than 5 invisibility potions, only 5 will be destroyed. In reality though, all the potions are destroyed in the PC and the party’s inventory, but the ones that shouldn’t have been destroyed are recreated in the inventory of the PC. Below are the two functions I created and used, the first based on travus’ script. I noticed that I needed to wrap the CreateItemOnObject in a function and then using DelayCommand for the game not to destroy the newly created items.

int HasPotions()
{
	object oPC = GetFirstPC();
	int nNum, nStack;
	
	object oFM = GetFirstFactionMember(oPC, FALSE);
	
	while (GetIsObjectValid(oFM))
	{
		object oItem = GetFirstItemInInventory(oFM);
		
		while (GetIsObjectValid(oItem))
		{
			if (GetTag(oItem) == "NW_IT_MPOTION008")
			{
				nNum = GetNumStackedItems(oItem);
				
				nStack += nNum;
				
				
			}
			
			oItem = GetNextItemInInventory(oFM);
		}
		
		oFM = GetNextFactionMember(oPC, FALSE);
	}

	if (nStack >= 5)
	{
	
	SetGlobalInt("NumPotions",nStack);
 	return TRUE;
	}
		
	else return FALSE;
}

void AddPotionsLeft()
{

object oPC = GetFirstPC();

int nPotions = GetGlobalInt("NumPotions");
int nLeft = nPotions - 5;
	
CreateItemOnObject("nw_it_mpotion008",oPC,nLeft,"NW_IT_MPOTION008",FALSE);

}

fixed in Nwn2Fixes

void TakeNumItems(object oTarget, string sItem, int nNumItems)
{
	int iStack;

	object oItem = GetFirstItemInInventory(oTarget);
	while (GetIsObjectValid(oItem) && nNumItems > 0)
	{
		if (GetTag(oItem) == sItem)
		{
			iStack = GetItemStackSize(oItem);
			if (iStack > 1 && iStack > nNumItems) // 1.71: proper stacked items handling
			{
				SetItemStackSize(oItem, iStack - nNumItems);
				return;
			}

			ActionTakeItem(oItem, oTarget);
			nNumItems -= iStack;
		}
		oItem = GetNextItemInInventory(oTarget);
	}
}

- change ActionTakeItem() to DestroyObject() if the items should be destroyed instead of given to the caller.

 
huh i just noticed bug in that the items in a stack don’t get transfered to caller …

1 Like

@andgalf,

I know I am late in posting, as you have your answers already, but I just thought I would add that dealing with stacked items can be difficult. There are a couple of methods, much like you (delete all and recreate all) and KevL (set stack sizes) have pointed out.

However, I have encountered other difficulties in the past that require some careful attention. While you may not need this info now, it may be worth bearing in mind:-

  1. If you recreate items, remember the OnAcquire will fire again. Therefore, if you have any related script action based on this tag, you may need to consider a brief ignore variable check. SetItemStackSize avoids this (iirc).

  2. Watch out for stacked items inside containers, especially if you wish to maintain where they may be recreating.

  3. You need to consider extra steps when checking an entire party for specific stacked items to ensure you keep the correct amount across the whole group. e.g. Using recreate will move all remaining stock to one PC rather than (perhaps) keeping to original carrier.

  4. If an item with the same tag has a variable, extra care needs to be taken again. This is what prevents stackable items of same tag from stacking if they have different vars on them.

I have had to play around with stacked items a lot in my module, and so I am aware of the difficulties they can bring. I believe I have encountered most (if not all) now. If I recall some other points, I will add it to this post.

Thanks for both of your points. I haven’t downloaded/used Nwn2Fixes as I believe it to be a constant work in progress…I don’t know…and now you noticed a bug so…Well, I think I’ll stick with what I used for now. I’ve had a feeling it’s like with the Client Extension, that it’s really good but can create new unforeseeable bugs (like the Client Extension breaking my module/game on my laptop making it unplayable there), and that’s why I’ve been very reluctant to use it.

The only downside creating the items again is that they end up in the inventory of the PC instead of where they were originally (if you for some reason have these potions spread out among your companions), but I think this is such a minor thing that the player won’t mind it. The player can always move the potions left to the character they want afterwards.

Edit: At first when I used my script like this I used the function TakeNumItems, but I noticed that DestroyObject was far better since in that function you could choose not to notify the player that you take the potions, and in my case this was a lot better, otherwise the player sees that I remove all the invisible potions and not just the 5.

im sure it fixes far more stuff than it breaks

that said, it also adds another layer of complexity to an already complicated hierarchy of “this overrides that”

its primary mission is to fix bugs (some of them game-breaking) in the OC/MotB/SoZ

but it also has many script/model/etc fixes (albeit of stock resources only – which can cause conflicts with custom or 3rd party content that wants to override those same resources)

 
it is what it is.

1 Like

int GetNumItems(object oTarget,string sItem)
{
int nNumItems = 0;
object oItem = GetFirstItemInInventory(oTarget);
while (GetIsObjectValid(oItem) == TRUE)
{
if (GetTag(oItem) == sItem)
{
nNumItems = nNumItems + GetNumStackedItems(oItem);
}
oItem = GetNextItemInInventory(oTarget);
}
return nNumItems;
}

/* Script generated by
Lilac Soul’s NWN Script Generator, v. 2.3

For download info, please visit:
http://nwvault.ign.com/View.php?view=Other.Detail&id=4683&id=625 */

int StartingConditional()
{
object oPC = GetPCSpeaker();

if (GetNumItems(oPC, “pocioninvisible”) < 5) return FALSE;

return TRUE;
}