WelcomeWhat's NewsORLibraryDownloadsLinksWebMaster@OnyxRing.com

This site
pageviews since

Today's date:

Author: Jim Fisher
Title: Advanced NPCs, Part 2a: Conversation and Learning

Creation Date: 8/9/2004 6:09:52 PM
Last Updated: 8/9/2004 6:09:52 PM

The Basics
The Menu System
Ask/Tell/Answer (via Keywords)
Ask/Tell/Answer (via Conversation Objects)
Concluding the Basics
Note: Because of the length, the "Knowledge and Conversation" article was written in three sections (of which this is the first) listed here for convenience:
Part A (§1): The Basics
Part B (§2): Advanced Techniques
Part C (§3): More Advanced Techniques
Knowledge and Conversation
§1 The Basics
(Advanced NPC Implementation, Part 2a)

s developers, we can put forth a great deal of effort while trying to make an NPC appear believable. We can make the character walk around randomly. We can make him steal items that are lying around. We can even program the character to scribble graffiti on walls at significant locations. It's true, a character can be designed to do just about anything imaginable, but there is one aspect of NPC design that will, above all others, make or break the believability of an NPC: conversation.

A successful implementation of a PC-NPC conversation is no small task. Many developers have tried and failed to create believable dialog. Obviously, success at this bares great dependency upon the author's writing skill. Conversely, a well-written manuscript can be implemented poorly. The truth is, a text adventure without code is just text. Code and prose are equally important in interactive fiction and the two must complement each other to truly be effective.

The purpose of this three-part article series is to identify the major coding techniques used in the development of talking NPCs and give examples of implementing some of these. Additionally we will cover advanced techniques such as uniting knowledge and information, separating information from conversation, morphing conversation topics, conversation scripts, and implementing knowledge that is learnable. First, a disclaimer:

In interactive fiction, complex puzzles often have multiple solutions. The similarities between life and art are ever obvious in this regard, since complex coding objectives often can be accomplished by multiple techniques. As a general rule of thumb, the more complex the objective, the more diversified are the possible solutions. Since this is an article about "advanced" techniques in NPC conversations, the variations and alternate implementations abound. It should be noted that the techniques covered in this article work for me, but my way is not the only way. If you, as the reader, see another technique then feel free to implement it.

Now that that stuff is out of the way, let us begin slowly by touching upon the three major techniques used to implement interactive character conversations: Menus, keywords, and topics.

The Menu System

A commonly used method of implementing conversation is the menu-based system. There are several advantages to using this technique:

  • There is never any doubt for the player that there is a conversation to be had (i.e.: that there are things to say or ask) since the conversation options are listed up front.
  • The player never has a problem with phrasing (finding the right words to ask).
  • The developer has control of the flow of conversation. Specifically, the author determines what can be asked and when that menu option appears.

  • There are also some disadvantages to this technique:
  • The player's experience is a less personal one. Choosing conversation options is more akin to the "Choose Your Own Adventure" style of stories. It can also be frustrating to a player that wants to ask a question which isn't listed. Given the same conversation coded two ways, many players come away feeling that their options seemed more limited with this technique. A player that has the ability to formulate his own question experiences a feeling of freedom, even if the question has not been coded for and the response is just the simple: "I know nothing about that."
  • The menu technique also requires less player involvement. In conventional methods, important conversation topics can be derived from seemingly insignificant details mentioned previously. For example, asking about a book of restaurant matches, found in a character's coat, might reveal him as a previously unknown witness to a restaurant murder. In a mystery game, the matches might be a clue that the player would need to attend to and recognize the significance of. Seeing the menu item "Ask about matchbook" fairly spoils the need for the player to pay attention to this sort of detail.
  • A significant drawback to menu-based conversations is the inherent limitation of realism. A believable NPC is often coded with dozens of insignificant responses that have no importance in the game except to add realism to the character. Implementing this sort of detail in a menu-based system is difficult at best. If not carefully limited in scope, the player could be left facing a substantially sized menu of meaningless small talk.

  • This is not to say that the menu system is not worth considering. In fact, it has been implemented with great success in a few choice works. It is, however a replacement for what the standard library offers, the Ask/Tell/Answer paradigm, and will not be discussed further in this article.

    Nearly every other conversation technique is based upon the Ask/Tell/Answer model. At its most basic implementation is the use of keywords...

    Using Ask/Tell/Answer (With Keywords)

    As stated above, the standard library implements a basic conversation framework by defining three communication verbs. These three verb are 'ask,' 'tell,' and 'answer' (or say) and they are handled traditionally in the NPC's life property. For the 'tell' and 'ask' forms, the first significant keyword is stored in the library variable 'second.' For the 'answer' form, it is stored in the variable 'noun.' Take the following as an example:

     object -> magicmirror "magic mirror" 
     has animate
     with name 'magic' 'mirror'
     ,   life [;
             tell: if(second=='hello')"~Yes, I know what 'hello' is.  
                I may not get out much, but I recognize a greeting!~";
             answer: if(noun=='hello') "~Well, 'hello' right back at you,   
             ask: if(second=='hello') "~You want to know about 'hello?'
                Where have you been all your life, that you don't 
                recognize a greeting?~";

    See how the keyword 'hello' is handled differently depending upon the verb used to reference it:

     >ask mirror about hello
     "You want to know about 'hello?'  Where have you been all your life, 
     that you don't recognize a greeting?"
     >tell mirror about hello
     "Yes, I know what 'hello' is. I may not get out much, but I recognize a    
     >say hello to mirror
     "Well, 'hello' right back at you, mortal."

    Alone, this technique is fairly limited, but it is also one of the most widely used methods for implementing conversation that you will find in Inform games. As such, there are various methods of making it somewhat more powerful. The consult_from and consult_words variables, for example, can be used to "look ahead" and example more than a single word. To demonstrate a starting point for diversifying the keyword technique, let's focus on the ASK form of communication in the next example:

     object -> magicmirror "magic mirror" 
       has animate
       with name 'magic' 'mirror'
       , life [word;
             ask:wn=consult_from; !position the pointer for the
                word=NextWord(); !NextWord call
                if(word=='hello') { !make sure we are talking about a type of hello
                   while((word=NextWord())=='in' or 'from'); !ignored words
                   switch(word) { !lets find what kind of hello we are looking for
                      0: "~You want to know about 'hello?' Where have you been
                         all your life, that you don't recognize a  greeting?~";
                      'canada', 'canadian': "~In Canada they say 'Hello, eh.'~";   
                      'hawaii', 'hawaiian': "~Hmmm, I believe that would be 

    Now the keyword 'hello' can be put into a specific context:

     >ask mirror about hello in canada
     "In Canada they say 'Hello, eh.'"
     >ask mirror about hello in hawaiian
     "Hmmm, I believe that would be 'Aloha.'"
     >ask mirror about hello
     "You want to know about 'hello?' Where have you been all your life, that   
     you don't recognize a greeting?" 

    Obviously the above example isn't as useful as it could be since it is too dependent upon word order. For instance,

     >ask mirror about Hawaiian hello   

    will NOT be matched by the routine in its present form. Certainly it could be modified to scan for words despite order, but the more we pattern match, the more we are duplicating code that exists in the parser. For conversation topics that are more than a single unique keyword, there is a better way.

    Using Ask/Tell/Answer (With Conversation Objects)

    Inform is an object based language. The rooms the player visits are objects, the items that are gathered are objects, and the very NPCs that exist in the game are objects. Not surprisingly, all of these things are accessible through the game. We can look at the room we are in, pick up a hammer that lies on the floor, and talk to the NPCs. Each of these objects exists in the game world. One of the more allusive concepts for a new Inform developer to grasp is that objects do not have to actually be present in the game at all. They can, instead, be abstractions. Use of these non-existent objects is the third, and most powerful method of implementing NPC conversation, since it opens the door for numerous other techniques.

    Let us set the stage for more advanced techniques by walking through a thorough example of basic conversation objects. First, we can create a base class from which all conversation objects will be derived:

     class KnowledgeTopic;   

    Not the most impressive line of code, but it is a step in the right direction. In fact, what it accomplishes is significant. We can now, for instance, write a fully qualified scope rule to determine if an NPC "owns" a specific conversation object and extend the 'ask' verb to use that rule.

     Object CommonKnowledge;
     [TopicInTarget o; !isolate information known by the target
          2:objectloop(o ofclass KnowledgeTopic && 
             parent(o)==inputobjs-->2 or CommonKnowledge) 
             PlaceInScope(o); rtrue;
          3: "I did not understand who or what you were referring to.";   
     extend "ask" first * creature 'about' scope=TopicInTarget-> Ask;

    The TopicInTarget() routine (scope rule) will pull into scope any KnowledgeTopic derived object that is a child of the character being questioned. In this way we can define what the character "knows". Additionally, notice the definition of the CommonKnowledge object. Topics that are placed within this object are also pulled into scope. All characters know topics that are classified as "common knowledge." Here is an example of two topics. The first, "beauty," is a child of the CommonKnowledge object and so is known by all characters. Only the mirror knows the second, "snowwhite":

     KnowledgeTopic beauty CommonKnowledge with name 'beauty';
     KnowledgeTopic snowwhite magicmirror with name 'snowwhite' 'girl';   

    As you can see, using knowledge objects allows us to fill the 'name' property with the keywords that can reference a topic, just as we would a table or a chair. With this technique, the variable 'second' no longer holds the keyword, but points to the object that was recognized. The life property must be changed to reflect this:

     object -> magicmirror "magic mirror" 
       has animate
       with name 'magic' 'mirror'
       , life [; ask:switch(second) {
             beauty: "~Ah yes. That is only skin deep.~";
             snowwhite: "~Yes, I remember her.  But that was long ago.~"; }] 

    For the sake of example, and to verify that our new scope rule is working how we intended it, let's create another character coded almost exactly as the mirror:

     object -> guard "guard" 
       has animate
       with name 'guard'
       , life [; ask:switch(second) {
             beauty: "~Ah yes. That is only skin deep.~";
             snowwhite: "~Yes, I remember her.  But that was long ago.~"; }] 

    Now we have two characters to converse with. Note that, like the mirror, the guard is also coded to respond to the 'snowwhite' question. It'll be up to our scope rule to determine if he knows about the topic. Since he does not, this portion of his life rule will never be called and the text, although defined, will never print:

     >ask guard about beauty
     "Ah yes. That is only skin deep."
     >ask mirror about beauty
     "Ah yes. That is only skin deep."
     >ask mirror about snowwhite
     "Yes, I remember her. But that was long ago."   
     >ask guard about snowwhite
     There is no reply.

    That was what we expected. The guard knows nothing of 'snowwhite.' Our new scope rule is pulling in the appropriate knowledge for the appropriate character.

    On to Advanced Techniques (Concluding the Basics)

    The topics discussed in this section have set the stage for a discussion of more advanced techniques. In §2 we will begin to delve into more advanced topics that build upon what we have learned so far. For readers that wish to further contemplate the material of this section, I highly recommend a review of Roger Firth's Infact page. Specifically, the conversation section found at "
    http://www.firthworks.com/roger/infact/convers1.html" is of immediate relevance.

    And now, on to section 2...

    §2b: Advanced Techniques

    Pronouns on Steroids

    Copyright 2004 - 2021 : Jim Fisher
    OnyxRing.com has been given the potentially non-exclusive right to display the content of this article. However the original author retains all rights. Permission to reproduce this article -- either in part or in whole -- is left strictly to the discretion of the original author.

    Table of ContentsAuthorsSearchIndex
    Would you
    recommend this
    article to
    someone else?
    You bet I would! Heck No!