The situation is this: The PC is told that in order to make a Moonberry Potion they’ll need the juice from a half dozen Moonberries. So they go out foraging and eventually collect 6 or more Moonberries - enough to make the potion.
Then they bring it back to an Alchemist Apparatus, interact with the placeable, opening a conversation. The script in the conversation is supposed to check that the PC has in their inventory Moonberryx6 before allowing for the option to brew the potion. And then when this option is selected - then 6xMoonberry are removed from the PC inventory and a Moonberry Potion is given to them.
The problem it is easy enough for the script wizard to deal with a single of a particular item. But I want the script to recognize a specific number of a particular item. And also to remove a specific number of a particular item from the inventory.
I swear I’ve seen this kind of thing before in other modules. But, of course, now that I want to copy the same behavior - I can’t find an example anywhere.
In this case, instead of using GetItemPosssedBy(), you have to examine the entire inventory, something like this snippet:
int nBerryCount = 0;
object oItem = GetFirstItemInInventory(oPC);
while (GetIsObjectValid(oItem))
{
if (GetTag(oItem) == "Berry") ++nBerryCount;
oItem = GetNextItemInInventory(oPC);
}
return (nBerryCount >= 6);
The code for removing berries will be similar. You will need to assign oPC and use the correct tag for the berries. If they are stackable, instead of incrementing the counter by 1, you need to use GetItemStackSize().
Try generating the code for a single item, then add lines similar to the above.
It probably won’t work first time, but you’ll learn more by having a go, posting you script here for further help.
P.S. I’m writing this on a tablet, so check for typos.
Your first stop should be to look in the “Neverwinter Nights Newbie FAQ #2.pdf” which is part of the NwN Scripting FAQs & Tutorials download on the Just The FAQs, ma’am project page. After a quick look in there I came across this Frequently Asked Question -
Q) How do I get an NPC to take more than one of the same item from a PC’s inventory? (aka “The Goblin-Ear Dilemma” ) A) Good call for this thread – this one comes up a lot, because the Script Wizard in the conversation editor doesn’t really handle cases like this. What you need to do is loop through every item in the PC’s inventory looking for your item, and perform an action on each one you find. Two functions make it easy to loop through the whole inventory:
GetFirstItemInInventory and GetNextItemInInventory.
Here’s an example of how to use them to reward a PC for every item they have with the tag “goblin_ear”:
// This script goes in the "Actions Taken" tab of a
// conversation. It will reward the PC 10 gold and
// 10 XP for every item they possess with the tag
// "goblin_ear".
void main()
{
// First, get the PC being spoken to
object oPC = GetPCSpeaker();
// Now, find the first item in that PC's inventory
object oItem = GetFirstItemInInventory(oPC);
// Start the loop. The next statement says to
// stay in the loop until "oItem" no longer points
// to a valid object. This will happen as soon as
// we've looped through every one of the PC's items.
while (GetIsObjectValid(oItem))
{
// Check the tag on the current item
if (GetTag(oItem) == "goblin_ear")
{
// Statements within these brackets will
// be run for each item that matches the
// "goblin_ear" tag.
// The next two lines give the rewards:
// 10 gold and 10 XP.
GiveGoldToCreature(oPC, 10);
GiveXPToCreature(oPC, 10);
// Now take the ear from the PC. Note that
// this doesn't really take the ear and give
// it to the NPC; it just destroys it. Why
// do NPCs give money for these things,
// anyway? Who wants a bunch of rotting ears
// in their pockets?
DestroyObject(oItem);
}
// Now move on to the next item
oItem = GetNextItemInInventory(oPC);
} // End while loop
} // End script
Which, with a little editing (remove the XP and gold awarrds, perhaps) might suit.
Then 2 or 3 questions later there was this one -
Q) How do I get an NPC to Check for more than 1 of the same item in a PC’s inventory? (option 2) A) A simple way to check for Items in a “Text Appears When” script mode is to use the GetNumItems(). This is a bit easier than going through the Inventory and “counting” each item. This need to be in the “Text Appears When” Script node of the Text line you want to appear to the PC if he/she has the correct amount of said Item. Change “ITEM_TAG” to that of the Item you are looking for and set nMarkCount to the number of items you want.
#include "nw_i0_plot"
int StartingConditional()
{
//check inventory of oPC for oQuestItem and get iNumItems
string sMark = "ITEM_TAG";
int nMarkCount = 2;
object oMark = GetObjectByTag(sMark);
if(GetNumItems(GetPCSpeaker(), sMark) >= nMarkCount)
return TRUE;
return FALSE;
}
Very well, prolerics script is perfect (I can’t do it better ), if the item is not stackable. Say, can the berries be stacked?
If the item can be stacked, then counting it is a little bit more tricky and removing the proper number is just not really trivial. You’ll find sample scripting in my module “bloodfeud”, see dialogue “auktionator” and script “inc_takeitems”. It’s to handle smugglercoins (Stacksize 10 possible) and will work for non stackable items as well.
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.
Good job that function doesn’t appear in either of the functions I posted.
I finally got a little free time to test this and Proleric’s script worked great for checking for the correct number of berries in the inventory. Thanks.
But regarding removing the berries from the inventory - to clarify - I don’t want to remove all the berries from the PC’s inventory - just exactly 6 of them. So if the PC has picked 8 berries, they should have 2 berries left over after they brew the potion. If they have picked a dozen berries, they should be able to go back and brew the potion again - and so on.
Try this then - it’s a melding of the 2 functions I showed you before -
[edit]
Do not use this version. It has a major error. Instead try the version lower down thread.
// This script goes in the "Actions Taken" tab of a conversation.
#include "nw_i0_plot"
const int nMarkCount = 6;
void main()
{
int iCount = GetNumItems(GetPCSpeaker(), "ITEM_TAG"); //replace ITEM_TAG with actual tag of item to destroy
object oPC = GetPCSpeaker();
object oItem;
if(iCount >= nMarkCount)
{
oItem = GetFirstItemInInventory(oPC);
while(GetIsObjectValid(oItem) && iCount)
{
// Check the tag on the current item
if (GetTag(oItem) == "BerryTag") //replace BerryTag with actual tag of item to destroy
{
DestroyObject(oItem);
iCount--;
}
// Now move on to the next item
oItem = GetNextItemInInventory(oPC);
} // End while loop
CreateItemOnObject("PotionResRef", oPC); //replace PotionResRef with actual ReeRef of potion to give
}
}
Pay attention to the comments in the code. What it is supposed to do is -
Check that the PC has 6 or more berries.
If they do, destroy just 6 of them and finally
Give the PC 1 potion.
Note that I haven’t tested this code so there could be syntax or other errors. Also that the above code assumes that the berries are not in a stack. While the count should work with stacked items, the destruction may not.
After a short glance on the code, I just want to point that out, so that nobody relies on the bio-standardfunction. I didn’t consult the lexicon. It’s quite obvious that in the near vicinity of the count-function is a take-function too.
Ooops. Well my excuse is it was late at night. Here is a hopefully fixed version -
// This script goes in the "Actions Taken" tab of a conversation.
#include "nw_i0_plot"
const int nMarkCount = 6; // Placed here to make it easier to find if you want to change the number of berries
void main()
{
object oPC = GetPCSpeaker();
object oItem;
int iTotalCount = GetNumItems(oPC, "ITEM_TAG"); //replace ITEM_TAG with actual tag of item to destroy
int iCount = nMarkCount;
if(iTotalCount >= nMarkCount)
{
oItem = GetFirstItemInInventory(oPC);
while(GetIsObjectValid(oItem) && iCount)
{
// Check the tag on the current item
if (GetTag(oItem) == "BerryTag") //replace BerryTag with actual tag of item to destroy
{
DestroyObject(oItem);
iCount--;
}
// Now move on to the next item
oItem = GetNextItemInInventory(oPC);
} // End while loop
CreateItemOnObject("PotionResRef", oPC); //replace PotionResRef with actual ReeRef of potion to give
}
}
No need for an excuse We all write bugs from time to time…
Fwiw, something like this in the if(GetTag() ... block should handle stacked berries.
int nCurStack = GetItemStackSize(oItem);
if (nCurStack > iCount) {
SetItemStackSize(oItem, nCurStack - iCount);
break; // we're done.
}
// stack is <= iCount take them all and adjust iCount.
DestroyObject(oItem);
iCount -= nCurStack;
I just used the copy button on the code box and then pasted it into the toolset script editor. I then compiled it with no syntax errors, so it is most likely an error at your end.
If you did include the #include, you did place your tag in quotes (example “ITEM_TAG”), didn’t you?
Another quick piece of advice, this time about copying the code. When there is code posted in here in a code box, there is a very easy way to copy all of it. Hover your mouse pointer over the code for a short time and look at the top right of the code box. A copy button should appear. Click on that and you copy everything that is in the code box to your clipboard from where you can paste it wherever you want.