Streamlining Quests

Our PW (Amon) has quite a few bounty type quests. All are conversation based with 3 or 4 individuals. I’d like to add the ability to check “Companion” inventories (not party, but Henchmen) for, and these items.

Bounty Check

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

if (GetItemPossessedBy(oPC, “KoboldEar”) == OBJECT_INVALID) return FALSE;

return TRUE;
}

Bounty Redeem

#include “pcinclude”
void main()
{
object oPC = GetPCSpeaker();
// Remove items from the player’s inventory
int nPelts = DestroyItemsInInventory(oPC, “KoboldEar”, 9999);
// Give the speaker some gold
GiveGoldToCreature(oPC, 50 * nPelts);

// Give the speaker some XP
GiveXPToCreature(oPC, 50 * nPelts);

}

Also, there is an individual script for each item check and for each item redemption. Is it feasible to consolidate all the checks into a single script, and all the redemptions into another script, and how would that affect the conditions of the conversation?

Also, also (wik), does anyone have handy a method for increasing stack sizes beyond 99? And what effect would this conceivably have on a server running fairly vanilla scripts on modern hardware (i.e. not yet NWNX)?

Thanks
Sknymick

1 Like

It sure is feasible to put together catch-all scripts for bounty items, and in a rather lot of different ways, to boot.

If you want to do it in two steps, you’d want one script to check whether the PC or any of their associates have any qualifying bounty item in their possession, and one script to destroy/take all the bounty items and give the reward. The effect on the conditions in the conversations would, for one thing, be that being in possession of any one bounty item would make the dialogue option to redeem the bounty item show up for all others, so you’d probably want to set up a single generalized [Redeem bounty items]-dialogue option that works for all of them.

Disclaimer going forward: Primarily a singleplayer scripter, no PW experience, may not be aware of performance-friendlier options.

To check through the companion inventories, you could cycle through the members of the PC’s faction, and check the inventories of each faction member whose master is the PC. Or use GetAssociate() with an incrementing integer in a while loop, specifically checking one henchman-type associate after another as long as it keeps finding a henchman. That’d probably be saner in this case, since the faction-members-GetMaster()-method checks summoned creatures and such as well, which might be unnecessary for this.

Example conditional script for the first half of that, using the former method, checking for match by ResRef:

// Compares sResRef against all listed bounty item ResRefs.
// Returns TRUE if it's a match, FALSE if not.
int GetIsBountyItemResRef(string sResRef);
int GetIsBountyItemResRef(string sResRef)
{
    if (sResRef == "resrefofthekoboldears" ||
        sResRef == "somethingsomething"    ||
        sResRef == "anothersomething")
        return TRUE;
    else
        return FALSE;
}

// Looks for bounty items in oTarget's inventory, as defined in
// GetIsBountyItemResRef().
int GetHasBountyItem(object oTarget=OBJECT_SELF);
int GetHasBountyItem(object oTarget=OBJECT_SELF)
{
    int nHasBountyItem;
    object oItem = GetFirstItemInInventory(oTarget);
    while (oItem != OBJECT_INVALID)
        {
        if (GetIsBountyItemResRef(GetResRef(oTarget)))
            nHasBountyItem = TRUE;

        oItem = GetNextItemInInventory(oTarget);
        }

    return nHasBountyItem;
}

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

    // Check if the PC has a bounty item.
    if (GetHasBountyItem(oPC))
        {
        nHasBountyItem = TRUE;
        }
    // If they don't...
    else
        {
        // ... check if any one of the PC's associates has a bounty item.
        object oTarget = GetFirstFactionMember(oPC);
        while (oTarget != OBJECT_INVALID)
            {
            if (GetMaster(oTarget) == oPC &&
                !GetIsPC(oTarget))
                {
                if (GetHasBountyItem(oTarget))
                    nHasBountyItem = TRUE;
                }

            oTarget = GetNextFactionMember(oPC);
            }
        }

    // Return the result.
    return nHasBountyItem;
}

Disclaimer: Not tested, only compiled.

But it could be streamlined even further, by putting the entire thing into a single script, and automatically removing the bounty items and handing out the rewards as they’re being found while looking through the items in the inventory. That way, you’d just be checking through the inventories of the PC and their associates once.

For increasing stack sizes, I’d try altering baseitems.2da. There’s a “Stacking” column in it that has different values, ranging from 1 to 99 to 50000 (gold).

Tip: [code] insert text here [/code]

2 Likes
  1. Use GetFirstFactionMember and GetNextFactionMember to loop through the party
  2. Then use if(GetAssociateType(oMember) == ASSOCIATE_TYPE_HENCHMAN) to determine if a faction member is a Henchman
  3. #include “x0_i0_henchman” and use GetMaster if you need to determine the master too.
2 Likes

@Sknymick You might find this thread useful for using these forums (including how to change your avatar).

TR

@TheBarbarian Thank you very much for that. I’ll put this to work in a script (and conversation) and see what should arise. My “two step” solution request is only because that’s what’s there now… 100 times. If I can turn 200 scripts into 8 scripts (or even 4) that’s going to be money.

  • Would I simply place this at the bottom of the provided script:
int nBounty = DestroyItemsInInventory(oTarget, “KoboldEar”, 9999);
GiveGoldToCreature(oPC, 50 * nBounty);
GiveXPToCreature(oPC, 50 * nBounty);

int nBounty = DestroyItemsInInventory(oTarget, “OrcEar”, 9999);
GiveGoldToCreature(oPC, 75 * nBounty);
GiveXPToCreature(oPC, 75 * nBounty);

Or does it need something more declarative? Or if i’m going directly for the reward, do I need to “Return the result”?

  • Will the “GetNextFactionMember(oPC);” in the associates section check other party members also? I ask because I’ve seen it in some of our larger quest scripts that check the entire party for quest items.
  • Also, does a baseitems.2da change require clients to download .haks? Or an override? If so, I can wait on this. I’m trying to convince my server admin/owner that we can use NWNX, but that’s a different topic.

@pscythe Will this improve on the code that TheBarbarian supplied? I’m pretty heavy handed with the copy/paste, but I’ll do it if it will help. We have a fairly small player base, and the likelihood that this code will have to search through many people is slim. But I’m more prone to doing the job right the first time.

@Tarot_Redhand - Message received and understood. I thought I was preventing people from having to scroll through my old code, but looking back, it can be unclear what (or even that) I was asking. I shall comply in the future :slight_smile: I’m kinda low maintenance, and my big green S is easy to spot among all the other pictures… So it might stay, unless it’s recommended that I change it.

To me, this is all like hearing a conversation in Latin. “I understood that word!.. And that one!!” but sometimes the syntax and order evade me, so please forgive my ignorance and clumsiness. Klaatu Verata Necktie… Nectarine… It was definitely an N word…

1 Like

@Sknymick my post supplements @TheBarbarian’s. His code posted above is actually a complete solution. But if you want you could change

if (GetHasBountyItem(oTarget))

to

if (GetAssociateType(oTarget) == ASSOCIATE_TYPE_HENCHMAN && GetHasBountyItem(oTarget))

But you really don’t have to do it.

All that does is make sure you check Henchman Inventories only and not summons, familiars or animal companions.

1 Like

@pscythe I can see the value of not checking creatures needlessly, especially on a busier server. I’ll amend what @TheBarbarian gave me in the interest of having it prepared for the next guy that takes over, in hopes that Amon re-grows. Thank you.

1 Like

what exactly you want to achieve?

you can increase max stack in baseitems.2da if you think 99 is too little

there is a method how to spawn stack of more than 99 while still having 99 as max but it is quite complicated so first tell me what you want to do exactly with that

1 Like

I am sure you already know this…

In both functions where you got nHasBountyItem = TRUE inside a loop, it can be rewritten to return TRUE to break out off the loop early instead of just looping on needlessly when you already have the result.

2 Likes

:thinking: If DestroyItemsInInventory() does what it looks like it does, which is “destroy all items with a particular tag and award gold and return the number of items it found”, then you’d want to put the condition check script into the “Text Appears When…” tab of the dialogue node, and the bounty redemption into the “Actions Taken” one. The Actions Taken script would stand on it’s own, and look something like this:

#include "pcinclude"

// Destroys all items tagged sTag possessed by oHasItem, and gives oGetsReward
// the XP and gold rewards times the amount of items found.
void RedeemBountyItemType(string sTag, int nGoldReward, int nXPReward, object oHasItem, object oGetsReward)
{
    int nBounty = DestroyItemsInInventory(oHasItem, sTag, 9999);
    GiveGoldToCreature(oGetsReward, nGoldReward * nBounty);
    GiveXPToCreature(oGetsReward, nXPReward * nBounty);
}

// Redeems bounty items. Lists tags and XP/Gold rewards of redeemable
// bounty items; add new bounty items here.
void RedeemAllBountyItems(object oHasItem, object oGetsReward);
void RedeemAllBountyItems(object oHasItem, object oGetsReward)
{
    RedeemBountyItemType("KoboldEar", 50, 50, oHasItem, oGetsReward);
    RedeemBountyItemType("OrcEar",    75, 75, oHasItem, oGetsReward);
}

void main()
{
    object oPC = GetPCSpeaker();

    // Redeem bounty items on the PC speaker.
    RedeemAllBountyItems(oPC, oPC);

    // Start looking through the faction members.
    object oTarget = GetFirstFactionMember(oPC);
    // As long as a valid faction member continues to be found...
    while (oTarget != OBJECT_INVALID)
        {
        // Only target faction members who are henchmen whose master is the PC speaker.
        if (GetAssociateType(oTarget) == ASSOCIATE_TYPE_HENCHMAN && GetMaster(oTarget) == oPC)
            {
            // Redeem bounty items on the henchman.
            RedeemAllBountyItems(oTarget, oPC);
            }

        // Check the next faction member.
        oTarget = GetNextFactionMember(oPC);
        }
}

But you could probably leave the conditional script out entirely, and let the “[Redeem bounty items]” option be always available, just using this one script under Actions Taken. Trying to redeem bounty items without being in possession of any should just not do anything whatsoever (0*50), assuming there are no problems with the DestroyItemsInInventory function. It’d skip the effort of cycling through the faction members and the items in the associate inventories.

I think so, but I get all my testing done in singleplayer mode for lack of easily-accessible guinea pigs, so don’t take my word for it. :x Either one of the GetAssociateType and the GetIsPC checks should exclude other PCs, though, skipping over faction members who have the henchman associate type or are PCs themselves.

Without NWNX, for sure. :thinking: I don’t know what can be done with NWNX, unfortunately. I’ve seen someone say they saw edits I’d normally expect to be 2da-dependent on a PW without having had to download anything once, but I can’t judge whether they were right, or how it was done if they were. Normally, yes - if a file is edited, the edited file needs to be distributed to the players somehow if it’s supposed to have an effect for them.


@pscythe
Assume that I just sat down and started tinkering one day, and have no idea what I’m doing. Pointing stuff like this out is always worthwhile and welcome. :heart:

1 Like

you should try PRC Lite then ;), though you should know what is normally not possible before you try it because everything in it uses vanilla GUI so it looks like its from Bioware

anyway changing stacksize doesn’t require to distribute that change to players - all you need is to put it into override folder on server (if module is not using any haks with this 2da in them) or put it into top-hak.

1 Like

@Shadooow
Does this go for all 2da-based changes on PWs? What about models, scripts, textures? Can stuff just be plonked into the override folder on the server?

Offtopic-reduction!
i tried to once but i got tangled up in the installation and then i gave up and felt bad about being *too stupid to install something* x_x and then i felt bad about giving up because *that's not how to get things done at all* but then i got distracted by other things and ever since then i just kind of avoid the whole nwnx sector like it's on fire and emanating an uncomfortable heat it is a source of ongoing regret for me if you teach me how to use this stuff i will in turn teach others x_x

Backtracking to check - it was Roaringthing who asked about making alterations to classes.2da, to make rangers unable to cast spells, but without mandating a hak. That help request might still be open.

no not all, something requires players to have same 2da or at least same value in 2da and something doesn’t

I have this info in my head but I am not exactly keen on writing some exhaustive list for various reasons… sorry.

Just try to change it in override, if it won’t work for players then they need that 2da too.

1 Like

@TheBarbarian @Sknymick I’m still building/writing, so not tested anything extensively, however when I tried the 2da changes to Classes and Spells etc I did have to have the 2da on my client to get them to work (I think ;p) - I’m building on Windows and play on OS X, so have two separately folders, which functions like another player basically.

I’ve kept my world vanilla, with no hak paks, so would be a real shame to make anyone have to download / install something to try / play - I suppose that is the benefit of NWNX. For example Arelith server is basically a different game with all the class and feat changes etc and there is no extra content required, so I can only assume this is all NWNX based.

2 Likes

@Shadooow - increasing stack size - would like to have larger arrow stack sizes and “Gem” stack sizes to reduce used inventory space.

  • If I change my moduel with PRC Lite (without NWNX) is the client required to install it to play? To simply use some functionality?
  • I’ll ask the rest of my questions on my other post. I don’t want to cross into this one too much (and you’re very helpful/prolific on the board)

@pscythe - I did not, in fact, already know. I can decode a script as I’m reading it, just as a novice reader can understand most of The Wheel of Time. But I am no Robert Jordan.

@TheBarbarian - Even more thanks to you. I had consolidated all my Action Taken turn in scripts and had more than 300 lines of code… This will reduce that by half. I’ll consolidate my list into the code and paste it in a new reply.

@Roaringthing - You sir are more ambitious than I. I harvested an old favorite PW off the Vault, and we’re just trying to keep it modern/interesting. Ours was built by Craig Sutter (DM Ren) in the Vanilla NWN of 2001/2002. So some of the script is klunky. It was built to run on 2 crappy old Linux laptops with RAM in the 250-500 mb range. So mine is not doing a lot of the things it could (or as Shadooow helped me fix in another thread, things it SHOULD), but it’s amazing for what it was.

@TheBarbarian - Below is a consolidated script fromwhat you gave me today with my list of ResRefs:


// Destroys all items tagged sTag possessed by oHasItem, and gives oGetsReward
// the XP and gold rewards times the amount of items found.
void RedeemBountyItemType(string sTag, int nGoldReward, int nXPReward, object oHasItem, object oGetsReward)
{
    int nBounty = DestroyItemsInInventory(oHasItem, sTag, 9999);
    GiveGoldToCreature(oGetsReward, nGoldReward * nBounty);
    GiveXPToCreature(oGetsReward, nXPReward * nBounty);
}

// Redeems bounty items. Lists tags and XP/Gold rewards of redeemable
// bounty items; add new bounty items here.
void RedeemAllBountyItems(object oHasItem, object oGetsReward);
void RedeemAllBountyItems(object oHasItem, object oGetsReward)
{
    RedeemBountyItemType("ArustethHead", 75, 75, oHasItem, oGetsReward);
    RedeemBountyItemType("BeholderEyeStalk",    75, 75, oHasItem, oGetsReward);
    RedeemBountyItemType("BisonMeat",    40, 40, oHasItem, oGetsReward);
    RedeemBountyItemType("BisonSkin",    100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("BisonTongue",    60, 60, oHasItem, oGetsReward);
    RedeemBountyItemType("BoarSkin",    25, 25, oHasItem, oGetsReward);
    RedeemBountyItemType("1Shortbow",    100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("CarasinHobgoblinEar",    75, 75, oHasItem, oGetsReward);
    RedeemBountyItemType("1Crossbow",    100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("tr_forrestboarskin",    25, 25, oHasItem, oGetsReward);
    RedeemBountyItemType("FireGiantHead",    100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("FriaknarOrcEar",    100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("FrostGiantHead",    150, 150, oHasItem, oGetsReward);
    RedeemBountyItemType("GatorSkin",    100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("GlacierBearSkin",    250, 250, oHasItem, oGetsReward);
    RedeemBountyItemType("GlacierBearSkin2",    100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("GoblinEar",    50, 50, oHasItem, oGetsReward);
    RedeemBountyItemType("HrendyrHide",    100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("KoboldEar",    50, 50, oHasItem, oGetsReward);
    RedeemBountyItemType("Sharpzombiebone",    50, 50, oHasItem, oGetsReward);
    RedeemBountyItemType("SpiderHeart",    60, 60, oHasItem, oGetsReward);
    RedeemBountyItemType("SpineGiantHand",    200, 200, oHasItem, oGetsReward);
    RedeemBountyItemType("ViperSkin",    150, 150, oHasItem, oGetsReward);
    RedeemBountyItemType("Toweraxe",    100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("WildlingScalp",    50, 50, oHasItem, oGetsReward);
    RedeemBountyItemType("WorgsTail",    50, 50, oHasItem, oGetsReward);
}

void main()
{
    object oPC = GetPCSpeaker();

    // Redeem bounty items on the PC speaker.
    RedeemAllBountyItems(oPC, oPC);

    // Start looking through the faction members.
    object oTarget = GetFirstFactionMember(oPC);
    // As long as a valid faction member continues to be found...
    while (oTarget != OBJECT_INVALID)
        {
        // Only target faction members who are henchmen whose master is the PC speaker.
        if (GetAssociateType(oTarget) == ASSOCIATE_TYPE_HENCHMAN && GetMaster(oTarget) == oPC)
            {
            // Redeem bounty items on the henchman.
            RedeemAllBountyItems(oTarget, oPC);
            }

        // Check the next faction member.
        oTarget = GetNextFactionMember(oPC);
        }
    }

VERY much obliged to you for your assistance here.

1 Like

De nada. :slight_smile: Test it, though! I write crap sometimes.

Sidenote: ResRefs and tags are different things; see under the “General” tab when you open the item properties panel for the blueprint. Either can be used to identify something, but the ResRef is unique to the blueprint, while many different items can have the same tag.

Lined up the spaces.
// Redeems bounty items. Lists tags and XP/Gold rewards of redeemable
// bounty items; add new bounty items here.
void RedeemAllBountyItems(object oHasItem, object oGetsReward);
void RedeemAllBountyItems(object oHasItem, object oGetsReward)
{
    RedeemBountyItemType("ArustethHead",        75,  75,  oHasItem, oGetsReward);
    RedeemBountyItemType("BeholderEyeStalk",    75,  75,  oHasItem, oGetsReward);
    RedeemBountyItemType("BisonMeat",           40,  40,  oHasItem, oGetsReward);
    RedeemBountyItemType("BisonSkin",           100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("BisonTongue",         60,  60,  oHasItem, oGetsReward);
    RedeemBountyItemType("BoarSkin",            25,  25,  oHasItem, oGetsReward);
    RedeemBountyItemType("1Shortbow",           100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("CarasinHobgoblinEar", 75,  75,  oHasItem, oGetsReward);
    RedeemBountyItemType("1Crossbow",           100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("tr_forrestboarskin",  25,  25,  oHasItem, oGetsReward);
    RedeemBountyItemType("FireGiantHead",       100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("FriaknarOrcEar",      100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("FrostGiantHead",      150, 150, oHasItem, oGetsReward);
    RedeemBountyItemType("GatorSkin",           100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("GlacierBearSkin",     250, 250, oHasItem, oGetsReward);
    RedeemBountyItemType("GlacierBearSkin2",    100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("GoblinEar",           50,  50,  oHasItem, oGetsReward);
    RedeemBountyItemType("HrendyrHide",         100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("KoboldEar",           50,  50,  oHasItem, oGetsReward);
    RedeemBountyItemType("Sharpzombiebone",     50,  50,  oHasItem, oGetsReward);
    RedeemBountyItemType("SpiderHeart",         60,  60,  oHasItem, oGetsReward);
    RedeemBountyItemType("SpineGiantHand",      200, 200, oHasItem, oGetsReward);
    RedeemBountyItemType("ViperSkin",           150, 150, oHasItem, oGetsReward);
    RedeemBountyItemType("Toweraxe",            100, 100, oHasItem, oGetsReward);
    RedeemBountyItemType("WildlingScalp",       50,  50,  oHasItem, oGetsReward);
    RedeemBountyItemType("WorgsTail",           50,  50,  oHasItem, oGetsReward);
}

Ok,
I fired it up single player, Debug, and hired me a Henchman. Gave myself some items and went to the fella with the script.

He took all the PCs items perfectly. Left the Hench’s where they were. Am I missing a #include “x0_i0_henchman”? Do I need to establish a master for it to recognize that he’s a henchman? I have no idea…

Ok then you should modify baseitems.2da and put that modified file into your top hak on server.

Include files exist to make functions available to use in other scripts. For instance, if you put the functions RedeemBountyItemType() and RedeemAllBountyItems() into a separate script file, without a void main in it, and saved it, you could then #include that script at the top of another, and call the functions from there.

Hold up, I’mma set up a test scenario and come debug.

Also, tip: To find out where something’s going wrong, place debug messages. For instance, you can put a SendMessageToPC(oPC, “Checking “+GetName(oTarget)+”.”); into the faction member cycling:

    // As long as a valid faction member continues to be found...
    while (oTarget != OBJECT_INVALID)
        {
        SendMessageToPC(oPC, "Checking "+GetName(oTarget)+".");

        // Only target faction members who are henchmen whose master is the PC speaker.
        if (GetAssociateType(oTarget) == ASSOCIATE_TYPE_HENCHMAN && GetMaster(oTarget) == oPC)
            {
            SendMessageToPC(oPC, "This object is a qualifying henchman.");

            // Redeem bounty items on the henchman.
            RedeemAllBountyItems(oTarget, oPC);
            }

        // Check the next faction member.
        oTarget = GetNextFactionMember(oPC);
        }

Once you run that, you know whether it’s failing to find the henchman as a faction member, or whether it’s the GetAssociateType/GetMaster check that’s the problem.

1 Like