| <html lang="en" xml:lang="en"> | |
| <head> | |
| <title>An Adventure Authoring System</title> | |
| <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> | |
| <meta name="Description" content=""An Adventure Authoring System" is an overview of AdvSys by its author, | |
| David Betz. Originally published in the May 1987 issue of BYTE magazine. | |
| Transcribed into HTML by David Welbourn." /> | |
| <style type="text/css"> | |
| pre { margin-left:1em; } | |
| div.rinset {float:right; margin-left:1em; margin-bottom:0.5em; } | |
| table.flowchart tr td {font-family:Arial,Helvetica,sans-serif; text-align:center; background-color:#EEE; color:black; } | |
| table.flowchart tr td.box {border:solid red 1px; background-color:#FEE; color:black; } | |
| table.flowchart tr td.arr {background-color:#EEE; color:red; } | |
| td.clshdr {background-color:#FED ; color:black; } | |
| td.prophdr {border-top: solid black 1px; } | |
| td.prop {text-indent:1em; } | |
| </style> | |
| </head> | |
| <body style="padding-left:0.5in;padding-right:0.5in;background-color:white;color:black"> | |
| <p style="text-align:right"><big>David Betz</big></p> | |
| <h1>An Adventure Authoring System</h1> | |
| <h2 style="font-family:Arial;Helvetica;sans-serif"><i>A tour of AdvSys, a tool for writing text adventure games</i></h2> | |
| <p>[Editor's note: <i>“Interactive Fiction as Literature,” a companion piece to this article, | |
| begins on <a href="byte87_buckles.html">page 135</a>.</i>]</p> | |
| <p>AdvSys is a system I designed for writing text adventure games. In adventure games, the player acts as an | |
| adventurer in a simulated world (real or imaginary). Players determine their own course of action by typing commands | |
| that trigger events in the simulated world.</p> | |
| <p>You can approach the writing of an adventure game in many ways, and a number of books describe how to use | |
| traditional programming languages to write adventures. The most commonly used language is BASIC. But while you can | |
| certainly build very complex and interesting adventures using BASIC, it was not designed specifically for that purpose.</p> | |
| <p>Much of the task of building an adventure game program consists of constructing complex data structures that model | |
| the game universe. BASIC has no convenient means for describing these data structures. Even Pascal, which is rich in data | |
| structuring facilities, has no easy means of constructing complex initialized data structures.</p> | |
| <p>Another approach to writing adventures is to use a special language specifically designed for the purpose. This | |
| article describes such a language.</p> | |
| <p>A language for writing adventures must have three essential features: a parser to handle commands typed by the | |
| player, an object-description facility, and a language for specifying the events that take place in response to the | |
| players' commands.</p> | |
| <div style="float:right;border-top:solid black 3px;border-bottom:solid black 3px;padding-left:1em; | |
| padding-top:0.5em;padding-bottom:0.5em;font-family:Arial,Helvetica,sans-serif;font-style:italic;width:25%">Adventure | |
| games generally take place in a world made up of a network of interconnected 'locations.'</div> | |
| <h3>The Parser</h3> | |
| <p>The parser is responsible for prompting the player to enter a command. It must read the command from the keyboard | |
| and break it into pieces that can be digested by the action code. All commands are broken into one of five different | |
| types of phrases:</p> | |
| <ol> | |
| <li>an actor phrase</li> | |
| <li>a verb phrase</li> | |
| <li>a list of direct-object noun phrases</li> | |
| <li>a preposition</li> | |
| <li>an indirect-object noun phrase</li> | |
| </ol> | |
| <p>Not all of these phrases are present in every command, and the parser recognizes only a limited set of command | |
| forms. AdvSys recognizes these forms:</p> | |
| <ol> | |
| <li>[actor,] verb</li> | |
| <li>[actor,] verb direct-objects</li> | |
| <li>[actor,] verb direct-objects preposition indirect-object</li> | |
| <li>[actor,] verb indirect-object direct-objects</li> | |
| </ol> | |
| <p>where “direct-objects” is defined as:</p> | |
| <p style="margin-left:1em">direct-object [conjunction direct-object]*</p> | |
| <p>(In this article, phrases within square brackets are optional. Phrases followed by an asterisk may be repeated | |
| zero or more times.)</p> | |
| <p>The terms “actor,” “direct-object,” and “indirect-object” all represent noun | |
| phrases. Each noun phrase is of the form</p> | |
| <p style="margin-left:1em">[article] adjective* noun</p> | |
| <p>In other words, a noun phrase consists of an optional article followed by zero or more adjectives followed by a noun. | |
| Here are some examples of noun phrases recognized by the AdvSys parser:</p> | |
| <p style="margin-left:1em">sword<br />the angry man<br />the thick red book</p> | |
| <p>Now that we have defined the command forms that are handled by the parser, let's look at some complete commands. | |
| I will precede each example with a number indicating the form on which it is based:</p> | |
| <ol> | |
| <li value="1">Look</li> | |
| <li value="1">Fred, wake up</li> | |
| <li value="2">Drop the book</li> | |
| <li value="2">Take the sword and the orange vial</li> | |
| <li value="3">Give the book to the librarian</li> | |
| <li value="4">Librarian, give me the book</li> | |
| <li value="4">Show the librarian the book</li> | |
| </ol> | |
| <p>The second example illustrates another feature of the parser. Verb phrases can consist of either a single word like | |
| “take” or a pair of words like “pick up” or “wake up.” If a verb phrase consists of | |
| two words, the words do not have to be immediately adjacent to each other in the command. Either “Pick up the | |
| book” or “Pick the book up” will produce the intended result.</p> | |
| <p>After breaking the command into phrases, the parser sets a small number of global variables to communicate the | |
| results of its work to the rest of the adventure system. The parser stores each noun phrase in an internal array, | |
| indexed by the noun phrase number. The index associated with the actor noun phrase is stored in the global variable | |
| <code>$actor</code>, the index associated with the first direct-object noun phrase is stored in the variable | |
| <code>$dobject</code>, and the index of the indirect-object noun phrase is stored in the variable | |
| <code>$iobject</code>. If a noun phrase is missing from the command, its corresponding variable is set to <code>nil</code> | |
| (which is the same as zero in this system). These noun phrase numbers will be used later to determine which objects | |
| the noun phrases refer to.</p> | |
| <p>The parser uses the verb phrase and the preposition to select an action to handle the command. It stores the | |
| selected action in the global variable <code>$action</code>. (More details about actions later.) The adventure language | |
| statements you use to define the vocabulary used by the parser are</p> | |
| <p style="margin-left:1em"><code>(adjective word*)<br />(preposition word*)<br /> | |
| (conjunction word*)<br />(article word*)</code></p> | |
| <p>In addition, it is useful to define synonyms of some words. This is accomplished by the statement</p> | |
| <p style="margin-left:1em"><code>(synonym word synonym*)</code></p> | |
| <h3>Objects</h3> | |
| <p>Adventure games generally take place in a world made up of a network of interconnected “locations.” | |
| Each location has a set of exits that connect it with adjacent locations. The player explores the game world by moving | |
| an actor from location to location through these exits.</p> | |
| <p>In the course of exploring these locations, the player encounters “things” and other “actors.” | |
| In a fantasy adventure, for example, the player might encounter a magic sword or an angry dwarf.</p> | |
| <p>AdvSys groups locations, actors, and things in the general category of <i>objects</i>. Each object in the adventure | |
| has an associated set of <i>properties</i>. Each property has an associated <i>value</i>.</p> | |
| <p>A location object, for example, has a property for each of its exits. The values of these exit properties are the | |
| locations a player will reach by passing through the corresponding exits. Location objects also have <i>description</i> | |
| properties whose values are text strings describing the location under different circumstances. The concept of objects | |
| with properties is very general in this system, leaving you, as you write your game, free to invent new properties | |
| appropriate to a particular type of game and to define new classes of objects that share common properties, structure, | |
| and behavior.</p> | |
| <p>For instance, a location object might be defined as</p> | |
| <pre>(location living-room | |
| (property | |
| description "You are in the living room.\n" | |
| north library | |
| south entrance | |
| east dining-room))</pre> | |
| <p>This is a definition of <code>living-room</code>, a location object with the properties <code>description</code>, | |
| <code>north</code>, <code>south</code>, and <code>east</code>. The description property has the string <code>"You are | |
| in the living room.\n"</code> as its value. (A back slash followed by the letter <code>n</code> instructs the program | |
| to start a new line). The property <code>north</code> has the value <code>library</code> (the lcoation the player | |
| reaches when traveling north from the living room), the property <code>south</code> has the value <code>entrance</code>, | |
| and the property <code>east</code> has the value <code>dining-room</code>.</p> | |
| <p>Things are objects that the player can manipulate. A thing must have a noun associated with it. And since the | |
| same noun can refer the different objects, you can associate adjectives with the objects to make references to the | |
| objects umambiguous. Here is an example of an object description:</p> | |
| <pre>(thing sword | |
| (noun sword weapon) | |
| (adjective red) | |
| (property | |
| description "a red sword" | |
| weight 20 | |
| value 10 | |
| initial-location entrance))</pre> | |
| <p>This definition describes the object <code>sword</code>, which has the nouns <code>sword</code> and | |
| <code>weapon</code> and the adjective <code>red</code>. Thus, a player could refer to this object as “the | |
| sword,” “the weapon,” “the red sword,” or “the red weapon.”</p> | |
| <p>The <code>sword</code> object also has four properties: <code>description</code> has the value <code>a red sword</code>, | |
| <code>weight</code> has the value <code>20</code>, <code>value</code> has the value <code>10</code>, and | |
| <code>initial-location</code> has the value <code>entrance</code>.</p> | |
| <p>The <code>weight</code> property might be used to provide the player with a limited carrying capacity. If each object | |
| has a weight, you can prevent the player from carrying a set of objects whose combined weight exceeds the player's load | |
| capacity. Similiarly, the <code>value</code> property could be used for scoring. Each object has an associated value that | |
| the game will add to the player's score when the object is carried to some specified location. The meaning of these | |
| properties is up to you, the game author.</p> | |
| <h3>Defining Objects</h3> | |
| <p>Actors are objects that represent characters in the adventure. The player controls a special actor that is the | |
| “player character.” The player “sees” through this actor's eyes and takes part in the action | |
| by controlling this actor's movements. In AdvSys, the player character is called the “adventurer.” | |
| Other actor objects represent nonplayer characters. These nonplayer characters are controlled by the adventure program | |
| (the code that you have written) rather than by the player, and they may include both friendly and hostile characters | |
| with whom the adventurer must interact to solve the adventure. An example of a nonplayer character might be</p> | |
| <pre>(actor troll | |
| (noun troll dwarf) | |
| (adjective angry) | |
| (property | |
| description "There is an angry troll here." | |
| short-description "an angry troll" | |
| initial-location "dungeon"))</pre> | |
| <p>This defines a <code>troll</code> that the player can refer to as “the angry troll,” “the dwarf,” | |
| and so on, and is initially found in the dungeon, where the adventure will see the words <code>"There is an angry troll | |
| here"</code> upon entering.</p> | |
| <p>So far we have seen how to describe the static portions of an adventure game. Location objects allow us to build | |
| the adventure universe, things allow us to place interesting objects in this universe, and actors allow us to populate | |
| the universe with other characters.</p> | |
| <p>I have defined locations, things, and actors as part of a run-time package that comes with AdvSys, but you can define | |
| your own objects if you wish. The statements used to define these objects, as shown in the previous examples, are</p> | |
| <pre>(object object-type | |
| object-statement*) | |
| (object-type name | |
| object-statement*)</pre> | |
| <p>where the <code>object-statement</code> may be defined using one of the following:</p> | |
| <pre>(noun word*) | |
| (adjective word*) | |
| (property [property-name initial-value]*) | |
| (class-property [property-name initial-value]*) | |
| (method (selector arg-name* [&aux tmp-name*]) expression*)</pre> | |
| <p>Now we will explore how things happen in the adventure universe.</p> | |
| <div class="rinset" style="width:25%"> | |
| <table class="flowchart" cellspacing="0" style="width:100%"> | |
| <tr><td class="box">init</td><td colspan="4" rowspan="2" /></tr> | |
| <tr><td class="arr">↓</td></tr> | |
| <tr><td class="box">update</td><td class="arr">←—</td> | |
| <td class="arr">———</td><td class="arr">——</td><td /></tr> | |
| <tr><td class="arr">↓</td><td colspan="3" /><td class="arr">|</td></tr> | |
| <tr><td>(parse)</td><td class="arr">—→</td><td class="box">error</td> | |
| <td class="arr">—→</td><td class="arr">|</td></tr> | |
| <tr><td class="arr">↓</td><td colspan="3" /><td class="arr">↑</td></tr> | |
| <tr><td class="box">before</td><td colspan="3" /><td class="arr">|</td></tr> | |
| <tr><td class="arr">↓</td><td colspan="3" /><td class="arr">|</td></tr> | |
| <tr><td>(action)</td><td colspan="3" /><td class="arr">|</td></tr> | |
| <tr><td class="arr">↓</td><td colspan="3" /><td class="arr">|</td></tr> | |
| <tr><td class="box">after</td><td class="arr">——</td><td class="arr">———</td> | |
| <td class="arr">——</td><td /></tr> | |
| </table> | |
| <p style="margin-top:0.5em"><b>Figure 1:</b> <i>The action in an AdvSys game is controlled by “handlers,” | |
| described below.</i></p> | |
| </div> | |
| <h3>Handlers</h3> | |
| <p>All action within an AdvSys adventure is controlled by a set of “handler” and “action” | |
| procedures. There are five different handlers that are part of the main control loop. Each of these handlers contains | |
| user-defined code written in the adventure language. Figure 1 illustrates the control flow of the adventure system.</p> | |
| <p>At the beginning of the game, the AdvSys interpreter calls the “init” handler. The init handler is | |
| responsible for printing any introductory text explaining the initial situation and for performing any initialization. | |
| Here is an example of an init handler definition:</p> | |
| <pre>(init | |
| (print "Welcome to the sample adventure!\n") | |
| (setq curloc nil))</pre> | |
| <p>This example handler prints a welcome message and sets the variable <code>curloc</code> (the current location of | |
| the adventurer) to <code>nil</code>.</p> | |
| <p>The first handler in the main loop is the “update” handler, which is responsible for handling changes | |
| in the game state. If the player has moved to a new location, the update handler should print a description of the new | |
| location. Here is an example:</p> | |
| <pre>(update | |
| (if (not (= (getp adventurer parent) curloc)) | |
| (progn | |
| (setq curloc (getp adventurer parent)) | |
| (send curloc describe))))</pre> | |
| <p>This handler checks to see if the adventurer's new location is different from the current one. If it is, the handler | |
| updates the current location and prints a description of the new location by sending the message <code>describe</code> | |
| to the new location object. (Note that on the first pass through the control loop, the update handler sees the location | |
| of the adventurer as being different from that stored in <code>curloc</code> and prints a description of the initial | |
| location.)</p> | |
| <p>After the update handler has finished, the AdvSys interpreter calls the parser. The parser prompts the player for a | |
| new command, allows the player to enter the command, and parses the command according to the description above. The | |
| parser communicates its results to the remaining handlers by setting the global variables <code>$actor</code>, | |
| <code>$action</code>, <code>$dobject</code>, <code>$ndobjects</code>, and <code>$iobject</code>. If an error occurs | |
| during the parsing of the command, the system prints an error message, calls the error handler, and goes back to the | |
| start of the main loop (the update handler).</p> | |
| <p>Assuming that the parser succeeds in parsing a syntactically valid command, the AdvSys interpreter calls the | |
| “before” handler, which handles any general preprocessing that needs to be done before the command-specific | |
| code is performed.</p> | |
| <p>Next, the action associated with the player's command is performed. This is the action that was stored in the global | |
| variable <code>$action</code> by the parser. This code is responsible for actually carrying out the player's request | |
| (if it is allowed in the current situation).</p> | |
| <p>The last handler in the main loop is the “after” handler, which handles any processing that must happen | |
| after the action is complete, such as updating the game clock or the player's score, or anything that should happen | |
| only at the end of a successful turn.</p> | |
| <p>The adventure language statements that are used to define handlers are:</p> | |
| <pre>(init expression*) | |
| (update expression*) | |
| (before expression*) | |
| (after expression*) | |
| (error expression*)</pre> | |
| <h3>Actions</h3> | |
| <p>The only part of the adventure system left to describe is the method for defining actions. Each action definition | |
| handles a specific command form and verb/preposition combination. Let's look at an example:</p> | |
| <pre>(action a-take | |
| (verb take get (pick up)) | |
| (direct-object) | |
| (code | |
| (setq %dobject (in-location $dobject)) | |
| (if (getp %dobject takeable) | |
| (progn | |
| (if (send %actor carrying? %dobject)) | |
| (complain "You are already carrying the " $dobject "!\n")) | |
| (send %actor take %dobject) | |
| (print-noun $dobject) | |
| (print " taken.\n")) | |
| (complain "You can't take the " $dobject "!\n"))))</pre> | |
| <p>This action definition handles commands like “take the lamp” or “pick up the sword.” | |
| It handles any command with the verbs “take,” “get,” or “pick up” and at least one | |
| direct object. The code begins by determining to which object the direct-object noun phrases refers. The function | |
| <code>in-location</code> looks for an object in the current location (<code>curloc</code>) that matches the noun phrase | |
| it receives as its argument. The function returns the matching object or signals an error if no object in the current | |
| location matches the noun phrase.</p> | |
| <p>Assuming that <code>in-location</code> finds a matching object, the <code>action</code> code assigns that object | |
| to the variable <code>%dobject</code> and then checks to see if it is possible to pick up the object. This is done by | |
| checking the value of the property <code>takeable</code>. If the result is <code>true</code>, the object may be taken. | |
| If not, the program prints an error message and the turn ends.</p> | |
| <p>If the object is takeable, the code then checks to see if the player is already carrying it. It does this by sending | |
| the message <code>carrying?</code> to the <code>actor</code> object. This message checks to see if the object is currently | |
| being carried by the actor receiving the message. Both actor and location objects support the concept of containment. | |
| If the adventurer is already carrying the object, the program prints a message saying so and the action is complete. | |
| If not, the program adds the object to the adventurer's inventory by sending the <code>actor</code> object the | |
| <code>take</code> message and the program prints a message indicating the successful completion of the command.</p> | |
| <p>This example illustrates the use of object-oriented programming techniques in the specification of action code. | |
| AdvSys lets you define “methods” to handle messages sent to objects. Each message requests that the object | |
| perform some operation. The specific operation that is performed in response to a message is determined by a method | |
| definition associated with the object that receives the message. AdvSys supports hierarchical inheritence of both | |
| properties and methods so you can define classes of objects that share the same property structure and behavior.</p> | |
| <p>The default run-time environment in AdvSys adventures (contained in the file OBJECTS.ADI) defines methods that | |
| implement the default behavior for the built-in object classes. But the power of the system is that it lets you define | |
| subclasses of these default classes that implement either objects or classes of objects whose behavior and properties | |
| are unique to a particular adventure. This allows you to build on the existing classes rather than “reinventing | |
| the wheel.” The default run-time environment is thus a framework for building an adventure rather than merely | |
| a sample program.</p> | |
| <p>If a command contains multiple direct objects, the parser will store the first direct object noun phrase number in | |
| <code>$dobject</code> and the number of direct objects in <code>$ndobjects</code>. If your action code doesn't touch | |
| the value of <code>$ndobjects</code>, at the end of the after handler, the system will assign the next direct object | |
| ot the variable <code>$dobject</code>, decrement the count stored in <code>$ndobjects</code>, and loop back to the | |
| before handler again. This means that you don't need to do anything special to handle commands with multiple direct | |
| objects. However, if you have a reason to want to handle all of the objects yourself, you can do so on the first pass | |
| through the action code and then set the variable <code>$ndobjects</code> to nil to prevent the system from looping | |
| back to handle the additional direct objects.</p> | |
| <p>The adventure language statements used to define actions are:</p> | |
| <pre>(action name | |
| action-statement*)</pre> | |
| <p>with <code>action-statement</code> defined as one of the following:</p> | |
| <pre>(actor [flag]) | |
| (verb [word | (word1 word2)]*) | |
| (direct-object [flag]) | |
| (preposition word*) | |
| (indirect-object [flag]) | |
| (code expression*)</pre> | |
| <p>and where <code>flag</code> is one of the following:</p> | |
| <pre>required | |
| optional | |
| forbidden</pre> | |
| <h3>Expressions</h3> | |
| <p>Handlers, actions, and methods all contain executable statements called expressions. The complete list of | |
| expressions allowed in AdvSys is shown in table 1. Each of the forms shown computes a result, which is returned as | |
| the value of the expression.</p> | |
| <p>Here is an example of an expression derived from table 1:</p> | |
| <pre>(setq x (+ (* a b) (* c d)))</pre> | |
| <p>This executable statement is an arithmetic expression that multiplies <code>a</code> times <code>b</code> and | |
| <code>c</code> times <code>d</code>, adds the two products, and stores the result in the variable <code>x</code>. | |
| Even the <code>setq</code> form returns a value. Its value is the new value of the variable after the assignment | |
| is done.</p> | |
| <div style="width:100%;border:black solid;border-width:3px 1px;padding:5px;"> | |
| <caption><b>Table 1:</b> <i>The executable statements used in actions and handlers to control an adventure | |
| game written with AdvSys.</i></caption> | |
| <table> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td> | |
| <table> | |
| <tr><td>(+ expr1 expr2)</td><td>Add two numbers</td></tr> | |
| <tr><td>(− expr1 expr2)</td><td>Subtract two numbers</td></tr> | |
| <tr><td>(* expr1 expr2)</td><td>Multiply two numbers</td></tr> | |
| <tr><td>(/ expr1 expr2)</td><td>Divide two numbers</td></tr> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td>(% expr1 expr2)</td><td>Remainder after dividing two numbers</td></tr> | |
| <tr><td>(& expr1 expr2)</td><td>Bit-wise AND of two numbers</td></tr> | |
| <tr><td>(: expr1 expr2)</td><td>Bit-wise OR of two numbers</td></tr> | |
| <tr><td>( expr)</td><td>Bit-wise complement of a number</td></tr> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td>(< expr1 expr2)</td><td>Is expr1 less than expr2?</td></tr> | |
| <tr><td>(= expr1 expr2)</td><td>Is expr1 equal to expr2?</td></tr> | |
| <tr><td>(> expr1 expr2)</td><td>Is expr1 greater than expr2?</td></tr> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td>(setq sym value)</td><td>Set the value of a variable</td></tr> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td>(getp obj prop)</td><td>Get the value of a property</td></tr> | |
| <tr><td>(setp obj prop val)</td><td>Set the value of a property</td></tr> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td>(and [expr]*)</td><td>Logical AND of a set of expressions</td></tr> | |
| <tr><td>(or [expr]*)</td><td>Logical OR of a set of expressions</td></tr> | |
| <tr><td>(not expr)</td><td>Logical NOT of an expression</td></tr> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td>(cond [clause]*)</td><td>LISP style conditional</td></tr> | |
| <tr><td>(if expr then-expr [else-expr])</td><td>Traditional “IF” statement</td></tr> | |
| <tr><td>(while expr [expr]*)</td><td>Traditional “WHILE” statement</td></tr> | |
| <tr><td>(progn [expr]*)</td><td>Group expressions into a block</td></tr> | |
| <tr><td>(return [expr])</td><td>Return from a function</td></tr> | |
| </table> | |
| </td><td style="vertical-align:top"> | |
| <table> | |
| <tr><td>(expr [expr]*)</td><td>Call a user-defined function</td></tr> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td>(class obj)</td><td>Get the class of an object</td></tr> | |
| <tr><td>(send obj sel [expr]*)</td><td>Send a message to an object</td></tr> | |
| <tr><td>(send-super sel [expr]*)</td><td>Send a message to the superclass of an object</td></tr> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td>(randomize)</td><td>Initialize the random-number generator</td></tr> | |
| <tr><td>(rand expr)</td><td>Generate a random number between 0 and n-1</td></tr> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td>(yes-or-no)</td><td>Prompt the user and accept YES or NO</td></tr> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td>(print expr)</td><td>Print a string</td></tr> | |
| <tr><td>(print-number expr)</td><td>Print a number</td></tr> | |
| <tr><td>(print-noun expr)</td><td>Print a noun phrase</td></tr> | |
| <tr><td>(terpri)</td><td>Start a new print line</td></tr> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td>(match np obj)</td><td>Does this noun phrase match this object?</td></tr> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td>(finish)</td><td>Finish this turn (go to the AFTER handler)</td></tr> | |
| <tr><td>(chain)</td><td>Exit this handler and go to the next</td></tr> | |
| <tr><td>(abort)</td><td>Abort this turn (go to the UPDATE handler)</td></tr> | |
| <tr><td>(exit)</td><td>Exit the adventure (back to DOS)</td></tr> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td>(save)</td><td>Save the current game state to a file</td></tr> | |
| <tr><td>(restore)</td><td>Restore the game state from a file</td></tr> | |
| </table> | |
| </td></tr> | |
| </table> | |
| </div> | |
| <h3>Run-Time Functions</h3> | |
| <p>Not all of the functions that I have used in the examples are listed in table 1. The missing functions are part | |
| of the run-time package OBJECTS.ADI (see table 2) and are not built into the language. These functions are defined | |
| in adventure code and can be changed by the adventure author to suit a variety of tasks.</p> | |
| <p>I wrote the run-time package so that you would not have to start from scratch when writing adventures. The run-time | |
| package defines commonly used object types such as locations, actors, and things; common actions such as look and take; | |
| game control commands like save and restore; and methods for handling common messages. These methods define the default | |
| behavior of objects, but can be easily supplemented or overrideen by methods defined in objects or subclasses defined | |
| by the adventure author. In this way, you can build your adventure game on the basic framework provided by the run-time | |
| package. For example, the run-time package supplies a method for the message <code>leave</code>, which allows an actor | |
| to leave a location.</p> | |
| <div style="width:100%;border:black solid;border-width:3px 1px;padding:5px;"> | |
| <caption><b>Table 2:</b> <i>The functions included in the basic run-time package for the AdvSys adventure writing | |
| system, OBJECTS.ADI.</i></caption> | |
| <table cellspacing="0" style="width:100%"> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td class="clshdr" colspan="2">BASIC-THING</td> | |
| <td rowspan="37" style="border-right:solid black 1px;width:5px;" /> | |
| <td rowspan="37" style="width:5px;" /> | |
| <td class="clshdr">THING</td><td class="clshdr">Things that can be taken.</td></tr> | |
| <tr><td colspan="2">superclass:</td><td colspan="2">superclass:</td></tr> | |
| <tr><td class="prop">object</td><td /><td class="prop">basic-thing</td></tr> | |
| <tr><td class="prophdr" colspan="2">properties:</td><td class="prophdr" colspan="2">properties:</td></tr> | |
| <tr><td class="prop">initial-location</td><td>Initial location of the object.</td> | |
| <td class="prop">takeable</td><td>Can the thing be taken? (defaults to T)</td></tr> | |
| <tr><td class="prop">parent</td><td>Current location of the object.</td> | |
| <td class="prophdr" colspan="2">methods:</td> | |
| <tr><td class="prop">sibling</td><td>Next object in the location.</td> | |
| <td class="prop">(none)</td></tr> | |
| <tr><td class="prophdr" colspan="2">methods:</td></tr> | |
| <tr><td class="prop">(none)</td></tr> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td class="clshdr">ACTOR</td><td class="clshdr">The adventurer or non-player characters.</td> | |
| <td class="clshdr">STATIONARY-THING</td><td class="clshdr">Things that cannot be taken.</td></tr> | |
| <tr><td colspan="2">superclass:</td><td colspan="2">superclass:</td></tr> | |
| <tr><td class="prop">basic-thing</td><td /> | |
| <td class="prop">basic-thing</td></tr> | |
| <tr><td class="prophdr" colspan="2">properties:</td> | |
| <td class="prophdr" colspan="2">properties:</td></tr> | |
| <tr><td class="prop">child</td><td>First object carried by the actor.</td> | |
| <td class="prop" rowspan="3" style="vertical-align:top">(takeable)</td> | |
| <td rowspan="3" style="vertical-align:top">Can the thing be taken? (Because this<br />property is missing, GETP will return<br />NIL as its value.)</td></tr> | |
| <tr><td class="prophdr" colspan="2">methods:</td></tr> | |
| <tr><td class="prop">(move dir)</td><td>Move in the specified direction.</td></tr> | |
| <tr><td class="prop">(take obj)</td><td>Take an object.</td> | |
| <td class="prophdr" colspan="2">methods:</td></tr> | |
| <tr><td class="prop">(drop obj)</td><td>Drop an object.</td> | |
| <td class="prop">(none)</td></tr> | |
| <tr><td class="prop">(carrying? obj)</td><td>Is actor carrying the specified object?</td></tr> | |
| <tr><td class="prop">(inventory)</td><td>Show the actor's inventory.</td></tr> | |
| <tr><td style="height:0.5em" /></tr> | |
| <tr><td class="clshdr">PORTAL</td><td class="clshdr">Connections between locations.</td> | |
| <td class="clshdr">LOCATION</td><td class="clshdr">Locations in the adventure.</td></tr> | |
| <tr><td colspan="2">superclass:</td><td colspan="2">superclass:</td></tr> | |
| <tr><td class="prop">basic-thing</td><td /> | |
| <td class="prop">object</td></tr> | |
| <tr><td class="prophdr" colspan="2">properties:</td> | |
| <td class="prophdr" colspan="2">properties:</td></tr> | |
| <tr><td class="prop">other-side</td><td>Counterpart in the other location.</td> | |
| <td class="prop">description</td><td>Long description.</td></tr> | |
| <tr><td class="prop">closed</td><td>Is it closed?</td> | |
| <td class="prop">short-description</td><td>Short description.</td></tr> | |
| <tr><td class="prop">locked</td><td>Is it locked?</td> | |
| <td class="prop">visited</td><td>Has the player been here?</td></tr> | |
| <tr><td class="prop">key</td><td>Key to lock and unlock.</td> | |
| <td class="prop">child</td><td>First object in this location.</td></tr> | |
| <tr><td class="prophdr" colspan="2">methods:</td> | |
| <td class="prop">(exit directions)</td><td>Exits.</td></tr> | |
| <tr><td class="prop">(knock? obj)</td><td>Can this object enter?</td> | |
| <td class="prophdr" colspan="2">methods:</td></tr> | |
| <tr><td class="prop">(enter obj)</td><td>Cause this object to enter the location.</td> | |
| <td class="prop">(knock? obj)</td><td>Can this object enter?</td></tr> | |
| <tr><td class="prop">(open)</td><td>Open the portal.</td> | |
| <td class="prop">(enter obj)</td><td>Cause this object to enter the location.</td></tr> | |
| <tr><td class="prop">(close)</td><td>Close the portal.</td> | |
| <td class="prop">(leave obj dir)</td><td>Leave in the specified direction.</td></tr> | |
| <tr><td class="prop">(lock key)</td><td>Lock the portal.</td> | |
| <td class="prop">(describe)</td><td>Describe the location.</td></tr> | |
| <tr><td class="prop">(unlock key)</td><td>Unlock the portal.</td></tr> | |
| </table> | |
| </div> | |
| <h3>Associated Definitions</h3> | |
| <p>The following method definition could be associated with a particular location and would require the actor | |
| to be carrying the “rusty key” in order to leave the location:</p> | |
| <pre>(method (leave obj dir) | |
| (if (send obj carrying? rusty-key) | |
| (send-super leave obj dir) | |
| (print "You seem to be missing | |
| something!\n")))</pre> | |
| <p>This example also illustrates the use of the <code>send-super</code> form. Send-super passes a message to the parent | |
| (or super) class of the current object. This definition says that if the actor (the value of <code>obj</code>) is | |
| carrying the rusty key, the <code>leave</code> message should be handled normally. If not, the program prints a message | |
| and the action is aborted.</p> | |
| <p>The adventure language statements used to define constants, functions, variables, and property-names are:</p> | |
| <pre>(define symbol value) | |
| (define (function-name symbol*) expression*) | |
| (variable symbol*) | |
| (property symbol*)</pre> | |
| <h3>Summary</h3> | |
| <p>AdvSys is a tool for writing adventure games, much as a word processor is a tool for writing novels. It is not | |
| a substitute for good creative writing, but a tool for the writer. I hope the availability of this system will | |
| inspire potential adventure authors to write adventure games and share them with the rest of us. (See the | |
| article “Interactive Fiction as Literature,” which begins on <a href="byte87_buckles.html">this page</a>.) | |
| <span style="font-family:Webdings"><</span></p> | |
| <p style="margin-top:2em">Editor's note: <i>The source code for AdvSys, the adventure game writing system, | |
| was written in C and includes the following files: ADVCOM, the adventure game compiler; | |
| ADVINT, the adventure game interpreter; OBJECTS.ADI, a run-time package containing the basic definitions | |
| needed for a game; and the AdvSys documentation.</i></p> | |
| <p><i>The files are available on disk, in print, and on BIX. See the insert card following page 324 for details. | |
| Listings are also available on BTYEnet. See page 4. In order to run the programs, you will need a C compiler | |
| appropriate for your computer system.</i></p> | |
| <hr style="margin-bottom:0" /> | |
| <p style="margin-top:0.25em"><i>David Betz is a BIX senior editor. He can be reached at BIX, One Phoenix Mill Lane, | |
| Peterborough, NH 03458.</i></p> | |
| <p style="text-align:right"><small>MAY 1987 • B Y T E <b>125–135</b></small></p> | |
| </body> | |
| </html> | |
Xet Storage Details
- Size:
- 36.2 kB
- Xet hash:
- 647f71a8b68ff52b243c3bd7b636c347e796af690392d9888b82d8f4e3c797c5
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.