Aller au contenu

Photo

Have a NPC check for multiple items?


  • Veuillez vous connecter pour répondre
18 réponses à ce sujet

#1
Apeleutheros

Apeleutheros
  • Members
  • 38 messages

Please bear with me, I'm trying to learn the basics.

 

I'm making a "Go kill and fetch" quest.

 

Sorry, for being cliche, but a tavern keeper wants rats killed in the cellar (I know, I know... I'm a sucker for cliche's as long as its done well, and done with some humor.)

 

I want the Tavern keeper to demand 10 rat tails, but I do not know how to script (I'm trying to learn). I'm testing the script only using 3 tails. The script I placed into the "Text Appears When" looks like this:

 

 

int StartingConditional()
{

    // Make sure the PC speaker has these items in their inventory
    if(!HasItem(GetPCSpeaker(), "RatsTail01"))
    if(!HasItem(GetPCSpeaker(), "RatsTail02"))
    if(!HasItem(GetPCSpeaker(), "RatsTail03"))
        return FALSE;

    return TRUE;
}

Obviously I didn't do it right. The inn keeper only checks for 1 of the tails, not all 3, and the completion dialog fires. My question is, how do I make him check for all 3 tails? I assume it would by placing a + in an appropriate place?

 

Honestly, I would prefer it if there is a way I can just have the inn keeper check for a quantity of x10 "RatsTail01" so I don't clutter up my plot items list with 10 rat's tails with separate tags.

 

Sorry, I haven't really gotten familiar with the scripting language yet. Sifting through the lexicon is like reading gibberish sometimes. I think once I get it, it should make sense to me.



#2
Apeleutheros

Apeleutheros
  • Members
  • 38 messages

Would this work?

 

 

 

int StartingConditional()
{

    // Make sure the PC speaker has these items in their inventory
    if(!HasItem(GetPCSpeaker(), "RatsTail01", 10))
        return FALSE;

    return TRUE;
}

Just add a value of 10 after the "item tag"?

 

EDIT: NVM, it didn't work.



#3
Tarot Redhand

Tarot Redhand
  • Members
  • 2 666 messages

A quick check with the lexicon reveals -

 

GetNumItems(object, string)

Gets the number of the item that the target carries.
 
int GetNumItems(
    object oTarget,
    string sItem
);
 
Parameters
 
oTarget
 
The creature to check the inventory.
 
sItem
 
The tag of the item to search for.
 
Description
 
Returns the number of items of sItem that oTarget has in inventory, to include all stacked items.
 
Remarks
 
Function can be found in nw_i0_plot.nss line 780. 
 
This will count each individual item in a stack, as well as items that are not in a stack. 
 
Does not count items equipped by oTarget.
 
Requirements
 
#include "nw_i0_plot"

 

In other words you need to put the line

 

#include "nw_i0_plot"

 

at the top of your script before GetNumItems is called.

 

TR



#4
KMdS!

KMdS!
  • Members
  • 189 messages

Let me give you a few things to think about. You might not understand what I am saying, but take the time to try to figure it out. 
When you get it, you will have a much better understanding of what you want to do. I don't want to give it to you w/o your taking a moment to think, and learn. It's the best way.

  • Are the rat tails "stackable". like potions or arrows?
  • Or are they unique, have different tags or resrefs for each tail?

This will affect how you go about finding them in the PC's inventory.

 

If the rat tail is stackable , you can find if they have the item in their inventory by using the GetItemPossessedBy method. Then use the GetItemStackSize method to see if they have the requisite quantity.

 

If they are unique items with different tags, you can use the same method, but will have to run the check multiple times, one for each tag/item.

 

The most likely situation is they are the same item (same tag and resref), and are not stsckable, In this case you mus set up a loop using the "while" loop to search the pc's inventory, setting up a counting variable to track each instance found, set each one as a local object on the pc so that if they have the requisite quantity, you can remove them from the players inventory when you dole out the reward.

 

Sounds like a daunting task? if you are new to nwscripting, it should be. Never fear, this is about as complicated as most scripting gets. Get a feel; for this and you will be ok. It's actually not that hard. Use the lexicon. Give it a try and whatever happens I will get you a working script in a couple of days that will get you close, if someone else doesn't do it first.

 

Keep trying and post your questions in the mean time.


  • Apeleutheros aime ceci

#5
Tarot Redhand

Tarot Redhand
  • Members
  • 2 666 messages

@KMdS! Why go to all that bother when the function I found doesn't care if the items are stackable or not? It does the counting for you. The only caveat is that they must all have the same tag which from the description the op gave, the rat's tails should in all probability have. If they don't the question is why don't they? is there some compelling reason to have a different tag for each one? It cleans up and shortens the script considerably. like so -

#include "nw_i0_plot"

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

    if(!(GetIsPC(oPC)))
        return FALSE;

    int ReturnValue = TRUE;

    // Make sure the PC speaker has these items in their inventory

    if(GetNumItems(oPC, "RatsTail")  < 10))
        ReturnValue = FALSE;

    return ReturnValue;
}

TR


  • Apeleutheros aime ceci

#6
Apeleutheros

Apeleutheros
  • Members
  • 38 messages

Thanks for the responses you guys.

 

I realized last night that I just need to sit down this weekend and get a good primer on scripting so I start to grasp the language. After I get the rat quest finished from your help, I am going to go through Celowin's scripting tutorial and do a bunch of exercises so scripts start to make some sense to me.

 

So far, I have been taking the things that you guys give me, using the variable wizard, or just finding existing scripts to frankenstein together. It seems to work but when I look at the scripts, they just looks like greek to me (At least in ancient greek I can form some sentences and sort of understand the function of certain case endings). When I start to grasp the commands, functions, and their structure, the lexicon will be much more helpful to me than it currently is.



#7
KMdS!

KMdS!
  • Members
  • 189 messages

@KMdS! Why go to all that bother when the function I found doesn't care if the items are stackable or not? It does the counting for you. The only caveat is that they must all have the same tag which from the description the op gave, the rat's tails should in all probability have. If they don't the question is why don't they? is there some compelling reason to have a different tag for each one? It cleans up and shortens the script considerably. like so -

#include "nw_i0_plot"

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

    if(!(GetIsPC(oPC)))
        return FALSE;

    int ReturnValue = TRUE;

    // Make sure the PC speaker has these items in their inventory

    if(GetNumItems(oPC, "RatsTail")  < 10))
        ReturnValue = FALSE;

    return ReturnValue;
}

TR

W/O knowledge of what he did I just thought to include as many contingencies as possible. His listing of "RatsTail01", "RatsTail02", "RatsTail03" left me unsure whether a mistake in logic or actually unique items.

 

I had forgotten the particular method you used and had difficulty reading your earlier post.  :P

 

@Apeleutheros

That's a good idea. Here's looking to your future success!



#8
KMdS!

KMdS!
  • Members
  • 189 messages

Did a little research and found why I didn't remember the method. Here is the actual code for the method

 

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;
}


#9
KMdS!

KMdS!
  • Members
  • 189 messages

Phoooey, I don't like the way the board processes posts, lost the actuaql info I wanted to convey because I entered it after the code....Here goes a second time.... :wacko:

 

Anyway, The method actually performs the loop I described above but performs no work. Without the required processing of the item searched for, another loop must be run through the PC's inventory to remove or otherwise process the item. That can be a very bad thing as players can have an incredible amount of inventory on hand and running multiple loops through it will many times get you the unwanted TMI (Too Many Instructions) error. Also, the method does not check any of the PC's container items either...more loops and loops. For this reason I never consider it, BUT!!!!!, it is an excellent starting template for the loop required.



#10
KMdS!

KMdS!
  • Members
  • 189 messages

Here would be a better template.

 

*Edited to take into account new info referenced below

// Return the number of items with the tag provided.

// Set bProcess = TRUE to store the item instance for processing outside of this method
// and the total count of instances found.
// Retrieve an instance of an object by retrieving the local object "ITEM_NUMBER_*") stored
// on the target object, the * must be a number between 1 and the total count of instances stored in
/// the local variable int "ITEM_COUNT".
// Retrieve the total number of item instances by retrieving the local variable int "ITEM_COUNT"
// stored on the target object.
int GetNumberOfItemsInInventory(object oTarget,string sItemTag, int bProcess = FALSE);
int GetNumberOfItemsInInventory(object oTarget,string sItemTag, int bProcess = FALSE)
{
    int nNumItems = 0;
    int iIteration = 0;
 
    object oItem = GetFirstItemInInventory(oTarget);
    while (oItem != OBJECT_INVALID)
    {
        if (GetTag(oItem) == sItemTag)
        {
            nNumItems += GetNumStackedItems(oItem);
            if(bProcess)
            {
                // Store the item for retrieval outside this method
                SetLocalObject(oTarget, "ITEM_NUMBER_"+IntToString(++iIteration), oItem);
                /***Place whatever additional processing you want one here**/
            }
        }
        oItem = GetNextItemInInventory(oTarget);
    }
    if(bProcess)
    {
        // Store the number of instances found for retrieval outside this method
        SetLocalInt(oTarget, "ITEM_COUNT", iIteration);
        /***Place whatever additional processing you want one here**/
    }
 
   return nNumItems;
}


#11
meaglyn

meaglyn
  • Members
  • 802 messages

You don't need the loop with the containers. Things in the PC's inventory which are in containers will still show up in the main loop. You can count things twice the way you have it.  TR already pointed to the GetNumItems routine so it should not have been too hard to find :)



#12
KMdS!

KMdS!
  • Members
  • 189 messages

Wonderful, it's fun top learn something new. Thanks..

 

Anyway, there is still the issue of having to run multiple loops to actually do anything with the items found.  I edited the post above to reflect the new information. and be able to do something to minimize the need to loop through inventory more than once should you need to do something with the items found.


  • meaglyn aime ceci

#13
meaglyn

meaglyn
  • Members
  • 802 messages

Yeah, I like your approach of saving them to temp local object vars so you can get at them later.  That can be useful.



#14
Apeleutheros

Apeleutheros
  • Members
  • 38 messages

I ended up using this script from the wiki for now.

 

#include "nw_i0_plot"
int StartingConditional()
{
    //check inventory of oPC for oQuestItem and get iNumItems
    string sMark = "RatsTail01";
    int nMarkCount = 10;
    object oMark = GetObjectByTag(sMark);
    if(GetNumItems(GetPCSpeaker(),sMark) >= nMarkCount)
        return TRUE;
    return FALSE;
}

It was easy to set up and it does what I want. I don't know if it will work if the rat tails are in a bag. At this point the PC is level 1, and doesn't have an opportunity to purchase a bag (It's late at night and the stores are all closed).

 

He then takes the tails,

 

#include "nw_i0_plot"
void main()
{
    string sMark = "RatsTail01"; // Enter tag of your Item
    object oMark = GetObjectByTag(sMark);
    int nMarkCount = 10; // Change to num Items you want
    if(GetNumItems(GetPCSpeaker(),sMark) >= nMarkCount)
        {
        TakeNumItems(GetPCSpeaker(),sMark,nMarkCount);
        // Uncomment the following line to Give the PC an Item Also
        // CreateItemOnObject("item_ResRef",GetPCSpeaker(),1);
        }
}

I still have to set it up so the Inn Keeper rewards the player with exp/gold and set a variable to open up the next part of the plot.

 

The dialogs are getting a little overwhelming for me. I'm trying to offer opportunity for the PC to role play a bit and it opens up a lot of trees. I'm getting it figured out, but all the dialog branches, copying, pasting, looping in the dialog, and all the variables and scripts are overloading my meager, one task brain, lol.



#15
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages

Wonderful, it's fun top learn something new. Thanks..

 

Anyway, there is still the issue of having to run multiple loops to actually do anything with the items found.  I edited the post above to reflect the new information. and be able to do something to minimize the need to loop through inventory more than once should you need to do something with the items found.

 

in a conversation it is better to run multiple loops then to store the number of items as a local,  due to players exploits.   


  • Shadooow aime ceci

#16
KMdS!

KMdS!
  • Members
  • 189 messages

Hmmm. As long as you set up the proper protocols for dealing with the stored variables, reseting at appropriate locations in the conversation etc., how can it be exploited?

 

Simple conversations my not encounter a problem, but it's best to know the possibilities.

 

My main concern is running loops in inventory, given there are 300 open slots for inventory which players load up with containers allowing for an additional 35 slots each, it can cause a problem. Even though many inventory items take up up to 6 slots, players can easily accumulate 3 to 4 hundred items or more. The nw engine has a hard coded limit of 1000 instructions, any second or third inventory loop can exceed the limit and break the running of the code and pay havoc with convo conditionals. Since the item searche methods all run an entire inventory loop the situation can come up.

 

Best to plan your use of loops. The best option is to plan ahead and the submitted code is a starting template.

 

Please let me know if you have information I am missing Lightfoot8, you are one I know that knows your stuff.

 

Thanks.



#17
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages

The exploit happens when the PC starts dropping / handing off items mid conversation.   So since you need to double check or reset your count anyway why not just recount them every time.  An exception would be if you need the same count in  several starting conditions on the same node, where the PC does not have any response time.  

 

As far as hitting the TMI error.   that is only going to happen if you over load a single script.  Running a script from a script without delay would also keep the instruction count progressing.    But from the conversation the instruction count will get reset at the beginning of every script ran.  So every starting condition/ action taken script has its own instruction count and it does not matter what happened in the other scripts.   

 

As far as number of executed VM  Instructions it takes to get a TMI the number is 0x2000 in hex. Or 8192 in decimal.  

Hmm that number still seems small. I my have to double check my memory later.     



#18
meaglyn

meaglyn
  • Members
  • 802 messages

Saving the actual object,  not just the count, as KMds! is doing should mitigate the dropping/handing off issues. The items could still be destroyed or taken.  Edit: but that still can be exploited for stackable items...

 

The instruction limit is 0x20000 (131,027). I think you dropped a 0 :)



#19
KMdS!

KMdS!
  • Members
  • 189 messages

That is a very good point LF8. Hmmm...There are ways to prohibit that through proper code logic, and the example code allows for it, possibly even can handle it better that any other way. Since the number of instances are stored, not the number it items, you can perform a small loop through the just saved objects to verify the item count as a check. No matter what the pc does, short of destroying the item during the specific node chain, the item would be processed. Making proper use of the conditional, action taken and end events there should be no escaping or exploit. Logic can prevail, as long as you know what your up against.

 

There will always be those that want to exploit the system and no method is immune, all we can do is plan for all we can conceive and get our friend to help us try to cover all the bases. Thanks, that particular exploit had not occurred to me.