Welcome to Planewalker Games! We are the home of The Broken Hourglass, a new CRPG in development for Windows, Macintosh, and Linux computers.
Inside the Engine: Create a Joinable NPC PDF Print

On the fourth Monday of each month, we explore the code underneath The Broken Hourglass, the game environment called "WeiNGINE." This month, we explore the creation of a new party member, and in the process introduce advanced dialogue scripting topics.

A variety of constraints force the developers of a party-based RPG to make certain difficult choices about the number and type of characters available as joinable NPCs. No matter the number (In The Broken Hourglass, we offer nine), some enterprising players will decide that a tenth character would really fit the bill. Adding a character to the game capable of joining the party requires very little in the way of extra preparation-at the most basic level, joining the party requires just a short, simple dialogue script action.

Understanding some of the earlier engine tutorials will come in very handy here-particularly the entries on creature creation and dialogue construction .

We will call our tutorial joinable NPC "Vondo." (It's tradition.) Vondo doesn't have scintillating dialogue-that's the modder's exercise. But we will show the basic concepts of how a joinable NPC is created, including the special dialogues we will want to use to allow him to be disbanded and re-joined to the party.

First, we will need a Vondo creature. Vondo will be a Cella Ilvari, with above-average swordsmanship. We will give him very basic equipment to start with. His CREATURE file, therefore, looks like this:

<<<<<<<< creature/vondo
<xml>
<Template    value="basic-person.CREATURETEMPLATE"/>
<Name    value=~Vondo~/>
<Race    value="cella"/>

<Appearance value="mehero1"/>
<Skin_Color    value="khaki"/>
<Hair_Color    value="SandyBrown"/>
<Major_Color    value="SeaGreen3"/>
<Minor_Color    value="chocolate"/>
<Text_Color     value="chocolate"/>

<Initial_Direction value="southwest"/>
<Sword_Precision bonus="+15"/>
<Starting_Item    value="longsword"/>
<Starting_Item    value="light-leather"/>

<X    value="666"/>
<Y    value="1082"/>
<Area    value="enthall"/>
<Portrait value="vondo_port"/>
<Dialogue    value="vondo"/>
</xml>

Most of the fields here have already been described in previous tutorials. The important addition here is "Portrait." While it is not strictly required, by convention a joinable NPC has a unique portrait, which appears in dialogue mode and in the party bar. Portraits come from PNG or JPEG images with an 11x17 ratio-they are automatically scaled to fit the various locations used on-screen. To define his portrait resource, we first need to place vondo.png in an .image directory anywhere in game modules. (A modder will typically use modules/mymod/mymod.image.) Next, we need to define the portrait resource to point at the correct image, and give the portrait a name.

<<<<<<<< portrait/vondo_port
<xml>
<Portrait value="vondo"/> // This tells the game to look for a "vondo.png" or "vondo.jpg" image resource
<Name value=~Vondo~/> // This gives the portrait a name in the character sheet interface
</xml>

Already, Vondo has all his necessary statistics and equipment, and has a portrait. All that remains is to provide him with some dialogue, including lines which ensure he can join the party, be kicked out, and then join again later.

In our previous dialogue example, Harika's "talk" state had a spoken line of dialogue. Because Vondo is a more complex character, and he may have different things to say when spoken to depending on the situation, his talk state will be a silent state, used as a "clearinghouse" for different possibilities. We want to cover four unique possibilities-a fully-realized NPC could well need more.

1.    The party meets Vondo for the first time.
2.    Vondo is in the party. A player may talk to an NPC while that NPC is in the party to engage in discussion, or to tell that NPC to grab some bench.
3.    The party meets Vondo subsequent times, but Vondo has never been a member of the party. Typically both #1 and #3 have some sort of dialogue in which the NPC and the player discuss the NPC joining the party.
4.    Vondo has been removed from the party. Typically a player will talk to the NPC to ask them to rejoin.

This means that Vondo's talk state will have no spoken lines of dialogue, but will instead have four conditional transitions. WeiNGINE uses a concept called transition weighting to ensure that we follow the correct transition for the correct situation. Transitions can be assigned a numerical weight-any valid floating point number will do, although typically integers around zero are used. Transitions with no explicit weight are considered to be zero. Weights are evaluated in increasing order, so a weight of -1 is checked before a weight of zero, and a weight of zero is checked before a weight of 1.

This is easier to see than it is to explain, so here is what the beginning of Vondo's dialogue looks like:

<<<<<<<< dlg/vondo

begin vondo

  begin talk // The "talk" state is special--it is the state considered by the engine when the PC click-talks on a party member.
  if #-2 true goto initial_meeting once_trans  // This transition has a weight of negative 2, and has a "true" condition on it because "if" is required in order to assign a weight.
  if #-1 _creature_in_player_party _me goto in_party // This transition has a weight of negative 1, so is checked next. It is followed only if Vondo is in the party.
  if !(_dlg_state_visited "vondo" "initialjoin") goto we_meet_again // No weight is given, so the weight is assumed to be zero. We are checking here if Vondo has ever spoken his "initialjoin" state. This transition is followed if he hasn't.
  if (_dlg_state_visited "vondo" "initialjoin") goto out_party // Again, no explicit weight, so it is assumed to be zero. This transition is followed if Vondo has spoken his "initialjoin" state.
  end

The four unique situations we wanted to address are encompassed in the four transitions to this state. Weighting ensures that we get the results we want.

First, the engine looks at the transition with the lowest weight: #-2. It checks the condition (which is "true", meaning it always occurs) and performs a goto to the "initial_meeting" state. At first glance this may seem to be a problem-at the lowest weight, with a condition of "true", won't this transition occur every time we speak to Vondo? No, because we use the "once_trans" directive. The game will only allow us to take the transition to goto initial_meeting once per game. After that, this transition becomes invalid.

Second, the engine looks at the transition with the next highest weight: #-1. This transition checks to see if Vondo is a member of the player's party. (_creature_in_player_party _me returns true when Vondo is in the party. "_me" is a special dialogue-mode function which returns the creature currently speaking.) If he is, we take the transition to the "in_party" state.

Third, the engine looks at the transition with the next highest weight. In this case, the remaining two transitions both have an unspecified, implicit weight of zero, so they are considered last. Because the two conditions are mutually exclusive, it is perfectly "safe" to have both of these transitions share the same weight value-they can never both be true at the same time, so we can be sure that we always get the transition we meant to see. _dlg_state_visited is a function which tracks the number of times a particular dialogue state has been played. Because a nonzero integer is considered the same as a return of "1" or "true" by the engine, we can skip an equality check here and just ask for the number of times to be returned. We will goto the "we_meet_again" state if Vondo has never played his "initialjoin" state (and thus has never been in the party), or the "out_party" state if Vondo has been in the party at least once in his career.

Notice that Vondo hasn't actually said anything yet. We have to provide the rest of his initial meeting states in order to see what he says.

(continuing vondo.dlg)
begin initial_meeting // We goto this state if we have never spoken to Vondo before.
say ~"I'm Vondo. I would love to get out of this place, but there's just nowhere to go. Got any ideas?"~
reply ~"Come with <pro_meus/>. You look like you would be a welcome addition to our party."~ goto initialjoin // <pro_meus/> is a special text replacement. "me" is printed if the PC is alone, "us" is printed if two or more characters are in the party.
reply ~"Why don't you just get a job?"~ goto getajob
end

begin getajob
say ~"I would, but they keep telling me they have no opportunities for astonishingly handsome elves. Just my luck, isn't it?"~ =
~"Come see me again if you have any other ideas."~
exit
end

begin initialjoin
say ~"Great. I was starting to feel like part of the furniture. Lead on, <charname/>."~
do { _join _me ; // Vondo joins the party
_add_journal ~vondojoin~ ~Building the Party~ ~Meeting Vondo~ ~I have met Vondo and asked him to join my party. As demo characters go, he seems an adequate enough addition to my group.~ ; // We create a player journal entry commemorating this momentous event. Its internal name is "vondojoin", it is part of the "Building the Party" quest, with a heading of "Meeting Vondo".
_complete_journal ~vondojoin~ } // We set the new journal entry to be a "completed" journal item, so that it does not appear to be an open quest entry.
exit
end
    
begin we_meet_again // We goto this state when we spoke to Vondo, but never asked him to join
say ~"Changed your mind? Got anything for me to do?"~
reply ~"Yes, why don't you join <pro_meus/>?"~ goto initialjoin
reply ~"Nope. Can't think of a thing."~ exit
end

begin in_party // We get this state when Vondo is currently a party member
    say ~I am so happy to be in your party, <charname/>.~
    reply ~I'm happy too.~ exit
    reply ~Get out of my party!~ do { _leave _me } exit  // Vondo leaves the party, and the dialogue exits.
  end

  begin out_party // We get this state when Vondo isn't a member of the party, but was at one time
    say ~Life isn't the same outside your party, <charname/>. May I come back now?~
    reply ~Sure, welcome back.~ do {_join _me} exit
    reply ~Nope.~ exit
 end

This gives Vondo a basic set of joining and parting dialogue. To give him a little extra flavor and character, we can add interparty banter to his dialogue file. Banter occurs on a semi-random basis. When a timer expires, one NPC is chosen and their list of banter-flagged dialogue states considered. If one of those states has a true transition, the banter begins. Usually that means the required NPCs are both in the party together, but there may be more complex requirements as well, such as a particular quest being completed or a previous banter having been performed.

We will give Vondo a brief exchange with Redethe. This banter could happen at any time when Vondo and Redethe are in the party together.

(continuing vondo.dlg)

  banter // Banter is a special dialogue keyword that indicates that the following state should be considered for random interparty conversation.
if (_ok_banter _redethe)  // _ok_banter is a catchall which checks, among other things, that the participant is neither dead nor ridiculously out of sight.
then begin vondo_redethe_1 // This is the actual name of the dialogue state
    goto vondo_redethe_1_chain // We could start the banter right here, but instead we will just have this banter state be a silent one, and put the entire conversation below.
   end

chain vondo vondo_redethe_1_chain // "chain" is a special dialogue keyword. Chain mode lets us quickly assemble long multi-actor conversations. A single = indicates "a new line from the previous speaker." Two == indicates "switch to a new speaker."
~"Do you know what I like best about being in <charname/>'s party, Redethe?"~ // Vondo speaks first since he "owns" the chain
== redethe ~"No."~ // We switch to Redethe
= ~But I fear you are about to tell me."~ // Single = means Redethe speaks this line as well
== vondo ~"I like the hours. I like being wanted. And I like the benefits."~ // Double == means we switch speakers, and specify Vondo, who speaks here.
= ~"What do you like?"~ // Single =, so we stick with Vondo
== redethe ~"I liked it better before you joined."~ // Switch back to Redethe for the stinger.
exit   

end // This "end" is actually terminating the "begin vondo" from the very top of the dialogue file.

These techniques make it possible to add rich, talkative NPCs to the gameworld. The flexible transition weighting system ensures that complex dialogues stay organized, and there are no special requirements for a creature to become joinable. This streamlines the path from character concept, to full-fledged add-on Broken Hourglass party member.

< Previous   Next >