<%@ LANGUAGE="JScript" %> Using NWN Conversations

Using NWN Conversations

A Statement to Save my Butt When I'm Wrong.

The explanation you are about to read contains many statements about how the game engine turns the conversations you create in the toolset's conversation editor into the interactive window you are familiar with from playing NWN. Since I am not a Bioware programmer and have never ever seen the code for the game engine, I can't possibly know exactly how it works -- I can only make speculations based on my experience as a programmer. Therefore this explanation is likely to be not precisely accurate, however it is geared towards giving those who know very little about it a way to view the game engine's operation as it relates to conversations that will facilitate better understanding of how to go about creating a conversation intended to work in a particular way. If you assume my explanation is correct, you will better understand how to create and modify conversations that do what you want them to. Sorry about the length of this page I tend to bloviate sometimes -- but what do you expect? I ain't no English professor I'm an engineer.

Using the Conversation Editor.

I won't spend much time here because there are other places to learn about all the bells and whistles in the conversation editor, but I will point out some of the basic things you must know when creating your conversations. A conversation is structured like a tree similar to the folder tree you see when you open a disk window in Windows Explorer. Every branch of this tree has one or more lines of conversation each of which can optionally have a sub-tree of other conversation lines underneath it. The branches of the conversation are color-coded to indicate where in the little window that appears during play the text will appear. I will refer to this little window as the dialog window from now on. The red branches are spoken by the conversation's "Owner" (who can be a PC, NPC, placeable, trigger, encounter, or door) and the text appears in the upper-right portion of the dialog window during play. The blue lines are spoken by a PC (always a PC) and appear in the lower part of the dialog window during play. Note that although you can have more than one red line at the same branch level of the conversation, only one of them will be displayed in the dialog window during play (there is only room for one in the window). Blue lines on the same conversation branch level, on the other hand, can all appear in the dialog window at the same time (as a list of options you can click on). This is a very important thing to understand about conversations, and keeping a picture of the dialog window in your mind while writing conversations will help you to realize this. You'll also see this more clearly when we get to the scripting bits later on.

Note also that when two or more NPCs are having a conversation with no PC involved, they will all use red lines only and the conversation will contain no blue lines at all. If a PC does get involved in such a conversation (one with no blue lines at all), a default blue [continue] will show up between each line spoken by an NPC during play. Actually this is will happen in any conversation that has a branch with no blue lines in between two defined red branches (i.e. red - empty blue underneath - red under that) and a PC is involved in the conversation. If you start up a conversation between two NPCs with no PC involved and the conversation has blue lines in it, they will be ignored (I believe) and not show up. One last note about NPC conversations with no PC involved, the dialog window does not display when these conversations run, all you see is the text in the speech window during play and the floating text they speak appear above their heads (just as you would if you walked by some PC having a conversation with an NPC not involving you).

On each line of the conversation, you can also set a TAG in the Speaker Tag field located kind of on the lower left of the conversation editor screen. If you put the TAG of an object into that field on a red line, that object's portrait and name will be used when that line of the conversation is displayed in the dialog window during play. For blue lines only the name is shown. The object whose TAG you put into that field must be alive and in the area where the conversation takes place during play or the line will not be displayed and the conversation might shut down prematurely. If you leave the Speaker Tag empty, the game will use the Owner's portrait and name for red lines and the PC's name for the blue lines. Because it works this way for blank Speaker Tag lines, when you have a conversation with more than one NPC, you must use the Speaker Tag field to make the conversation appear as though the other NPC is talking -- otherwise it will look like the Owner is conversing with himself. There are also a few buttons there you can click to change the color of text or do skill checks or have the PCs name or gender appear and the like -- I'll let you play with those on your own.

On the lower right portion of the conversation editor screen is another area where you can customize various aspects of the conversation line. There are five tabs across the top that bring up different little panels to enter stuff in. You can use the OtherActions panel for example to make sounds happen, have the speaker of the line do an animation, or have a journal entry placed into the player's journal. If you click the "<" and ">" buttons on the far right you'll be able to scroll thru them all. The three tabs labelled ActionTaken, TextAppearsWhen, and CurrentFile are all related to scripting. The scripts you enter into these panels control the behavior of the conversation as well as what happens when a PC clicks one of those blue lines during play.

Not every line you enter into a conversation will be displayed in the dialog window during play. For example, if you have 2 red lines on the same level, they can't both be displayed because of the way the dialog window is laid out. A choice will have to be made to determine which one should be shown. On a blue line branch, not every option listed need be displayed. If you want one or more left out you can do that. The TextAppearsWhen script is used to control whether or not the line of conversation will even get displayed at all. The ActionTaken script is the one that controls what happens when a red line is shown in the dialog window (before any associated blue lines under it are shown), or when the PC clicks on a blue line during play. The last tab, called CurrentFile, has two script slots. One is for what happens when the conversation ends normally. This happens when the PC clicks a blue line that says [EndDialog] at the end of it. All blue and red lines will have that put on them automatically if there are no branches underneath the line. The conversation also ends normally when a red line with no sub-tree underneath is spoken by the Owner. If a player presses the ESC key or walks away from the Owner during the conversation, it will be aborted and the script in the Aborted slot of the CurrentFile panel will determine what gets done at that point. None of these scripts are required all are optional.

If you don't already know how to make a conversation choice link to another branch, right click on the red line that you want some blue line to link to and choose "Copy" from the menu that appears. Then right click on the blue line and choose "paste-link". I think you can also drag and drop the lines to do it but I'm not sure as I always use the right-click method.

Lemme give you an example...Suppose you have a convo like this:
Hello, can I help you?
--Yes I'd like to know about blah blah.
----Well, yakkity yak yak yak
------I see, thanks for the info[End Dialog].
------Ok so long.[End Dialog]
--No I need no help.[End Dialog]


Now you want it so if they click on the line that says "I see, thanks for the info." the conversation will loop back and start over from where the guy says "Hello, can I help you?"
To do that you right-click on the red line "Hello, ..." and choose copy. Then right-click on the blue line "I see, ..." and choose Paste-Link. It will add a greyed out red line below the "I see, ..." line that indicates it's linked to the "Hello, ..." red line at the top. Also the [EndDialog] bit will be removed from the end of the blue line because clicking it will no longer end the conversation.
Hello, can I help you?
--Yes I'd like to know about blah blah.
----Well, yakkity yak yak yak
------I see, thanks for the info.
--------Hello, can I help you?
------Ok so long.[End Dialog]
--No I need no help.[End Dialog]

Different Types of Conversations.

Because of the flexibility the conversation editor brings to the table there can be a myriad of different types of conversations you can create and use. I have categorized them into three types but I'm sure others can come up with ones I haven't thought of. My three categories are:  One-Liner conversations, NPC-Only conversations, and PC conversations. Of these, the One-Liner ones are the easiest to do. They make the Owner say one line then the conversation ends -- boom, done. NPC-Only conversations are ones where no PC is involved at all. I have never made one of these so I can't and won't discuss them except to say look at the section above where I spoke a little about it and the bit below where I show how they should look -- but that stuff I say might all be lies concerning this type of convo. The PC conversations are the ones most frequently used and are typically the most complex to make because they will no doubt involve scripts. They don't always use scripts, but unless its just a guy you talk to in order to get directions or descriptions or story line information, you will need to include scripts to make things happen.

The following are examples of what each category of  conversation will typically look like in the editor screen.

One-Liners:
    Red Line - nothing underneath any of these red lines, only the top level branch is used.
    Red Line
    ...
    Red Line

NPC-Only:
    Red Line
   
|-  blank-no blue lines
   
|-    |- Red Line
   
|      |    |- blank-no blue lines
   
|-    |  ...
    Red Line
   
|-  blank-no blue lines
       
|-  Red Line
               
...

PC:
    Red Line
   
|-  Blue Line
   
|-  Blue Line
   
|-  ...
   
|- Blue Line
   
|   |-  Red Line
   
|   |    |- Blue Line
   
|   |    |- Blue Line
   
|        |- ...
   
|   |    |- Blue Line
   
|   |-  Red Line
   
|   |   ...
    Red Line
   
|-  Blue Line
   
|-  Blue Line
   
...
   
|- Blue Line
   
|   |-  Red Line
   
|   |    |- Blue Line
   
|   |    |- Blue Line
   
|        |- ...
   
|   |    |- Blue Line
   
|   |-  Red Line
   
|   |   ...
   
|-  ...

In One-Liners the top level red branch is the only one where lines are entered. In NPC-Only, there can be multiple levels in each branch, but no lines are entered in any of the blue levels, and the Speaker Tag field will almost certainly need to be used on at least half if not more of the red lines to indicate which NPC is speaking.. In the PC conversations, anything is possible structure wise, but there is almost always at least one blue line on each blue level. I don't think it is possible to create a conversation with a blank red level that has stuff underneath it.

How the Game Engine Runs the Conversation.

Whenever a conversation is started, the game engine opens up the conversation file and starts looking for a red line on the top level branch to display. It starts at the top of the conversation branch and works its way downward. At each red line it encounters, it calls the TextAppearsWhen script to determine if that line should be displayed this time. If the script returns a TRUE value, the line is chosen to be displayed. If the script returns FALSE, the line is skipped and the next red line below it on the same conversation branch level is checked. If there is no TextAppearsWhen script entered for the line, the engine assumes there is one there that always returns TRUE (so the default behavior is to always display the line). If it makes it all the way thru all the red lines without finding one to display, the conversation simply ends without showing the dialog window at all. When processing lower level red branches, the dialog window will be open so in this case the conversation is ended normally and the dialog window is closed.

When the game engine finds a line that returns TRUE, it checks the Speaker Tag field and displays the appropriate portrait and name in the dialog window then inserts the text contents from the line of the conversation into the upper right portion of the dialog window and makes the speech show up in the speech pane. Then it calls the ActionTaken script for the line if one is specified. That script can do just about anything you want, it doesn't affect the conversation processing unless you make the participants die or transport away or stop talking etc -- though it is often used (at least on the red lines) to set variables that control the behavior of deeper branches of the conversation. Notice that the ActionTaken of a red line will only get run if that red line is the one chosen to be displayed. The engine completely ignores all the rest of the red lines on that branch that come after the one found. None of their scripts run and the only way one of those lines will ever be shown during that particular instance of the conversation is if there is a link to them from some lower blue line. I have no idea what happens if you have a blue line link to a red line and that red line's TextAppearsWhen script returns FALSE. I suspect the conversation ends normally if that happens but I don't know for sure, it may continue downward on the same level from that red line looking for others to display if they exist.

The next step for the engine is to check the blue-level sub-branch immediately under the red line that was just displayed. It follows the same procedure starting from the top, calling TextAppearsWhen which works exactly the same way for blue lines, until it finds one to display. When it does, it spits out the line in the lower part of the dialog window. Unlike on the red levels, all the blue lines are tested. The engine does not stop when it finds one like on the red levels because more than one blue line is allowed to appear at the same time. If it goes all the way thru all the blue lines and doesn't find one to display, the conversation will end and the dialog window will close -- unless there is another red level defined below the current blue level. In that case, the engine inserts a [continue] blue line in the conversation which when clicked, will send the conversation to the next lower red level (not completely positive that this is what actually happens either). Also unlike on the red lines, the ActionTaken script is not called automatically when the line is displayed. It isn't called until the PC actually clicks the line with his mouse during play. On a red line the ActionTaken script is called just after the red line's text is displayed in the dialog window but before the blue lines at the next level down underneath it are processed and displayed by the game engine. Another difference between red and blue levels is that the speech won't appear in the speech pane unless and until the PC clicks a blue line. For red lines the speech appears at the same time in the speech pane that it does in the dialog window.

When the player clicks on a blue line and there is a red level branch underneath it (or it is linked to some red line elsewhere in the convo), the game engine starts over from the very beginning as if the red level was the top level branch. If the blue line clicked has an [EndDialog] at the end of it, the conversation ends and the dialog window closes (after the ActionTaken script for that line has finished of course). The game engine repeats this process over and over until the conversation ends normally or is aborted via the PC or a script command.

When a conversation ends, the game engine determines why it ended. If it ended because the conversation expired (red line spoken with no other lines underneath or vice-versa), the game engine calls the script specified in the Normal box of the CurrentFile tab. If it ended because a script caused it to abort (like killing the PC or Owner, calling ClearAllActions on a participant, etc) or because the PC either pressed the ESC key or simply left the vicinity or logged out, the game engine calls the script specified in the Abort box of the CurrentFile tab instead. This final script call ends the game engine's dealings with the conversation until the next time it gets started, and all participants are marked as no longer being involved in a conversation.

Put Some Gravy on That Beef

If the text entered is the meat of a conversation, the scripts are the gravy. They bring the conversation to life by controlling how and under what conditions the various conversation branches and entries are shown and navigated and/or cause actions to take place in the game based on the PCs selected responses and/or the Owner's prompts. Let's look at some script techniques for conversations starting with a simple One-Liner.

For this example we're gonna have a conversation for an NPC where he says hello to everyone he's met before, but ignores those he doesn't know. Here is his conversation -- one red line on the top branch and that's it:
    Hello my old friend it is good seeing you again.

As you may have already figured out, we need a TextAppearsWhen script that will return TRUE if the PC is known and FALSE otherwise. To accomplish this, we will be using a local integer flag which is set to TRUE on the NPC if he knows the PC and won't exist if he doesn't. To distinguish between different PCs, the name of this variable will include some part of the PC (his name or CD key etc) that is unique to him and can be easily determined. So the NPC will have a differently named variable stored on him for each player he meets. The TextAppearsWhen script of our conversation line will simply look for the variable and if found, return TRUE -- if not return FALSE. It will also set the PC's variable flag on the NPC to indicate they have met so that the next time the PC runs the conversation, the NPC will recognize him and say the line. Here is the script:

// TextAppearsWhen script
//::///////////////////////////////////
  
int StartingConditional()
{ // This gives us a variable to use in the script that points to the PC that the NPC is talking to.
   object oPC = GetPCSpeaker();
  
   // This creates the variable name based on this PC's name
   // For simplicity we will assume no two PCs have the same name.

   string sVariableName = "I_Have_Met_" +GetName( oPC);
  
   // This gets the value of this PC's variable flag from off the NPC.
   // When we set the variable we will always set it to TRUE. If it doesn't exist the
   // GetLocalInt function will return FALSE.
  
int bNPCKnowsPC = GetLocalInt( OBJECT_SELF, sVariableName);
  
   // Next we set up the variable for this PC on the NPC so that next time he talks
   // to the NPC, he will be known. Note that variables used in this manner in
   // TextAppearsWhen scripts are typically not setup inside this script but are
   // initialized elsewhere in the module (for example, to indicate a quest was
   // finished a variable is set up on the PC at the time he completes the quest
   // objectives). For this example however it works out quite well to do it right here.
  
SetLocalInt( OBJECT_SELF, sVariableName, TRUE);
  
   // Now return the originally value of the flag
   return bNPCKnowsPC;
}

Ok now that the conversation is finished, it gets hooked up to the NPC's conversation field in his properties dialog, and its off to test it out. What happens is the first time you click on the NPC, nothing happens, he says nothing at all and it appears as though you're click didn't work. Click him again and now he says "Hello old friend its good seeing you again." Kinda lame...so lets enhance it a little bit so at least he will always say something to you the first time you meet him. Let's add another line to the conversation for that:
    Good day stranger it's damn nice to meet ya!
    Hello my old friend it is good seeing you again.

Notice that I put the line in front of the other line. This is incorrect but will illustrate an absolutely critical fact that many many noobies overlook. We aren't going to put any scripts on this new line right now. Since there is no TextAppearsWhen script, our new line will always appear which is exactly what we want to happen when a new PC walks up to converse. Save it and test again though and guess what? I doesn't work anymore. The first time we talk to him he says "Good day stranger..." which is great. But when we click again, he still says the same thing. If you go back over the section on How The Game Engine Runs Conversations above, you should be able to figure out why. Since the engine starts at the top of the branch and the first line's TextAppearsWhen (TAW) script is blank (thus returning TRUE), the engine makes him say the first line then ignores the second one. The second line's TAW never gets run so the variable flag never gets set up on the NPC. Of course it wouldn't matter if the variable had been set up correctly because that second line is never ever tested at all. The first line overrides it. In fact it will override any lines we put into this level below it.

Now we can fix this two ways: we can create a TAW for the first line that works exactly backwards to the second line's TAW, or we can do a smart thing and move our new line down below the second one. Why is this the smart thing to do? Well because our new line is a default line that should always be spoken whenever the other line isn't, by moving it down to the end and leaving it with no TAW script it makes it so the NPC will always say the default line when no other line above it (all of which should always have a TAW by the way) meets the conditions to be displayed. An added advantage to doing this is that we don't have to create another TAW script at all, and it's always nice to not have to write another script.

Many people find it counter-intuitive to put the default thing to be said at the bottom, and noobies will invariably organize their conversations from top to bottom by the chronological time they expect things to happen. For example, they put the default thing on top that describes a quest. Then the next line is what happens after the first quest is done and gives him a second quest. After that is the third quest, and so on. But if you write your conversations like that, you will often run into this problem with previous things overriding subsequent things. You can always write your TAW scripts so each line is mutually exclusive of all the others to fix this, but why write scripts you don't have to when all you really need to do is re-order them in the branch. This is why it is always smart to build your conversations in reverse chronological order from top to bottom. The last line should be the default one and by having no TAW on it, it will always appear by default when no other lines meet their display criteria. Of course if your conversation has no default thing to say you will have to write a TAW for every line, but it is rarely the case that you want a conversation to work that way. Usually you want the guy to say something even if it is just some asinine greeting.

So for our example, if we just drag the new line down below the other one and try again...bang success!

Ok now let's look at a more complex version of our NPC that uses a full blown PC type conversation instead of just a One-Liner. He will behave the same way, but we're going to add a little spice to the convo by allowing the PC to decide if he wants to be known by the NPC. It will also provide an opportunity to see how the ActionTaken scripts come into play. Our new conversation will look like this:
    Hello my old friend it is good seeing you again.
        Thank you, I always enjoy talking with you as well.[EndDialog]
    Good day stranger it's damn nice to meet ya!
        Indeed, it is my pleasure sire.[EndDialog]
        Buzz off wierdo.[EndDialog]

The TAW scripts will be set up exactly the same as before, just that one on the first red line, and none of the blue lines will need them because we never want to suppress any of them. We will make a minor modification to the TAW to remove the setting of the variable flag however:

// TextAppearsWhen script
//::///////////////////////////////////
  
int StartingConditional()
{ // This gives us a variable to use in the script that points to the PC that the NPC is talking to.
   object oPC = GetPCSpeaker();
  
   // This creates the variable name based on this PC's name
   // For simplicity we will assume no two PCs have the same name.

   string sVariableName = "I_Have_Met_" +GetName( oPC);
  
   // This gets the value of this PC's variable flag from off the NPC.
   // When we set the variable we will always set it to TRUE. If it doesn't exist the
   // GetLocalInt function will return FALSE.
  
int bNPCKnowsPC = GetLocalInt( OBJECT_SELF, sVariableName);
  
   // Now return the value of the flag
   return bNPCKnowsPC;
}

The convo will work like this: you talk to the guy and he gives you the "Hello stranger..." line but this time he spits out two choices you can click on to answer him. If you click "Indeed it is my pleasure..." then the conversation will mark you as known by setting your variable flag on the NPC. If you instead click the "Buzz off..." line, the NPC will not be set up with the flag, and therefore will not recognize you next time around. As long as you click the "Buzz off..." line, he will continue to greet you with the "Hello stranger..." greeting. Once you click the "Indeed it is my pleasure...", he will greet you with the "Hello my old friend..." greeting from that point on.

What we need to do for this one is create an ActionTaken (AT) script for the "Indeed..." blue line that causes the PC's variable flag to get set on the NPC. This script will only get called when the "Indeed..." line is clicked on since it is a blue line. None of the other lines will need an AT script because there are no actions that need to be taken when those other lines are displayed (red) or clicked (blue). Here's what the script looks like:

// ActionTaken script
//::///////////////////////////
  
void main()
{ // This gives us a variable to use in the script that points to the PC that the NPC is talking to.
   object oPC = GetPCSpeaker();
  
   // This creates the variable name based on this PC's name
   // For simplicity we will assume no two PCs have the same name.

   string sVariableName = "I_Have_Met_" +GetName( oPC);
  
   // This sets up the variable for this PC on the NPC so that next time he talks
   // to the NPC, he will be known.
  
SetLocalInt( OBJECT_SELF, sVariableName, TRUE);
}

Hopefully those two examples have given you a fairly solid understanding of how these conversation things are created and where you need to put things to make them work properly. As a final example, I'll show you a way to make a town crier NPC that randomly chooses from a list of lines to speak in a One-Liner conversation. Here is the conversation we'll be using:
    Hear ye hear ye, the King has decreed that tomorrow will be a holiday to honor the Queen!
    Come one come all to the grand opening of Cog's Pub on the north side of town!
    Warriors keep a close eye on your wench! A rapist and axe murderer is loose in the castle.
    Notice to all, the new ferry system to the Isle of Silence where there are no conversations allowed begins operation today. Get your tickets early!

Our crier will randomly select one of those four lines and speak it when you click on him to converse (I know you don't think you should have to click him -- hold your water we'll get to that later). How can we get this done? Well, certainly we're going to need some TAW scripts on those lines to make the selected one appear. In this case, there won't be a default line for him because the situation does not call for it so all four lines will have a TAW on them. We are also going to need some way to randomly determine the line every time the conversation runs. Generating a random number from 1 to 4 isn't very hard, but how to make all the TAW scripts know what that number is? One way is to require a local integer value to be stored on the NPC when he randomly selects one of the lines. The TAW scripts can then test that number to determine if it selects the line the TAW is connected to. The TAW of the first line will return TRUE if the value is equal to 1, the second line's TAW returns TRUE if the value is 2, and so on. Here again, since we have no actions to perform in this conversation, there will be no need for any AT scripts.

Ok that's a pretty good plan, the only thing left is to figure out where to put the code that generates the random number and saves it on the NPC. Since the TAW of the first (top) red line of all conversations is always executed in every conversation each and every time the conversation runs, that makes is a perfect spot to perform conversation initialization procedures like generating a random number for use later in the conversation. So in the first line's TAW we will generate the random number, save it on the NPC, and if it's equal to 1 (thus selecting the first line) we return TRUE. If it isn't equal to 1, we return FALSE so the other 3 TAW scripts have a chance to go. Each of those will look at the value of the integer saved on the NPC by the first TAW and if it matches up with their line number, they will return TRUE -- otherwise they return FALSE so the next line will get checked. Here is what the TAW on line 1 will look like:

// TextAppearsWhen script
//::///////////////////////////////////
  
int StartingConditional()
{ // This generates a random number from 1 to 4 identifying which line will be spoken by the NPC this time.
   int iSelectedLine = d4()// we could have also written = Random( 4) +1; to get the same thing.
  
   // This saves the variable on the NPC so that the other TAW scripts can know what got selected this time..
  
SetLocalInt( OBJECT_SELF, "SelectedLine", iSelectedLine);
  
   // Now we return TRUE if the selected line is line 1 (because this TAW is hooked up to line 1).
   // Otherwise return FALSE to give the other lines their shot.
   return (iSelectedLine == 1);
}

All the other three TAW scripts are the same, except they test for a different number. Here's the second line's TAW:

// TextAppearsWhen script
//::///////////////////////////////////
  
int StartingConditional()
{ // This retrieves the variable from the NPC so we can know what number was randomly selected.
   int iSelectedLine = GetLocalInt( OBJECT_SELF, "SelectedLine");
  
   // Now we return TRUE if the selected line is line 2 (because this TAW is hooked up to line 2).
   // Otherwise return FALSE to give the other lines their shot.
   return (iSelectedLine == 2);
}

Lines 3 and 4 TAWs are set up exactly as line 2's TAW except at the bottom they return TRUE only if the number is 3 or 4 respectively. Well that's all there is to it. Now the guy will randomly speak one of the four lines each time you talk to him.

How Does the OnConversation Event Relate to Conversations?

Simple, this is the script that runs when you click on an object that has a conversation name entered in the conversation field of the object's properties dialog. By default the OnConversation script simply starts up the attached conversation with the PC that clicked it. If there is no conversation attached to the object, nothing happens. You can modify this event script to have some additional control over when, how, and if the conversation gets started and who will participate in it. You can also code in additional actions to happen when a conversation is requested via a click on the object. Conversations can be started via scripting from anywhere, but for a PC to start one he must do so by clicking on an object. That's what this event script is for -- to respond to that click with action(s).

Who Is The Owner?

The Owner of a conversation is the object that runs the conversation. The Owner is always the guy (or object) whose portrait and name appear in the dialog window and he always speaks red lines in the conversation. Sometimes you might create a conversation with a second NPC who "appears to" interjects things periodically during the course of the conversation and this second guy's portrait and name are used for those red lines, but the Owner is still the guy you click on to start things going. Usually this is the object that you click on to start conversing which has the conversation name entered in the conversation field of the object's properties dialog. But it doesn't have to be that way. You can, via scripting only, change who runs the conversation. You can even make objects run conversations that aren't hooked up to any object at all (there is no way to hook a conversation up to a PC, I don't think, it must be done via scripting), or start a conversation from an object that has no conversation already attached to it. In the conversation's AT, TAW, and the Abort and Normal End scripts, OBJECT_SELF is set to the Owner of the conversation, and GetPCSpeaker will return the PC that clicked the Owner (or was specified as the other participant in convos started from scripts) to become the PC participant. I have no idea what GetPCSpeaker returns in NPC-Only conversations, I suspect it returns OBJECT_INVALID in that case (this isn't really important since you know the TAGs of the NPCs involved and you can just use GetObjectByTag instead, but you need to be aware in case you're in the habit of grabbing TAWs or ATs written by others for One-Liners or PC conversation types). The Owner can only be a PC, monster, NPC, placeable, trigger, encounter, or door, you can't have conversations with other object types like waypoints, items, stores, sounds, etc. The Owner and the PCSpeaker can be the same object, a technique often used to simulate a dream sequence or the PC thinking to himself or other stuff like that. This is also a handy technique to use when you want to make it seem like you are talking to an object (usually an NPC) that is busy doing some other action at the time that you would prefer not to interrupt (like sitting).

While it may seem that you can use AssignCommand to make a PC be the Owner and a non-PC be the PCSpeaker, whenever a conversation is started with a PC and a non-PC object, the PC is always set to be the PCSpeaker and the non-PC object is always set to be the Owner. This happens regardless of the way you specify them when you use the scripting functions to start up the conversation. The only way a PC can become the Owner of a conversation is when it is started up with another PC or with himself, and there is no way to force a non-PC object to be the PCSpeaker. This makes total sense since NPCs and doors and the like are incapable of deciding which line should be selected from a list of blue lines, and even if they could, they have no mouse to click the line with.

Ways To Start A Conversation

Of course as a player, the only way you can start a conversation is to click an object that has an OnConversation event script defined on it. But as a scripter, you have a couple of other options as to how a conversation gets started. There are two commands in NWScript that allow you to start up a conversation via scripting: BeginConversation, and ActionStartConversation. The first one is not an action and was designed to only be used from the OnConversation script. The second one is an action you place into an object's action queue to be done when all the previous actions in the queue are completed (just like any other action command). And, just like any other action command, if you clear out the object's action queue using ClearAllActions before calling the ActionStartConversation function, then he will start the conversation as soon as the script finishes. Both of these functions require you to specify an object that will be treated by the game engine as the PCSpeaker during the conversation and in the conversation's TAW, AT, and End/Abort scripts. When a script is used to call the BeginConversation or ActionStartConversation command, the Owner of the conversation will always be the object the script is attached to. This is vital to remember so that you don't try to call one of those functions from an area, module, etc. event script of an object that is incapable of running a conversatiion. To change the conversation's Owner, you simply use AssignCommand to wrap the ActionStartConversation call. AssignCommand requires you to specify the object which will run the command -- this object will become the Owner of the conversation. You must use this technique to start a conversation from an object like an area or the module that can't normally start them up. You cannot use the AssignCommand to call the BeginConversation function because it returns a value -- you have to use ActionStartConversation. Yeah I know you can make a wrapper for the BeginConversation function that returns void and use that instead, but all that accomplishes is to basically do the same thing as a ClearAllActions followed by an ActionStartConversation so you might as well just use the action command and save yourself some coding.

One last thing about starting conversations via a script. You needn't use the conversation that is attached to the Owner when you start a conversation using one of the functions discussed above. You can use any conversation you want by simply specifying its name in the function call. There are also a couple of additional parameters to those functions that allow you to specify if the conversation is heard by others nearby or is private, and if the guy should play his greeting sound when the conversation starts or not. See the Lexicon for the syntax of those functions and a rigorous explanation of how they should be called and how they work.

To show an example of starting a conversation via scripting, let's turn back to our town crier NPC. What we really want from a town crier is a guy that will say his lines periodically on his own -- without any PC interaction to initiate him. We could use the heartbeat script or a pseudo-heartbeat function to have him say a line on a timed basis, but that's too simple for me and because it uses a heartbeat, he'll be standing there yellin stuff when there aren't even any PCs around to hear him. He also acts pretty often using the heartbeat method so he will contribute to lag. So instead, I'm gonna write an OnPerception script that will cause him to say his line whenever a PC comes into his perception range and he spots them. In an area busy with PCs, he will be yellin stuff fairly often as the PCs move around, but in an area with few or no PCs (or where the PCs aren't moving around alot), he will quiet down and not take up a lot of CPU cycles.

We will be using the One-Liner conversation we created for him in the first crier example above (the one with four red lines and no blue lines). We might as well leave everything the way it is (TAWs and such) so if a PC does happen to click on him he'll still say a line, but we'll also make him go from the OnPerception event. No additional changes to the conversation will be required for this, all we have to do is get him to start a conversation as if someone clicked on him and everything will work the same as before. Remember that this guy's scripts make no reference to PCSpeaker, so it really does not matter at all which object we specify to handle that role. When you call ActionStartConversation, the crier will run up to the object you specify to be the PCSpeaker and then start talking. We don't really need him to do that it would be more real if he just stood still to make his announcements. So what we will do is make him have the conversation with himself, That way he won't move or turn to face the PCSpeaker (since its him) and it will work and look more realistic. Here is the script:

// Town Crier NPC's OnPerception script.
//:://///////////////////////////////////////////////////
  
void main()
{ // See if we are spotting a new guy
   if( GetLastPerceptionSeen())
  { // Ok a new creature has been perceived. Let's make sure it's a PC we don't want him yelling when NPCs move about.
     if( GetIsPC( GetLastPerceived())
     { // Ok its a PC so start up the conversation so he'll make his announcement. We want to use the conversation that
        // is attached to the guy, so we don't specify a conversation name in the call. Since we want it to be a public conversation
        // heard by everyone in earshot, we tell the game engine to keep it public. Also we don't want him playing his voice
        // greeting each time before yelling his line, so that 4th parameter says to suppress that sound. And we make sure his
        // action queue is empty first so in case he is doing something else (like combat) he'll stop and yell the line.

        ClearAllActions( TRUE);
        ActionStartConversation( OBJECT_SELF, "", FALSE, FALSE);
     }
   }
}

Additional Tutorials, Examples, Links, and Other Information

Clicking on conversation lines can be accomplished using the keyboard as well as the mouse. Perhaps I should have used the term "activated" to describe the action and make my descriptions more accurate...but I didn't.

As soon as I find some related tutorials and links to helpful examples I will insert them here.

Any comments or suggestions to add, correct, enhance, or clarify the explanations on this page are welcome and can be sent to my account in the Bioware forums at http://nwn.bioware.com/forums/ -- the name is Axe Murderer.