Making a henchman start conversation

Here’s what I’m trying to do: Have a henchman join the PC, then assign them a task of speaking to every person in the area, in any order. Once this is done, the henchman will start a conversation with the PC to move the story along. This henchman conversation should only happen once.

I tried doing this by defining an integer that gets a +1 from each NPC upon conversing with them, and then adding a script to the henchman’s heartbeat, which checks for this integer to be the exact number of people the PC needs to speak with.

Unfortunately, nothing happens in the game, no matter how much I adjust the code (before the henchman would start the conversation multiple times, now they don’t start it at all). I tried including a DoOnce script but it didn’t help, and I tried all kinds of other things (Area & Module heartbeats even) but I’m at a complete loss.

Here’s part of the henchman heartbeat script:

object oTarget;
object oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN);
int nInt;
nInt = GetLocalInt(oPC, “folkcount”);

if (GetLocalInt(oPC, “folkcount”)== 2)
{
if (GetLocalString(OBJECT_SELF, “Done”) == “TRUE”) return;
else
{
SetLocalString(OBJECT_SELF, “Done”, “TRUE”);
ActionStartConversation(oPC, “c_brianwolf”);
nInt += 1;
SetLocalInt(oPC, “folkcount”, nInt);
}
}

Edit: Quick notes, both the “Done” string and the +1 to the “folkcount” integer are my attempts at making this conversation happen once. The string is self-explanatory, and for the integer, I was hoping by making it +1 (so it’s 3 post the script), the heartbeat will check for 2, find it’s 3, and ignore the script. But like I said, absolutely nothing happens in game once folkcount reaches 2.

Have you checked the scripting FAQs? I am sure this has been done before.

TR

Yeah I’ve looked. I’ve done all kinds of searches through the vault, tutorials, google. There are plenty tutorials on how to make NPCs start conversations with the PC, or henchman trigger interjections, but none of these are helpful for this. Either I’m blind as a bat or this hasn’t been outlined anywhere.

Few remarks:

  1. Instead of GetNearestCreature just use GetMaster. You want to converse with henchman’s master, not closest PC (who may happen to be someone else). In worst case, it’s a code easier to maintain.
  2. Are you sure you don’t increase folkcount over 2? I.e. speaking to same NPC twice.
  3. You are probably introducing a race condition here. You increase folkcount / set "Done" in the heartbeat script - better do it in conversation. Otherwise you risk that something may interrupt henchman’s action queue before they can begin the conversation. And since you already increased folkcount, this prevents the conversation-starting code to run.

EDIT: place your code inside the [code] tag for monospace font and whitespace formatting.

@NWShacker’s right.

You may also need to SetIsBusy to prevent the henchman AI from cancelling the conversation action. Clear the busy status at the end of conversation.

Not sure why folkcount is incremented by the henchman, but if that’s important it needs to happen in conversation.

Thanks, @NWShacker . These tips helped, I put the string and integer scripts in the conversation and it’s finally working. Unfortunately it takes a little longer than a heartbeat for the henchman to start the conversation (about 15 seconds) but I can live with that. And thank you @Proleric for the great tip!

Simple IsInConversation should be enough.

Not sure why it should take longer than the usual 6. You’ve got it on your end, but since I was about to post a complete solution, I’ll post it anyway.

In henchman’s hearbeat event handler (UNTESTED):

object oPC = GetMaster(OBJECT_SELF);

if(!GetIsObjectValid(oPC))
{
    return;
}

if(GetLocalInt(OBJECT_SELF, "Done") == 0 &&
    GetLocalInt(oPC, "folkcount") >= 2 && // could also be ==
    !IsInConversation(OBJECT_SELF))
    {
        SpeakString("I want to talk to you", TALKVOLUME_TALK); // debug
        ActionStartConversation(oPC, "c_brianwolf");
    }
}

Somewhere in the conversation (may be at the end):

SetLocalInt(OBJECT_SELF, "Done", TRUE)
1 Like

Done is a string so == 0 doesn’t do anything, and the conversation would loop endlessly. I’ll try it out with an adjustment - thank you either way because I’ve been trying to figure out how to do “and if” for ages.

1 Like

Naturally you can adjust that code to your liking. I used int because such use of comparison between string variable and “TRUE” literal - while valid - is in my opinion superfluous.

You can even drop Done altogether and use DeleteLocalInt(oPC, "folkcount") in conversation for the same effect. Of course, this assumes that NPCs cannot be talked to again to raise this value back up and that you won’t be needing it in the future.

1 Like

Adding mention of another route: If this is only supposed to happen once throughout the entire module, it might make sense to trigger the whole thing via scripts tied to the NPC conversations themselves. Anything you put in the heartbeat, your henchman is going to be doing over and over again, even once it’s no longer needed. It’s not gonna be a problem in this case, and NWShacker’s variant sensibly aborts if the henchman doesn’t have a master and thus doesn’t do any of the other stuff anymore anyway, but making a habit of thinking like that (“How do I set this up in a way that leaves the least amount of unnecessary background activity going on?”) pays off when projects get bigger. Watch out for this stuff if working with placeable and area heartbeats down the road. Multitudes of those running simultaneously can make even singleplayer modules laggy.

I’mma toss some links to Tarot’s basics primers in here, in no particular order:
https://neverwintervault.org/project/nwn1/other/trs-basics-decisions
https://neverwintervault.org/project/nwn1/other/trs-basics-variables-types-and-functions-tbc
https://neverwintervault.org/project/nwn1/other/trs-basics-mostly-operators
https://neverwintervault.org/project/nwn1/other/trs-basics-boolean-algebra
^- They save a whole lot of time on trial-and-error learning.

Lexicon pages, too:
https://nwnlexicon.com/index.php?title=Category:Operators
https://nwnlexicon.com/index.php?title=Category:Primers
https://nwnlexicon.com/index.php?title=Category:Functions

Also upping the emphasis on local ints versus local strings - if you’re setting TRUE/FALSE flags, go with integers instead of strings. If nothing else, they don’t carry the same risk of a typo mucking up your checks. And, you don’t have to go to the trouble of tacking on a == 0 or == TRUE when checking whether an integer is TRUE or FALSE, which improves readability, since the absence of the symbols when they’re not needed makes checks for a specific value immediately stand out in the code. This’ll work just the same:

// If nInt is TRUE
if (nInt)
    {
    SpeakString("nInt is anything other than zero.");
    }

// If nInt is FALSE
if (!nInt)
    {
    SpeakString("nInt is zero.");
    }
1 Like

It would be nice if NWScript recognized conventional Boolean naming. I can’t count how many times I’ve used AND instead of &&, OR instead of ||, or NOT instead of ! without even thinking about it.