Parser Summary and Quick Reference ------------------------------------------------------------ This file is part of the TADS Author's Manual. Copyright (c) 1987 - 2001 by Michael J. Roberts. All rights reserved. Edited by N. K. Guy, tela design. ------------------------------------------------------------ Chapter Seven ------------------------------------------------------------ Parser Summary and Quick Reference This section summarizes the entire sequence of function and method calls that the parser makes in the course of parsing and executing a command. Reading a Command Line The first thing the parser does at the start of each turn is read a new command line. commandPrompt(0); // the parser next reads a line of input from the keyboard commandAfterRead(0); If commandPrompt() isn't defined, the parser simply displays its default prompt, a blank line followed by a greater-than sign (">"). If commandAfterRead() isn't defined, the parser does nothing extra. preparse After reading the command line, the parser invokes preparse(), if defined. preparse(commandString); Parsing Next, the parser breaks the command into words, then divides the command into sentences if multiple sentences, separated with "then", periods, semicolons, or other unambiguous sentence separators, are present. The parser then parses and executes each sentence individually. The first step in parsing a sentence is checking for an actor. If the sentence starts with a noun phrase followed by a comma, the parser takes the noun phrase to refer to an actor. After reading the noun phrase specifying the actor, the parser finds the other parts of the command: the verb and its associated preposition (for example, the parser considers the word "up" in "pick up" to be the verb preposition, because it's effectively part of the verb); the direct object; the preposition introducing the indirect object (for example, the word "on" in "put box on shelf"); and the indirect object. Apart from the verb, all of these parts are optional. Throughout this parsing phase, the parser invokes the game-defined parseNounPhrase(), if present, to allow the game to perform custom noun phrase parsing. parseNounPhrase(wordlist, typelist, currentIndex, complainOnNoMatch, isActorCheck); If the parser cannot identify a suitable verb, or if the sentence structure does not match a syntax pattern that the parser recognizes, the parser invokes the game-defined parseUnknownVerb() function, if defined. parseUnknownVerb(actor, wordlist, typelist, errnum); Execution Sequence Once the parser has finished parsing the command, the parser executes the command. On a command line with more than one command, the parser executes the first command before starting to parse the second. To execute the command, the parser first invokes the preparseCmd() function, if defined by the game, passing a list of the strings making up the tokens of the command. preparseCmd(wordlist); Next, the parser identifies the deepverb object for the command's verb. Once the verb is identified, the parser invokes the roomCheck method in the current player character's Actor object. parserGetMe().roomCheck(verb); The parser now resolves and disambiguates any noun phrases in the command. (Note that the noun phrases were already parsed earlier, during the parsing phase; at this point, the parser resolves the words of the noun phrases to the game objects to which they refer.) To perform disambiguation, the parser invokes validDoList, validIoList, validDo, validIo, and the verification methods, verIoVerb (for an indirect object) and verDoVerb (for a direct object). If a noun phrase involving "all" was used, the parser will invoke the doDefault or ioDefault methods in the deepverb object to obtain the list of all available objects. The parser will also invoke the methods disambigDobj and disambigIobj, as appropriate, on the deepverb object. verb.disambigDobj(actor, prep, iobj, verprop, wordlist, objlist, flaglist, numberWanted, isAmbiguous, silent); verb.disambigIobj(actor, prep, dobj, verprop, wordlist, objlist, flaglist, numberWanted, isAmbiguous, silent); The parser next identifies the verb template (i.e., the verification and action methods for the verb). If the verb has only templates for more objects than the player provided, the parser attempts to obtain the missing objects. (For example, the verb "take" requires a direct object; if the player types "take" without any object, the parser will try to obtain a direct object.) First, the parser tries using the default object mechanism, by calling the doDefault or ioDefault method in the deepverb object. If this fails to supply exactly one default object, the parser asks the player to provide the missing object. Next, if the player's command used multiple direct objects, or used a noun phrase involving "all" for the direct object, the parser will invoke the rejectMultiDobj method in the deepverb object. verb.rejectMultiDobj(preposition); Starting Execution The parser next invokes the preCommand function, if defined: preCommand(actor, verb, dobj_list, prep, iobj) This function can simply return to proceed with the command, or it can use exit to skip to the fuses and daemons, or it can use abort to skip directly to endCommand, bypassing fuses and daemons. Direct Object Loop The parser now executes a series of method calls on the direct objects in the command. For a command with multiple direct objects, the parser executes all of the steps below on the first direct object, then executes all of the steps on the second direct object, and so on. If the current direct object is not the first in the command, the parser at this point re-validates the current direct object and indirect object. This re-validation is not necessary for the first direct object in the command, because the object resolution phase already did this for all of the objects, and nothing in the game state has changed since then, hence that earlier validation is still applicable. However, the game state might change in the course of executing the command for the first direct object, so the parser must re-validate all subsequent objects. To re-validate, the parser calls the deepverb object's validDo method for the direct object, and the validIo method for the indirect object. If validation fails, the parser displays an error (using the cantReach method if the object is visible, otherwise using parser error 38, "You don't see that here any more") and aborts the command. verb.validDo(actor, directObject, 1) verb.validIo(actor, indirectObject, 1) The parser now executes the methods calls below for the current direct and indirect object. If one of the methods below executes an abort statement, the parser terminates execution of the command entirely. If one of the methods executes an exit statement, the parser skips directly to the postAction function, then to the end-of-turn phase, then proceeds to parse and execute any remaining command on the command line. If one of the methods invokes exitobj, the parser skips the remaining steps for the current direct object only, calls postAction, then proceeds to execute all of the steps below on the next direct object in the current command. verb.verbAction(actor, directObject, preposition, indirectObject); actor.actorAction(verb, directObject, preposition, indirectObject); actor.location.roomAction(actor, verb, directObject, preposition, indirectObject); if (indirectObject != nil) { indirectObject.iobjCheck(actor, verb, directObject, preposition); if (indirectObject does not directly define ioVerb or verIoVerb) indirectObject.iobjGen(actor, verb, directObject, preposition); } if (directObject != nil) { directObject.iobjCheck(actor, verb, indirectObject, preposition); if (directObject does not directly define doVerb or verDoVerb) directObject.iobjGen(actor, verb, indirectObject, preposition); } if (directObject != nil && indirectObject != nil) { // if directObject doesn't define or inherit verDoVerb, // or if indirectObject doesn't define or inherit verIoVerb, // display an error ("I don't know how to...") and stop now if (verb has flag [disambigDobjFirst]) directObject.verDoVerb(actor); else directObject.verDoVerb(actor, indirectObject); // stop now if any output occurred in verDoVerb if (verb has flag [disambigDobjFirst]) indirectObject.verIoVerb(actor, directObject); else indirectObject.verIoVerb(actor); // stop now if any output occurred in verIoVerb indirectObject.ioVerb(actor, directObject); } else if (directObject != nil) { // if directObject doesn't define or inherit verDoVerb, // display an error ("I don't know how to...") and stop now directObject.verDoVerb(actor); // stop now if any output occurred in verDoVerb directObject.doVerb(actor); } else { verb.action(actor); } Regardless of how the above sequence finishes (by successfully execution of all methods, or by execution of exit, exitobj, or abort), the parser next invokes the game-defined function postAction: postAction(actor, verb, dobj, prep, iobj, status); The "status" code indicates how the command was terminated: +------------+--------------------------------------+ | EC_SUCCESS | Successful completion of all methods | +------------+--------------------------------------+ | EC_EXIT | exit executed | +------------+--------------------------------------+ | EC_EXITOBJ | exitobj executed | +------------+--------------------------------------+ | EC_ABORT | abort executed | +------------+--------------------------------------+ Fuses and Daemons If none of the methods that the parser invoked so far executed an abort statement, the parser executes all pending fuses and daemons. Each daemon is executed once, and each fuse whose timer has reached zero is executed and then deleted. endCommand Finally, the parser invokes the game-defined function endCommand(): endCommand(actor, verb, dobj_list, prep, iobj, status); The "status" parameter has the same meaning as it does in the parser's call to postAction(). The other parameters have the same values they did in the call to preCommand() at the start of the command. The parser invokes endCommand() for every command that enters the execution phase (in other words, for every command that does not end with an error prior to execution, such as in syntax analysis or noun-phrase resolution). The parser calls endCommand() even when the execution phase is terminated with exit, exitobj, or abort. ------------------------------------------------------------ Summary of Properties, Methods, Objects, and Functions As described throughout this manual, the parser calls certain methods that objects in your game can define, and calls certain functions that your game can provide. All of these methods and functions are described in detail elsewhere in the manual. This section summarizes these properties and function; for full information, refer to the appropriate section elsewhere in the manual. ------------------------------------------------------------ Properties and Methods -------------------------------------------------- *>action(actor) Called in a deepverb object during command execution to execute a command with no objects. -------------------------------------------------- *>actorAction(verb, dobj, prep, iobj) Called in the actor object during command execution. -------------------------------------------------- *>adesc Used to display an object's short description with an indefinite article. -------------------------------------------------- *>adjective Defines words as adjectives, and associates them with the object. -------------------------------------------------- *>anyvalue(num) The parser calls this method on an object with a generic numeric adjective (adjective = '#') when the player uses "any" in a noun phrase involving the object. This method returns a number to use for the arbitrary choice the parser must make. -------------------------------------------------- *>article Defines words as articles. -------------------------------------------------- *>cantReach(actor) Called in an object (only if cantReach isn't defined in the verb) when the object is visible but not accessible; this method is meant to display an explanation of this condition. -------------------------------------------------- *>cantReach(actor, dolist, iolist, prep) Called in a verb when the list of objects (only one of dolist or iolist will be non-nil) is visible but not accessible; this method is meant to display an explanation. -------------------------------------------------- *>construct Called immediately after an object is created with the new operator. Minimally, this method should install the object into its container's contents list. -------------------------------------------------- *>contents The list of an object's contents. -------------------------------------------------- *>destruct Called immediately before an object is destroyed with the delete operator. Minimally, this method should remove the object from its container's contents list, and should make sure that the object is not referenced from any other lists or properties. -------------------------------------------------- *>disambigDobj(actor, prep, iobj, verprop, wordlist, objlist, flaglist, numberWanted, isAmbiguous, silent) The parser calls this method in the deepverb object to resolve an ordinary noun phrase used as a direct object. The parser calls this method for every ordinary noun phrase used as a direct object, whether or not the noun phrase is actually ambiguous. -------------------------------------------------- *>disambigIobj(actor, prep, dobj, verprop, wordlist, objlist, flaglist, numberWanted, isAmbiguous, silent) The parser calls this method in the deepverb object to resolve an ordinary noun phrase used as an indirect object. The parser calls this method for every ordinary noun phrase used as an indirect object, whether or not the noun phrase is actually ambiguous. -------------------------------------------------- *>doAction Direct Object Action; associated with a deepverb object. Defines the verb template for a single-object command for a verb, and specifies the suffix for to be used for the action and verification methods for the verb (doSuffix and verDoSuffix, respectively). -------------------------------------------------- *>dobjCheck(actor, verb, iobj, prep) General direct object check, called during command execution before dobjGen would be called. -------------------------------------------------- *>dobjGen(actor, verb, iobj, prep) General direct object handler, called during command execution on the direct object prior to processing the verb's verification and action methods. The parser does not call this method if the object "overrides" it by explicitly defining, rather than merely inheriting, a verification method (verDoVerb) or action method (doVerb) for the verb. -------------------------------------------------- *>doDefault(actor, prep, iobj) Direct Object Default. Associated with a deepverb object. Returns the list of default objects for the verb. -------------------------------------------------- *>ioAction(prep) Indirect Object Action; associated with a deepverb object. Defines the verb template for a two-object command for a verb with the given preposition, and specifies the suffix for to be used for the action and verification methods for the verb (io Suffix and verIoSuffix, respectively). -------------------------------------------------- *>iobjCheck(actor, verb, iobj, prep) General indirect object check, called during command execution just before iobjGen would be called. -------------------------------------------------- *>iobjGen(actor, verb, dobj, prep) General indirect object handler, called during command execution on the indirect object prior to processing the verb's verification and action methods. The parser does not call this method if the object "overrides" it by explicitly defining, rather than merely inheriting, a verification method (verIoVerb) or action method (ioVerb) for the verb. -------------------------------------------------- *>ioDefault(actor, prep) Indirect Object Default; associated with a deepverb object. Returns the list of default objects for the verb. -------------------------------------------------- *>isEquivalent Used to determine if an object should be treated as equivalent to other objects of its immediate superclass. If this property is true, the parser treats all objects of the same immediate superclass as this object as interchangeable during disambiguation. -------------------------------------------------- *>isHim If true, the object can be the antecedant for the pronoun "him." This also affects certain default messages that adv.t displays for the object. -------------------------------------------------- *>isHer If true, the object can be the antecedant for the pronoun "her." This also affects certain default messages that adv.t displays for the object. -------------------------------------------------- *>isThem Added: TADS 2.5.2 If true, the object can be the antecedant for the pronoun "them." This also affects certain default messages that adv.t displays for the object. In other words, the parser recognizes the isThem property for the purposes of deciding on the pronoun to display when prompting for an indirect object. Prior to version 2.5.2, the parser only recognized the isHim and isHer properties, and used the pronoun "it" if neither of these properties were consistently defined for the objects matching the direct object. As of 2.5.2 the parser uses the pronoun "them" if the matching objects all have isThem set to true. -------------------------------------------------- *>isVisible(vantage) Used to determine if an object is visible from the given object. The parser uses this to determine how to complain when an object isn't accessible: if it's visible, but not accessible, the parser will use cantReach to complain, otherwise it will simply say "I can't see that here." -------------------------------------------------- *>location The object which contains the object. -------------------------------------------------- *>locationOK If this property is true, it tells the compiler that you know that the location property is not an object constant, and the compiler therefore suppresses the warning it would otherwise generate. -------------------------------------------------- *>multisdesc Used by the parser to display the current object when iterating through a list of direct objects in a command. Note that the parser will not call this method if the object defines or inherits a prefixdesc method. -------------------------------------------------- *>newNumbered(actor, verb, num) The parser calls this method on an object with a generic numeric adjective (adjective = '#') when the player uses the object in a noun phrase. This method returns the object to use as the resolution of the noun phrase; num gives the number that the player used in the noun phrase (for example, in "look in box 55", num will be the number 55). -------------------------------------------------- *>nilPrep Associated with a deepverb object; used by the parser to determine the preposition to use when a command of the form VERB IOBJ DOBJ is entered. If undefined, the preposition object associated with the word "to" is used by default. -------------------------------------------------- *>noun Defines words as nouns, and associates them with the object. -------------------------------------------------- *>parseUnknownDobj(actor, prep, iobj, wordlist) The parser calls this method to resolve a noun phrase (the token strings of which are given by wordlist) which contains an unknown word to a direct object. This method is invoked on the deepverb object for the command's verb. -------------------------------------------------- *>parseUnknownIobj(actor, prep, iobj, wordlist) The parser calls this method to resolve a noun phrase (the token strings of which are given by wordlist) which contains an unknown word to an indirect object. This method is invoked on the deepverb object for the command's verb. -------------------------------------------------- *>plural Defines words as plurals, and associates them with the object. -------------------------------------------------- *>pluraldesc Displays a description of the object in the plural. The parser, and certain utility functions in adv.t, use this to display a collective name for a group of indistinguishable objects ("five gold coins"). -------------------------------------------------- *>preferredActor Used by the parser to determine if an object is a "preferred" actor; if this property is true, the object is chosen as the actor in cases where the actor is ambiguous and none of the other objects are preferred actors. -------------------------------------------------- *>prefixdesc(show, current_index, count, multi_flags) Called by the parser before the execution phase for each direct object in a command, to display a prefix for the object before its execution results are displayed. The parser calls this method for each direct object in a command, regardless of whether the command has a single direct object or multiple direct objects. The parser calls this method in preference to multisdesc. -------------------------------------------------- *>prepDefault Associated with a deepverb object. The parser uses this property to find the default preposition object to use when the player enters a partial command that starts with the verb and a direct object, but omits the preposition and indirect object. -------------------------------------------------- *>preposition Defines words as prepositions, and associates them with the object. -------------------------------------------------- *>rejectMultiDobj(prep) Associated with a deepverb object. If this method returns true, the verb doesn't allow multiple direct objects or "all." -------------------------------------------------- *>roomAction(actor, verb, dobj, prep, iobj) Called in the actor's location during command execution. -------------------------------------------------- *>roomCheck(verb) Called in the actor at the beginning of execution, before object disambiguation. -------------------------------------------------- *>sdesc Short description. Used by the parser to display an object's name. -------------------------------------------------- *>statusLine Called in the actor's location to update the statue line. -------------------------------------------------- *>thedesc Displays the object's name with a definite article. -------------------------------------------------- *>validActor Called by the parser to determine if the object is valid as an actor. This method doesn't check to see if the object is logical to use as an actor, but simply if the object is accessible as the target of a command. -------------------------------------------------- *>validDo(actor, obj, seqno) Associated with a deepverb object. Returns true if the object is accessible as a direct object for the verb. -------------------------------------------------- *>validDoList(actor, prep, iobj) Associated with a deepverb object. Returns a list of possibly-valid direct objects for the verb. The objects returned will be tested for validity with validDo, so the list can contain invalid objects as well, but it must contain all of the valid direct objects for the verb. -------------------------------------------------- *>validIo(actor, obj, seqno) Associated with a deepverb object. Returns true if the object is accessible as an indirect object for the verb. -------------------------------------------------- *>validIoList(actor, prep, dobj) Associated with a deepverb object. Returns a list of possibly-valid indirect objects for the verb. The objects returned will be tested for validity with validIo, so the list can contain invalid objects as well, but it must contain all of the valid indirect objects for the verb. -------------------------------------------------- *>value The parser sets this property of the strObj or numObj object to the actual value of the string or number (respectively) entered in a command. -------------------------------------------------- *>verb Defines words as verbs, and associates them with the object. Strings in this list can consist of one or two words; the second word must also be separately defined as a preposition. -------------------------------------------------- *>verbAction(actor, dobj, prep, iobj) The parser calls this method on the deepverb object for the command, just before calling the actorAction method on the command's actor. ------------------------------------------------------------ Objects -------------------------------------------------- *>againVerb The deepverb object with the vocabulary for "again." The parser internally handles the "again" processing when this verb's vocabulary word is entered as the command. -------------------------------------------------- *>Me The default player actor object. When a command is not explicitly directed to another actor, this object is the actor to which the command is sent. When the game is first loaded, the parser uses Me as the default player character, but the game can change this using the parserSetMe() built-in function. -------------------------------------------------- *>numObj A special object which is used as the direct object when a number is entered in a command (such as in "enter 30 on calculator," in which numObj is the direct object). The value property is set to the number actually entered in the command. -------------------------------------------------- *>strObj A special object which is used as the direct object when a quoted string is entered in a command (such as in 'type "hello" on keyboard,' in which strObj is the direct object). The value property is set to the string actually entered in the command. -------------------------------------------------- *>takeVerb Formerly required, but no longer directly used by the parser. ------------------------------------------------------------ Functions -------------------------------------------------- *>commandPrompt(type) The parser calls this function, if defined, to display the prompt before asking for a command. The type code specifies the type of input being requested: 0 is a normal command; 1 is a command entered after the parser complains about an unknown word; 2 is a command entered in response to a disambiguation question; 3 is a command entered when the parser asks for a direct object; and 4 is a command entered when the parser asks for an indirect object. -------------------------------------------------- *>commandAfterRead(type) The parser calls this function, if defined, immediately after the player finishes entering each command line. This function can reverse any text effects that commandPrompt established just before the command line. The type code has the same value as in the preceding commandPrompt call. This new function is intended to make it easier to customize certain aspects of your game's user interface. In particular, if you're using special formatting for the player command in the HTML interpreter (for example, you want to use a special typeface or font color for player commands), you can use commandAfterRead to turn off the special formatting you turned on in commandPrompt. Each call to commandPrompt is matched by an equivalent call to commandAfterRead, so you can always count on being able to undo any formatting changes you make in commandPrompt by placing the matching formatting commands in commandAfterRead. -------------------------------------------------- *>endCommand(actor, verb, dobj_list, prep, iobj, status) The parser invokes this function, if defined, at the end of the command, after executing daemons and fuses. -------------------------------------------------- *>init The system calls this immediately after loading the game, and again whenever the game is restarted. This function can perform any startup operations, such as setting fuses and daemons, and displaying an introductory message. Note that the system does not call init() when the player starts the TADS interpreter and specifies on the command line (or local equivalent) a saved game position to restore; in this case, the system instead calls initRestore(). (However, if initRestore() isn't defined, the system does call init() in all cases.) -------------------------------------------------- *>initRestore(savedGameFileName) The system calls this function when the player starts the TADS interpreter and specifies a game to restore on the command line (or local equivalent). The purpose of this function is to allow the game to skip any introductory text that init() would normally display when starting a new game; since the player is explicitly jumping to a saved position, the player presumably doesn't need to see the introductory text again. If this function isn't defined, the system simply calls init() as though the game had been started without a saved game file. -------------------------------------------------- *>pardon The parser calls this function whenever the player enters an empty command. You can display an error message, if you wish. -------------------------------------------------- *>parseAskobj(verb, ...) The parser calls this function, if defined, to prompt for a missing direct or indirect object. If parseAskobjActor is defined, parseAskobj is ignored. If two arguments are provided, an indirect object is being requested, and the second argument is a preposition (which may be nil); otherwise, a direct object is being requested. -------------------------------------------------- *>parseAskobjActor(actor, verb, ...) The parser calls this function, if defined, to prompt for a missing direct or indirect object. If three arguments are provided, an indirect object is being requested, and the third argument is a preposition (which may be nil); otherwise, a direct object is being requested. -------------------------------------------------- *>parseAskobjIndirect(actor, verb, prep, objectList) The parser calls this function, if defined, to prompt for a missing indirect object. This is the parser's first choice when prompting for an indirect object; the parser calls this function in preference to parseAskobjActor() or parseAskobj(). -------------------------------------------------- *>parseDefault(obj, prep) The parser calls this function, if defined, to display a message indicating that the object (and the preposition, if not nil) are being used by default. -------------------------------------------------- *>parseDisambig(nameString, objList) The parser calls this function, if defined, to prompt for more information when the player enters an ambiguous object name. The nameString is the string of words entered by the player (given as a single-quoted string), and the objList is a list of the objects matching the name. -------------------------------------------------- *>parseError(num, str) The parser calls this function, if defined, to get the text of an error message. num is the message number, and str is the text of the message that the parser would display by default if this function weren't defined. Note that the parser never calls this function if your game defines a parseErrorParam function. -------------------------------------------------- *>parseErrorParam(num, str, ...) The parser calls this function, if defined, to get the text of an error message. num is the message number, and str is the text of the message that the parser would display by default if this function weren't defined. Additional parameters are specify the values for any "%s" (string) or "%d" (number) sequences in the default message string. -------------------------------------------------- *>parseError2(verb, dobj, prep, iobj) The parser calls this function, if defined, to display a message indicating that the verb can't be applied to these objects (because one of the objects doesn't define or inherit a verification method for the verb). -------------------------------------------------- *>parseNounPhrase(wordlist, typelist, currentIndex, complainOnNoMatch, isActorCheck) The parser calls this function, if defined, to parse each noun phrase. This function can do its own noun phrase parsing for custom noun phrase syntax, or can defer to the standard built-in noun phrase parser. -------------------------------------------------- *>parseUnknownVerb(actor, wordlist, typelist, errnum) The parser calls this function, if defined, to parse a sentence that the parser doesn't recognize. The parser invokes this function if the command does not have a valid verb, or if the sentence does not fit a syntax pattern that the parser accepts. -------------------------------------------------- *>preCommand(actor, verb, dobj_list, prep, iobj) The parser invokes this function, if defined, at the start of the execution phase. -------------------------------------------------- *>postAction(actor, verb, dobj, prep, iobj, status) The parser invokes this function, if defined, at the end of the execution phase. The parser calls this function once for each direct object in a command with multiple direct objects. The parser invokes this function even when the execution phase terminates with exit, exitobj, or abort. -------------------------------------------------- *>preinit Called by the compiler, after compilation is complete, to perform any desired initialization. This function can't set any fuses or daemons, or display any messages; these functions are not saved in the compiled game file, so they must wait until the init function, which is invoked at run-time. The preinit function is intended to perform any time-consuming computation - for example, setting up lists of objects or initializing property values - that would otherwise be done in init every time the game is started. By performing these functions in preinit, the work can be done at compile-time, saving players from having to wait for it each time they run the game. Note that this function won't be called until run-time if the game is compiled for debugging; while this doesn't affect the behavior of the game (apart from slowing down run-time startup), it allows you to use the debugger to step through your preinit function. -------------------------------------------------- *>preparse(cmd) The parser calls this function with a (single-quoted) string giving the full text of the command line each time the player enters a new command. -------------------------------------------------- *>preparseCmd(wordList) The parser calls this function with a list of (single-quoted) strings giving the words of the current command. This function is called for each individual command on a command line. ------------------------------------------------------------ Language Reference ------------------------------------------------------------ This file is part of the TADS Author's Manual. Copyright (c) 1987 - 2001 by Michael J. Roberts. All rights reserved. Edited by N. K. Guy, tela design. ------------------------------------------------------------ Chapter Eight ------------------------------------------------------------ Language Reference This section describes in detail all of the keywords, operators, syntactic elements, built-in functions, and special considerations of the TADS language. ------------------------------------------------------------ Typographical Conventions This section uses abstract syntax diagrams to describe the language. In these diagrams, several typographical conventions are used: italics are used to represent a description of an item that must be replaced with an actual instance of the item. [Square Brackets] are used to indicate that the enclosed item or items are optional. Typewriter text is to be used verbatim. The same applies to any other punctuation shown. ------------------------------------------------------------ Version Information TADS has gained many new functions and features over the years, but tracking what features were available with which version of the software was problematic. Since the release of version 2.2.4, however, TADS has had the ability to check to see what version of the interpreter (runtime) software is being used. For that reason all new functions and features added with 2.2.4 and higher are marked in this documentation. If no version number is listed you can assume that the item in question was available to all versions of TADS prior to 2.2.4. ------------------------------------------------------------ Components of TADS The TADS development system includes a compiler, a run-time system or interpreter, and several TADS source files containing basic adventure definitions. When you write a TADS program, you use a text editor (which is not included with TADS) to prepare a source file, then you compile the file. The compiler checks the entire program for syntax errors, then creates another file containing a binary representation of your source file; this form is more efficient to execute, so your game will require less memory and run faster. After you have compiled your program, you use the interpreter to run the game. The interpreter contains the player command parser and other code for user interaction. ------------------------------------------------------------ Input File Format The TADS compiler accepts plain text (standard ASCII) files as input. The compiler considers any amount of whitespace to be the same as a single space, and spaces, tabs, and newlines all count as whitespace. Whitespace can occur between any two syntactic elements, but need only appear between two identifiers or keywords that have no other intervening punctuation. The compiler knows that punctuation is never part of a keyword or identifier, and can manage to break apart punctuation when it's all run together without spaces. For readability, liberal use of whitespace is recommended. ------------------------------------------------------------ Including Other Files The #include directive allows one file to insert the contents of another: #include "file.t" #include Here, file.t is the name of an operating system file to be included. An included file can include another, and so forth, down to ten levels of nested files. Note that the pound sign, #, must be in the first column of the line; no leading spaces are allowed. The include file adv.t\ is normally included at the start of a game file to define the many items that are needed in a normal adventure. These definitions are quite general, but specific games might modify them to customize the game. Note that when the filename is enclosed in double quotes, the compiler searches for the file first in the current directory, then in the directories in the include path (as set with the -i compiler switch on most operating systems). When the filename is enclosed in angle brackets, the compiler searches only in the directories in the include path - the current directory is not searched. Generally, a system include file (such as adv.t) should be enclosed in angle brackets, while your files should be in the current directory and enclosed in double quotes. Note also that the #include directive cannot be preceded by a space character. The # symbol must be the first character on the line. If you copy and paste sample code from a Web browser window be sure to check for this common problem, or else your code simply will not compile. ------------------------------------------------------------ Multiple Inclusions of the Same File The TADS compiler automatically keeps track of each file you have included, and ignores redundant #include directives. Note that this feature is based on the filename as specified in your program; if you refer to the same file by different names (for example, by specifying a path on one inclusion, but using angle brackets to let TADS find the file on the second), the compiler will not be able to identify the duplicate file, and will include it twice, resulting in countless unnecessary errors. For this reason, you are encouraged to use angle brackets to specify include files, rather than using specific paths in your source file. Doing so will help ensure that your source files are portable to different computers, as well, since each operating system uses its own conventions for specifying file paths. Note that the include files that make up a pre-compiled binary file, loaded with the compiler's -l option, are recorded in the binary file. Thus, there is no need for you to remove the #include directive in your source file just because you are using the pre-compiled version of that include file. For more information on using pre-compiled include files, see the section on using the compiler. ------------------------------------------------------------ Comments Outside of a quoted string, two consecutive slashes, //, indicate that the rest of the line is a comment. Everything up to the next newline is ignored. Alternatively, C-style comments can be used; these start with /* and end with */; this type of comment can span multiple lines. Examples: // This line is a comment. /* This is a comment which goes across several lines. */ ------------------------------------------------------------ Identifiers Identifiers must start with a letter (upper or lower case), and may contain letters, numbers, dollar signs, and underscores. Identifiers can be up to 39 characters long. Upper and lower case letters are distinct. ------------------------------------------------------------ Scope of Identifiers All objects and functions are named by global identifiers. No identifier may be used to identify different things; that is, no two objects can have the same name, an identifier naming a function can't also be used for an object, and so forth. Property names are also global identifiers. A name used for a property can't be used for a function or object, or vice versa. However, unlike functions and objects, the same property name can be used in many different objects. Since a property name is never used alone, but always in conjunction with an object, the TADS compiler is able to determine which object's property is being referenced even if the same name is used in many objects. Function arguments and local variables are visible only in the function in which they appear. It is permissible to re-use a global identifier as a function argument or local variable, in which case the variable supersedes the global meaning within the function. However, this is discouraged, as it can be a bit confusing. ------------------------------------------------------------ Object Definitions A basic object definition has the form: identifer: object [property-list] ; This defines an object which has no superclass. An object can be defined as having a superclass with the alternative form of object definition: identifier: class-name [, class-name [...]] [property-list] ; Here, the class-name is an identifier which is defined elsewhere in the program as an object or class (either with or without a superclass), and is the new object's superclass; if more than one superclass is present, a comma separates each superclass in the list. The new object named by identifier inherits all the properties of the superclass or superclasses. If a property in the optional property-list is also in the property list of the superclass (or its superclass, and so forth), the new property overrides the inherited one. If a property is inherited from more than one of its superclasses (and is not overridden in the object's own property list), the property is inherited from the superclass that appears earliest in the list. For example, suppose you define an object like this: vase: container, fixeditem ; If both container and fixeditem define a method named m1, and vase itself doesn't define an m1 method, then m1 is inherited from container, because it appears earlier in the superclass list than fixeditem. There is a more complicated case that can occur, but it is very unusual. You will probably never encounter this, so skip this section if you find it confusing. Suppose that in the example above, both container and fixeditem have the superclass item, and that item and fixeditem define method m2, and that neither container nor vase define m2. Now, since container inherits m2 from item, it might seem that vase should inherit m2 from container and thus from item. However, this is not the case; since the m2 defined in fixeditem overrides the one defined in item, vase inherits the m2 from fixeditem rather than the one from item. Hence, the rule, fully stated, is: the inherited property in the case of multiple inheritance is that property of the earliest (leftmost) superclass in the object's superclass list that is not overridden by a subsequent superclass. ------------------------------------------------------------ Property Lists A property list takes on this form: property-definition [property-list] (This is a formal way of saying that you can string together any number of property definitions in a property list, one after the other.) A property definition looks like this: identifier = data-item Note that no semicolon comes after the property definition. A semicolon terminates the entire object definition, not individual property definitions. ------------------------------------------------------------ Property Data Items A data item can be a number, a double-quoted string, a single-quoted string, a list, an object, nil, or true. ------------------------------------------------------------ Numbers A number is just a string of digits. The default base is decimal; you can also enter octal and hexadecimal numbers. An octal number simply starts with a leading zero, so 035 is an octal number having the decimal value 29. A hexadecimal number starts with 0x, as in 0x3a9. Numbers can vary from -2147483647 to 2147483647 (decimal), inclusive. Only integers are allowed; numbers cannot have a decimal point or a fractional part. ------------------------------------------------------------ Double-quoted Strings A double-quoted string is an arbitrary string of characters enclosed in double quotation marks, such as, "This is a double-quoted string." Stretches of whitespace in such strings are compressed to single spaces. This includes newlines; double-quoted strings can go on past the end of a line, and keep going for several lines. For example, this is perfectly legal: "This is a string that goes on for several lines." Note that TADS converts all of that blank space, including the blank lines, down to a single space between each word. Thus, you can enter your text without having to worry about formatting it; TADS will do all the work of filling up each output line when your program runs. Sometimes, you will want to format your output in a specific way, overriding the standard output formatting. Since TADS converts all whitespace in your strings (including newlines) to spaces, you have to specify any special formatting you want explicitly. TADS provides several special character sequences you can use to obtain these effects. +--------+--------------------------------------------+ | \t | Tab to the next stop. Tab stops are every | | | four spaces. This is useful if you want | | | stretches of blank space within a line. | | | The HTML-TADS equivalent is the tag . | +--------+--------------------------------------------+ | \n | A newline (carriage return). Ends the | | | current line, and skips to a new one. | | | Note that repeating this sequence has no | | | effect, since the output formatter | | | ignores redundant \n sequences. This is | | | normally convenient, because it means you | | | don't have to worry about several newline | | | sequences being displayed by different | | | objects filling the display with unwanted | | | blank lines. If you want to force one or | | | more blank lines, use \b. The HTML-TADS | | | equivalent is the tag
. | +--------+--------------------------------------------+ | \b | Ends the current line, and then outputs a | | | blank line. Multiple \b sequences will | | | result in multiple blank lines. The | | | HTML-TADS equivalent is the tag

. | | | Almost. Multiple

s don't insert | | | multiple blank lines. | +--------+--------------------------------------------+ | \" | A double quotation mark. Note that this | | | is a neutral quotation mark. If you want | | | curved (typographical) quotation marks | | | you need to use HTML-TADS. | +--------+--------------------------------------------+ | \' | A single quotation mark. Note that this | | | is a neutral apostrophe. If you want | | | curved (typographical) apostrophes or | | | single quotes you need to use HTML-TADS. | +--------+--------------------------------------------+ | \\ | A backslash. | +--------+--------------------------------------------+ | \< | A left angle bracket. This is not | | | required to produce a single angle | | | bracket (although it will do so), but you | | | will need to use this on second and | | | subsequent angle brackets in a stream of | | | contiguous angle brackets, to prevent | | | TADS from thinking you want to embed an | | | expression in your string (see below). | | | Hence, if you want to display <<<<<, you | | | will have to type this in your program: | | | "<\<\<\<\<". Note also that is displayed | | | as a left angle bracket in the HTML TADS | | | system, and will not be interpreted as a | | | tag. | +--------+--------------------------------------------+ | \^ | (That is, a backslash followed by a | | | circumflex or "up arrow" or "hat," | | | entered with the shifted "6" key on most | | | keyboards.) This sequence causes the next | | | character in the output to be | | | capitalized. This sequence is generally | | | used just before invoking a function or | | | method that will display text that will | | | be at the start of a sentence. By using | | | this sequence prior to the text, you | | | ensure that the first letter of the | | | sentence is capitalized, even if the | | | function or method displaying the text | | | doesn't know it should be capitalized. | | | For example: "\^<< obj.adesc >> is | | | sitting on the table." Note that the | | | caps() built-in function has the same | | | effect. | +--------+--------------------------------------------+ | \v | This sequence is the opposite of "\^". | | | Whereas "\^" converts the next character | | | displayed to a capital letter, "\v" | | | converts the next character to a small | | | letter. Displaying "\v" is equivalent to | | | calling the nocaps() built-in function. | +--------+--------------------------------------------+ | \space | (That is, a backslash followed by a | | | space.) A quoted space. This is useful in | | | certain cases to achieve special | | | formatting effects, because it overrides | | | the output formatter's suppression of | | | multiple spaces, and it also suppresses | | | the double-space that sometimes follows | | | punctuation marks such as periods in | | | certain versions of the TADS interpreter. | | | For example, if you want to print | | | someone's name with a middle initial, you | | | don't want two spaces after the initial. | | | Since some versions of the runtime print | | | the two spaces automatically you would | | | thus enter: "Michael J.\ Roberts" to | | | ensure that the name is displayed | | | correctly with all versions of the | | | interpreter. Note also that some versions | | | of the TADS interpreter will print either | | | one or two spaces after a punctuation | | | mark, depending on a preferences setting. | +--------+--------------------------------------------+ | \( | Begin highlighting. Text after the \( is | | | displayed with a special attribute, which | | | varies by system. On some machines it | | | will appear in a different color than | | | ordinary text, while on others it may | | | appear in a boldfaced font. On some | | | systems it may have no effect at all. | | | This is intended to be used for | | | emphasizing text the same way that | | | boldface or italics would emphasize text | | | in a book. Note that highlighting does | | | not "nest"; that is, if a \( sequence | | | occurs while highlighting is already | | | active, the second sequence has no | | | additional effect. This functionality | | | predates the HTML TADS software, which | | | offers richer and more flexible | | | text-formatting options. | +--------+--------------------------------------------+ | \) | End highlighting. Text after the \) | | | without the special highlighting | | | attribute. Note that a single \) sequence | | | turns off highlighting, no matter how | | | many \( sequences preceded it. | +--------+--------------------------------------------+ | \- | Pass two bytes. The sequence is a | | | backslash followed by a hyphen, and tells | | | the formatter to pass the following two | | | bytes as-is, without any interpretation. | | | This is useful for multi-byte character | | | sets (such as on a Japanese-localized | | | Macintosh), where a single character may | | | be represented by two bytes. For the most | | | part, you can freely mix single- and | | | double-byte characters within text | | | strings without any special work. | | | However, some double-byte characters | | | contain the ASCII code for a backslash as | | | one of their two bytes; in these cases, | | | the output formatter incorrectly | | | interprets the byte whose code is "\" as | | | introducing an escape sequence. By | | | preceding such characters with "\-", you | | | can prevent the parser from interpreting | | | the byte with the backslash code as an | | | escape sequence, so the character will be | | | displayed correctly. | +--------+--------------------------------------------+ | \H+ | Turn on the HTML parser. This character | | | sequence is used to enable the HTML | | | functionality of HTML-TADS. For more | | | information consult the HTML-TADS | | | documentation. | +--------+--------------------------------------------+ | \H- | Turn off the HTML parser. | +--------+--------------------------------------------+ There is no specific limit on the length of a double-quote string, although the compiler may impose some limits depending on the context in which the string appears. A double-quoted string is always displayed whenever evaluated. When a double-quoted string appears in code, executing that code results in displaying the string. Likewise, when a property has a double-quoted string as its value, the string is displayed whenever the property is evaluated. HTML-TADS supports many more formatting options than regular TADS, but does so using HTML tags rather than TADS character sequences. Consult the HTML-TADS documentation for more information. ------------------------------------------------------------ Embedding Expressions in Strings TADS has a convenient feature which allows you to embed expressions in strings, without actually stopping the string. Any time TADS encounters two consecutive left angle brackets in a string, it will momentarily stop the string, and evaluate what comes after the angle brackets as an expression whose value is to be displayed. The expression ends, and the string resumes, when two right angle brackets are encountered. This makes property definitions that need to look up another property very convenient to define; for example, itsHere = "<< self.thedesc >> is here." The statement above is equivalent to this more verbose version: itsHere = { self.thedesc; " is here."; } An embedded expression can evaluate to a number, a single-quoted string, or a double-quoted string. Other datatypes are not allowed. A string may have any number of embedded expressions within it. An embedded expression may only contain an expression; statements (such as if) are not allowed, and semicolons may not be used to separate multiple expressions - use commas instead. This feature has certain limitations. It is illegal to use a string in an embedded expression which itself has an embedded expression. This feature can be used only in double-quoted strings; single-quoted strings cannot contain embedded expressions. Because some game authors may wish to use two or more consecutive left angle brackets in their strings, the special sequence `\<' is provided. See the section on special character sequences, above, for more information. Note that this feature is fully compatible with HTML TADS and does not cause any problems, even though it uses the < and > symbols. This is because these symbols are interpreted by the compiler at compile-time and never appear in the actual game text, where they could be misinterpreted by the runtime. ------------------------------------------------------------ Single-quoted Strings Single-quoted strings are essentially the same as double-quoted strings in appearance, except, of course, that they are enclosed in single quote marks. The sequence \' is available to produce a single quote in the string itself. Single-quoted strings do not display when evaluated; instead, they are treated as values. Certain built-in functions are available to manipulate these strings, such as say, which displays such a string value. Note also that all vocabulary words appear as single-quoted strings (or lists of single-quoted strings). ------------------------------------------------------------ Lists A list is an aggregate of other data items. It is entered as a set of data items enclosed in square brackets (note that these square brackets are the actual punctutation, not indicators of optionality): [ data-list ] A data-list can be nothing at all, in which case the list is empty, as in [ ] Or, it can be one or more data items (numbers, strings, objects, lists). Generally, it is only useful to construct lists with the same datatype for all members, but this is not required by the language. Examples of lists: [ 1 2 3 ] [ 'hello' 'goodbye' ] [ [1 2 3] [4 5 6] [7 8 9] ] [ vase goldSkull pedestal ] List elements need not be constants, unless the entire list needs to be a constant. For example, a list used as a property value must be a constant, so all of the elements must be constants (hence, local variables and non-constant expressions cannot be used). However, a list used in code, such as a list assigned to a local variable, can contain non-constant expressions within the list elements. For example, the following function dynamically constructs a list from three expressions: f: function(x, y, z) { return([x+1 y+1 z+1]); } The non-constant list elements are legal because the list construction appears in code. Note that the routine above has the same effect as this code: f: function(x, y, z) { return((([ ] + (x+1)) + (y+1)) + (z+1)); } The second example starts with an empty list, then adds an element whose value is (x+1), then adds a second element whose value is (y+1), and so on. The two examples construct exactly the same list. However, you should use the first construct where possible, because it is much more efficient: the first example constructs only a single list, whereas the second must construct four lists, one at a time. The first example is faster and consumes less memory at execution time. ------------------------------------------------------------ *>nil *>true Two special datatypes, nil and true, are pre-defined. These are generally used as "truth values"; nil means false and true means true. For example, 1 > 3 evaluates to nil, whereas 1 < 3 is true. In addition, nil means the absence of a value; taking the car of an empty list returns nil, as does evaluating a property of an object when the property is neither defined nor inherited by the object. For truth values, you should use nil and true rather than numeric equivalents, since nil and true provide much more explicit self-documentation of your program. ------------------------------------------------------------ Expressions A property definition can contain an expression rather than a simple constant. When a property value is an expression, the expression must be enclosed in parentheses. The expression is evaluated each time the property is evaluated. An example of using an expression for a property value: exprObj: object x = 1 y = 2 z = (self.x + self.y) ; Whenever exprObj.z is evaluated, the sum of the current values of exprObj.y and exprObj.z is returned. Note that a property defined in this manner can take parameters, just like methods (described below). For example: exprObj2: object w(a, b) = (a * b) ; When exprObj2.w is evaluated, it requires two parameters, which are multiplied together to give the value of the property. For example, evaluating exprObj2.w(3, 4) produces a value of 12. Note that the property z above could have been written without the "self." prefix on x and y: z = (x + y) This is because properties, if used without an object name, are assumed to belong to the current self object. ------------------------------------------------------------ Methods Code can be used in a property definition just like any other datatype. When you associate code with an object, the code is called a method. Another way of looking at it is that a method is code inside an object. When the method is evaluated or triggered, the code is executed. The return value is the value of the method, but often it is useful for the code to produce side effects as well. Method code is enclosed in braces, { and }. Anything valid in a function may appear in code associated with a method. The method may have local variables, and it can receive arguments. If the method has arguments, the argument list (which is identical to a function's argument list) appears after the method name. For example: obj1: object f(x) = { return(x + 1); } ; When such a method is evaluated, the argument list is specified after the method name, as in: f1: function { say(obj1.f(123)); } ------------------------------------------------------------ Functions A function is a standalone piece of code that, unlike a method, isn't part of an object. A function definition has this form: identifier: function [ ( argument-list ) ] { function-body } The argument-list is optional; functions need not take arguments if there's no need to send any data to it. But if the function does take one or more arguments, the list looks like this: identifier [, argument-list ] The identifiers may be used just like local variables within the function. The punctuation in the above syntactic description may be a little much, so an example might be helpful: addlist: function(list) // add up the numbers in the list { local sum, count, i; i := 1; // index the first item in the list sum := 0; // initialize the sum to zero count := length(list); // get the number of items in the list while (i < count) // as long as there's more to do... { sum := sum + list[i]; // add next element i := i + 1; // go on to the next list element } return(sum); } ------------------------------------------------------------ Functions with Variable Argument Lists It is possible to define a function that takes a variable number of arguments. In the function definition, you can specify a minimum number of arguments that are always passed to the function (which can be no arguments at all, if you wish), and then specify that more arguments can optionally follow. This is done with the "ellipsis" token, "...", as the final "argument" in the function's argument list. For example, to define a function that takes any number of arguments: f: function(...) { } To define a function that always takes at least one argument, but could take additional arguments: g: function(fmt, ...) { } In a function taking a variable number of arguments, you can determine how many arguments were actually passed to the function by inspecting the pseudo-variable argcount. This pseudo-variable's value is simply the number of arguments to the current function. To retrieve an argument, use the getarg(argnum) built-in function. The argument argnum is the number of the argument you want to retrieve; getarg(1) returns the first argument, getarg(2) returns the second, and so forth. Note that in a function which has some explicit arguments, followed by an ellipsis, getarg(1) still returns the first argument, even though it has a name in the argument list. For example, in the function g above, getarg(1) returns the value of the argument fmt. Another built-in function, datatype(value), can be used to determine the datatype of an argument. See the description of the datatype function later in this chapter for more information. As an example, the function below displays any number of values. displist: function(...) { local i; for (i := 1 ; i <= argcount ; i++) { say(getarg(i)); " "; } "\n"; } ------------------------------------------------------------ Forward-Declaration of Functions An alternative form of the function statement allows you to forward-declare a name as a function, without actually defining the function. The format of a forward declaration is: identifier: function; Note that this does not define the function; it merely tells the compiler that a function definition for the specified identifier will appear later in the file. This reserves the identifier for use as a function name, and prevents the compiler from assuming the identifier refers to an object. Forward declarations are not necessary except where setdaemon() and the like will be used. When a function is actually called, the syntax of the call tells TADS that the name refers to a function even when TADS hasn't seen the function definition yet. In setdaemon() and similar calls, though, no special syntax is present to tell TADS what the identifier refers to, so the compiler assumes it will refer to an object. ------------------------------------------------------------ Writing Code In this section we cover the details of the language that goes inside functions and methods. Function and method code consists of a series of statements; the statements are executed sequentially in the order they appear in the source program. The following pages provide details about the statements that may be used. Each statement in TADS code is terminated with a semicolon. ------------------------------------------------------------ *>local At the start of a function or property definition, you can define local variables for the current code block. This is done with a statement such as this: local identifier-list ; The identifier-list has the form: identifier [ initializer ] [, identifier-list ] An initializer, which is optional, has the form: := expression where the expression is any valid expression, which can contain arguments to the function or method, as well as any local variables defined prior to the local variable being initialized with the expression. The expression is evaluated, and the resulting value is assigned to the local variable prior to evaluating the next initializer, if any, and prior to executing the first statement after the local declaration. Local variables with initializers and local variables without initializers can be freely intermixed in a single statement; any local variables without initializers are automatically set to nil by the run-time system. The identifiers defined in this fashion are visible only inside the function in which the local statement appears. Furthermore, the local statement supersedes any global meaning of the identifiers within the function. A local statement can occur in any block (that is, any statements grouped together with braces), and a single block can have multiple local statements; all local statements in a block must precede the first executable statement of the bloc. In other words, the only thing that can appear before a local statement is another local statement. An example of declaring local variables, using multiple local statements, and using initializers is below. f: function(a, b) { local i, j; /* no initializers */ local k := 1, m, n := 2; /* some with initializers, some without */ local q := 5*k, r := m + q; /* OK to use q after it's initialized */ for (i := 1 ; i < q ; i++) { local x, y; /* locals can be at start of any block */ say(i); } } ------------------------------------------------------------ Expressions TADS expressions are entered in algebraic notation. Operators have different meanings on different datatypes. The basic list follows. +---+-------------------------------------------------+ | & | Takes the "address" of a function or property. | | | In other words, this expression allows you to | | | refer to a function or property without | | | evaluating (triggering) it. | | | | | | The & operator must be immediately followed by | | | the name of a property or the name of a | | | function. You can assign this value to a local | | | variable or a property, or you can pass it to | | | a function or method. The address value can | | | then be used to call the function or property. | | | See "Indirect Function and Method Calls" later | | | in this chapter. (Note: older versions of TADS | | | used the # operator to take the address of a | | | function. This operator is still understood by | | | the system, but we recommend that you use & in | | | new code instead.) | +---+-------------------------------------------------+ +----+------------------------------------------------+ | . | (Period or full stop.) Takes a property of an | | | object. On the left of the dot is an | | | expression that evaluates to an object, and | | | on the right is a property name. If the | | | property is a method that requires arguments, | | | these arguments are listed in parentheses | | | after the property name, just as with | | | function arguments. A local variable or an | | | expression enclosed in parentheses may appear | | | on the right hand side; the local or | | | expression must evaluate to a property | | | pointer. | +----+------------------------------------------------+ | [] | List indexing; applied to the right side of a | | | list-valued expression. An expression which | | | evaluates to a number must appear between the | | | square brackets. If the index (i.e., the | | | value between the square brackets) is n, the | | | nth element of the list is returned by this | | | operator. The first element in the list is | | | number 1. For example, ['one' 'two' | | | 'three'][1+1] evaluates to the string 'two'. | | | Note that a run-time error results if n is | | | outside the bounds of the list; that is, n is | | | less than 1 or greater than the number of | | | elements in the list (as returned by the | | | length() built-in function. | +----+------------------------------------------------+ | ++ | Increment; adds one to a numeric variable or | | | property. The expression to which ++ is | | | applied must be suitable for assignment | | | (i.e., it must be something that you can use | | | on the left-hand side of the assignment | | | operator, ":="). The ++ operator can be | | | either a prefix or postfix operator - that | | | is, it can be placed either before or after | | | its operand. If it's placed before the | | | operand, as in ++i, the operand is | | | incremented, and the value of the expression | | | is the value of the operand after the | | | increment. If it's placed after the operand, | | | as in i++, the value of the expression is the | | | value of the operand before the increment. | | | So, if the variable i has the value 3, ++i | | | has the value 4, while i++ has the value 3. | | | Think about it by reading the expression | | | left-to-right, and noting that the value of | | | the expression is the value of i when you | | | read it, and the increment is applied when | | | you read the ++ operator. So, with ++i, you | | | first read the increment operator, which adds | | | 1 to i, then you note the value of i, which | | | by now is 4. With i++, you first read i and | | | note its value, which is still 3, then you | | | see the ++ operator, leaving 4 in i. | +----+------------------------------------------------+ | -- | Decrement; subtracts one from a numeric | | | variable or property. This acts exactly like | | | the ++ operator, except that it decrements | | | its operand rather than incrementing it. | +----+------------------------------------------------+ +-----+-----------------------------------------------+ | not | Logical negation. Turns true into nil and | | | vice versa. This operator can only be | | | applied to the values true and nil. | +-----+-----------------------------------------------+ | - | Arithmetic negation. As a unary prefix | | | operator (that is, an operator that precedes | | | its single operand), the minus sign negates | | | the number following it. | +-----+-----------------------------------------------+ +---+-------------------------------------------------+ | * | Numeric multiplication. Multiplies the numbers | | | on either side of the asterisk together. | +---+-------------------------------------------------+ | / | Numeric division. The number on the left of | | | the slash is divided by the number on the | | | right. Note that TADS numbers are all | | | integers; the remainder of the division is | | | discarded. Hence, 7/2 has a value of 3. | +---+-------------------------------------------------+ +---+-------------------------------------------------+ | + | Adds numbers, concatenates lists, concatenates | | | strings. If both the left and right are | | | numbers, the result is numeric addition. If | | | one or the other is a list, but not both, the | | | non-list is added as the last element of the | | | list. If both operands are lists, the items | | | from the list on the right side are appended | | | to the list on the left; for example, [1 2 3] | | | + [4 5] yields the list [1 2 3 4 5]. If both | | | are strings, the right string is concatenated | | | to the left string. Other datatypes are | | | illegal. | +---+-------------------------------------------------+ | - | Subtracts numbers, removes items from lists. | | | If both operands are numbers, the right | | | operand is subtracted from the left. If the | | | left element is a list, the right item is | | | removed from the list if it appears in the | | | list on the left (nothing happens if not); if | | | the right item is also a list, each element | | | from the right list that appears in the left | | | list is removed from the left list. Other | | | datatypes are illegal. | +---+-------------------------------------------------+ +----+------------------------------------------------+ | = | Equality. The datatypes on either side must | | | be the same, but can't be lists. One | | | exception: anything can be compared for | | | equality to nil. Evaluates to true if the | | | items are the same, nil otherwise. | +----+------------------------------------------------+ | <> | Inequality. True if the operands on both | | | sides are not the same (although the datatype | | | of each operand must be the same). | +----+------------------------------------------------+ | > | Greater than. Like the other comparison | | | operators, this may only be applied to | | | numbers and strings. Evaluates to true or | | | nil. Note that, when applied to strings, the | | | comparison is based on the collation sequence | | | of the character set of the computer you are | | | using, such as ASCII. | +----+------------------------------------------------+ | < | Less than. | +----+------------------------------------------------+ | >= | Greater than or equal to. | +----+------------------------------------------------+ | <= | Less than or equal to. | +----+------------------------------------------------+ +-----+-----------------------------------------------+ | and | Logical product: both the left and right | | | sides must be true, in which case the value | | | is true; otherwise, the value is nil. Note | | | that if the left operand is nil, the right | | | operand is not evaluated at all, since the | | | value of the expression will be nil | | | regardless of the value of the right operand. | +-----+-----------------------------------------------+ +----+------------------------------------------------+ | or | Logical sum: one or the other of the left or | | | right operators must be true for the value to | | | be true; otherwise the value is nil. Note | | | that if the left operand is true, the right | | | operand is not evaluated at all, since the | | | value of the expression will be true | | | regardless of the value of the right operand. | +----+------------------------------------------------+ +-----+-----------------------------------------------+ | ? : | Conditional. This is a tertiary operator; | | | that is, it takes three operands, of the | | | form cond ? true-expr : false-expr. First, | | | the cond is evaluated; if it evaluates to | | | true or a non-zero number, then the | | | true-expr is evaluated, and its value is the | | | value of the entire conditional expression. | | | If the cond is false, the false-expr is | | | evaluated, and its value is the value of the | | | entire expression. Note that either | | | true-expr or false-expr is evaluated - not | | | both. This allows you to use this operator | | | when the expressions have side effects, such | | | as displaying strings. | +-----+-----------------------------------------------+ +----+------------------------------------------------+ | := | Assignment. The variable or property on the | | | left of the operator is assigned the value on | | | the right side. This is the lowest priority | | | operator, and operates right to left, unlike | | | the other operators. Hence, a := b := 3 | | | assigns the value 3 to b, then to a. Note | | | that an assignment is an expression, which | | | returns the assigned value; hence, the value | | | of (a := 3) + 4 is 7, with the side effect | | | that the variable a has been set to 3. | +----+------------------------------------------------+ | += | Add and assign. This is simply short-hand | | | notation. The expression a += b has exactly | | | the same effect as the expression a := a + b, | | | except that the += operator makes the | | | expression easier to write. The value of the | | | expression is a + b, which is the same as the | | | value of a after the assignment. | +----+------------------------------------------------+ | -= | Subtract and assign. This is short-hand | | | notation. The expression a -= b has the same | | | effect as a := a - b. | +----+------------------------------------------------+ | *= | Multiply and assign. The expression a *= b | | | has the same effect as a := a * b. | +----+------------------------------------------------+ | /= | Divide and assign. The expression a /= b has | | | the same effect as a := a / b. | +----+------------------------------------------------+ +---+-------------------------------------------------+ | , | Conjunction. The comma operator simply allows | | | you to start a new expression within an | | | expression. On the left hand side is an | | | expression, and on the right hand side is | | | another expression. The two expressions are | | | independent; the comma operator performs no | | | computation on either expression. The value of | | | a pair of expressions separated by a comma is | | | the value of the second (right-hand) | | | expression. The comma operator is useful | | | mostly in contexts where an expression is | | | required, but you wish to evaluate several | | | otherwise independent expressions for their | | | side effects. For example, in the | | | initialization portion of a for statement, you | | | often wish to initialize several variables; | | | you can do this by separating the | | | initializations with commas. See the | | | description for statement later in this | | | chapter. Note: the comma operator is different | | | from a comma that appears in a function's or | | | method's argument list. In the argument list, | | | the comma simply separates different | | | expressions that are the arguments; in a | | | normal expression, however, the comma | | | separates parts of the same expression. | +---+-------------------------------------------------+ The operators are shown above in their order of evaluation, from first to last. Hence, conjunctions are done last, after all higher-precedence operators have been evaluated. Note that there are some groups of operators in the list above; for example, all of the comparison operators are grouped together. This indicates that these operators have the same precedence. Operators of the same precedence associate left to right; for example, 3-4+5 is evaluated as (3-4)+5. The exception is the assignment operator, which groups right to left; that is, in the expression a := b := 2, b is first set to 2, then a is set to b. You can use parentheses to force a different order of evaluation of your expression. ------------------------------------------------------------ Assignments An assignment is not a special type of statement; it is merely an expression which uses an assignment operator: item := expression; The item is a local variable, a list element, or an object's property. When assigning to a property of an object, the object reference may itself be an expression. Likewise, when assigning to a list element, the list and index may both be expressions. Note, however, that lists cannot be expanded by assigning to an element past the end of the list; the index must refer to an existing member of the list to be replaced. A few examples of assignments: a: function(b, c) { local d, e, lst; d := b + c; // assign a local variable obj3.prop1 := 20; // assign obj3's property prop1 e := obj3; // assign an object to a local variable e.prop1 := obj2; // assign obj3's property prop1 the object obj2 e.prop1.prop2 := 20; // assign obj2's property prop2 the number 20 fun1(3).prop3 := 1 + d; // assign prop3 of the object returned by fun1 lst := [1 2 3 4 5]; // set up a local variable with a list lst[3] := 9; // lst is now [1 2 9 4 5] lst[5] := 10; // and it's [1 2 9 4 10] now /* lst[6] := 7 would be illegal - the list is only 5 elements long */ } ------------------------------------------------------------ C-Style Operators TADS users who are also C programmers often find the substantial similarity between TADS and C to be convenient, but also find the slight differences to be a source of confusion when switching between the two languages. TADS offers the option to use C-style operators. Note that if you're not an experienced C programmer you probably won't need to read this section. First, TADS supports the full complement of C operators. +---------+-------------------------------------------+ | a % b | Returns the remainder of dividing a by b | +---------+-------------------------------------------+ | a %= b | Assigns (a % b) to a | +---------+-------------------------------------------+ | a != b | Equivalent to (a <> b) | +---------+-------------------------------------------+ | !a | Equivalent to (not a) | +---------+-------------------------------------------+ | a & b | Bitwise AND | +---------+-------------------------------------------+ | a &= b | sets a to (a & b) (the bitwise AND of a | | | and b) | +---------+-------------------------------------------+ | a | b | bitwise OR | +---------+-------------------------------------------+ | a |= b | sets a to (a | b) (the bitwise OR of a | | | and b) | +---------+-------------------------------------------+ | a && b | equivalent to (a and b) | +---------+-------------------------------------------+ | a || b | equivalent to (a or b) | +---------+-------------------------------------------+ | a ^ b | bitwise XOR of a and b | +---------+-------------------------------------------+ | a ^= b | sets a to (a ^ b) (the bitwise XOR of a | | | and b) | +---------+-------------------------------------------+ | ~a | bitwise negation of a | +---------+-------------------------------------------+ | a << b | a shifted left by b bits | +---------+-------------------------------------------+ | a <<= b | sets a to (a b) (shifts a left by b bits) | +---------+-------------------------------------------+ | a >> b | a shifted right by b bits | +---------+-------------------------------------------+ | a >>= b | sets a to (a b) (shifts a right by b | | | bits) | +---------+-------------------------------------------+ Some of these operators, such as !, &&, and ||, are merely synonyms for existing operators. The "bitwise" operators act on numeric values rather than logical values; they treat their operands as bit vectors, and apply the operation to each bit of the numbers. For example, 3 & 2 has the value 2, since the bit patterns are "011" and "010," respectively. The bit-shift operations are equivalent to multiplying or dividing by a power of 2: 1 << 5 has the value 32, since it's equivalent to multiplying 1 by 2 raised to the 5th power. Second, TADS has a mode which uses the C-style assignment operator. Normally, the TADS assignment operator is :=, and the equality operator is =. In C, these operators are = and == respectively. If you prefer, you can tell TADS to use the C-style operators instead of the TADS version. By default, TADS still uses its own version of the operators. There are two ways to switch into C-style operator mode: by using a command-line option, or by using a #pragma compiler directive in your source code. To compile an entire game in C mode, use the -C+ command line option (Macintosh users will find a menu item for C-style operators under the "Options" menu; check this item to enable C operators, and uncheck it to use standard TADS operators). Using the -C+ compiler option enables C operator mode for the entire game's source code. (The -C- option explicitly turns off C operator mode. This is the default mode.) To specify that a particular file is to be compiled in C mode, you can use the directive #pragma C+. The similar directive #pragma C- specifies that TADS operator mode is to be used. These directives can appear anywhere in a source file (outside of comments and strings); they must be alone on the line, and must not be preceded by any whitespace on the line. A #pragma setting affects only the current source file, and any files it includes. The header files included with TADS (adv.t and std.t) both use TADS operators, so they explicitly specify #pragma C-. However, because these directives are limited to the header files, you can freely include adv.t from a file that uses C operator mode without having to worry about setting the mode to or from TADS mode. Simply #include adv.t exactly as you did before - even if your source file uses C mode, adv.t will compile correctly, because it sets the operator mode back to TADS for its own contents, and TADS automatically restores the enclosing file's mode at the end of adv.t. Note that the C-style operator mode setting affects only the assignment and equality operators. You can use all of the other C operators (such as the bitwise operators) in either mode - all of these symbols were invalid in previous versions of TADS, so there's no danger that they'll be misinterpreted for old games. When the compiler is using C-style assignment operators, it issues a warning, "possibly incorrect assignment," whenever it finds a statement in this format: if ( a = 1 ) ... While this statement is legal, with C-style operators it has the effect of assigning the value 1 to a; since the value of an assignment is the value assigned, this if will always succeed. It's a common error for C programmers (even highly experienced ones) to write this type of statement when they really want to compare the values. In fact, I originally chose to use ":=" as the assignment operator in TADS to reduce the likelihood of this type of error. Now that TADS can be switched to C syntax for assignments and comparisons, I've added the "possibly incorrect assignment" warning to help catch these. The compiler will flag assignments made in if, while, and do statements, and in the condition clause of for statements. To suppress this warning, you can explicitly test the value of the assignment like this: if ( ( a = 1 ) != 0 ) ... There are a couple of minor complications with some of the C-style operators. First, the >> operator can't be used in an expression embedded in a string with the << >> construct, because it would be taken for the >> that terminates the embedded expression. Even adding parentheses won't help, because the compiler recognizes the << >> construct before it looks at any expression in progress. So, this type of code won't work: myprop = "x divided by 128 is << (x >> 7) >>! " // wrong You would have to code this instead as: myprop = { "x divided by 128 is "; x >> 7; "! "; } // right Second, the & operator now has a binary interpretation in addition to its unary interpretation. For the most part, this won't create any confusion, but there's one situation in which it might: in lists. You might have lists in your games that look like this: mylist = [ &prop1 &prop2 &prop3 ] In past versions, since the & operator could only be a unary operator, this construct was unambiguous. However, now that & can be a binary operator, this could be interpreted either as three expressions involving unary & operators, or as a single expression involving one unary & operator and two binary & operators. For compatibility with past versions, TADS will interpret the & operators as unary operators. When it finds this construct, though, it will warn you that it is ambiguous. (The new warning is TADS-357, operator `&' interpreted as unary in list.) You can suppress this warning in one of two ways. First, you can render the list unambiguous. To do this, use a comma between each pair of list elements: mylist = [ &prop1, &prop2, &prop3 ] Note that if you actually want the binary interpretation, you should simply enclose the expression in parentheses: mylist = [ ( 2 & 3 & 4 ) ] The other way you can suppress this message is with the new -v-abin compiler option, which tells the compiler not to generate the warning. The compiler still interprets the operator the same way when you specify -v-abin - it just doesn't tell you about it. Note that TADS will treat any operator which has both a unary and binary interpretation as a unary operator when it finds it within a list, and will generate the TADS-357 warning. For the - operator, this is a change from past versions, which used the binary interpretation when in a list. I don't anticipate that this will be a compatibility problem, because the old binary interpretation was almost never desirable, and I think users avoided it. However, if you have an older game, you may wish to compile without the -v-abin option at least once, and check any lines where the TADS-357 warning is generated for - or + operators, to determine if your game's behavior will change with the new version. Any TADS-357 warnings generated for the & operator can be safely ignored for a game written with a previous version of TADS. ------------------------------------------------------------ Function Calls A function call is simply an expression involving a call to a function. function-name( [ argument-list ] ); The function-name is the name of a function defined elsewhere. The argument list, if provided, is passed to the function for its parameters. (Naturally, the arguments passed to a function should match in number those defined in the function's definition.) Each parameter can be an arbitrary expression, and the individual arguments are separated by commas. Whether there's an argument list or not, the parentheses are required; they tell the compiler that you wish to call the function. (You may wonder where you would ever want to use a function's name other than when calling the function. A few special built-in functions, such as setdaemon, which will be discussed in detail later, allow you to specify a function that is to be called eventually, but not right away. In these cases, you need to be able to refer to a function without actually calling it. In these cases, you use the function's name without parentheses.) If the function returns a value, the value is discarded in this form of the call. A function called in this manner is invoked for its side effects rather than its return value. An example: showlist(a+100, 'The list is: ', list1); ------------------------------------------------------------ Indirect Function and Method Calls You can call a function or method "indirectly" - that is, you can use a pointer to a function or method to call the function or method. Another way of looking at it is you can refer to the function or method without actually evaluating (triggering it). This can sometimes be useful in setting up a general routine which calls other routines based on parameters passed into it. To call a function with a function pointer, simply use an expression yielding a function pointer, within parentheses, where you'd normally use a function name. For example: g: function(a, b, c) { return(a + b + c); } f: function { local fptr, x; fptr := &g; /* get address of function g */ x := (fptr)(1, 2, 3); /* call function with pointer */ } A property pointer is used in essentially the same way. f: function(actor, obj) { local propPtr := &doTake; /* get pointer to doTake property */ obj.(propPtr)(actor); /* call property through pointer */ } For example, suppose you wish to implement a way of asking characters in a game about various objects in the game. One way you could do this is by defining a general "ask" routine that takes the character and the object as arguments. Set things up so that each object defines a property saying what each actor knows about that object. Then, each actor specifies via a "property pointer" which property to evaluate in an object to find out what the actor knows about the object. If an object doesn't define this property, the actor doesn't know anything about that object. So, our general "ask" routine is very simple (or concise, anyway): ask: function(actor, obj) { local propPtr; /* find out what property to evaluate in the object */ propPtr := actor.askPropPtr; /* see if it's defined in the object */ if (defined(obj, propPtr)) { /* it is defined - call the property indirectly */ obj.(propPtr); } else { /* it's not defined - use default message */ actor.dontKnow; } } Now, each actor simply has to define a property that each object uses to specify what the actor knows about the object, and place the address of this property the actor's askPropPtr property. The actor also needs to define the default message in the dontKnow property. Here's an example: joe: Actor sdesc = "joe" noun = 'joe' dontKnow = "Joe just scratches his head and shrugs. " askJoe = "You probably don't want to get Joe started on his life story. " askPropPtr = askJoe ; Finally, each object must define an appropriate askJoe property if Joe knows anything about that object. Likewise, it will define other properties for what other actors know about it. This way, all of the information about an object, including what the various characters in the game know about it, can be kept with the object itself. In addition, the general "ask" routine is extremely simple. The overall concept behind the mechanism is somewhat complicated, but the finished product is very simple and easy to use and expand. ------------------------------------------------------------ *>return A function can return a value to its caller by using a statement such as: return [ expression ]; With or without the expression, execution of the function is terminated and the caller resumes execution where it left off. If the expression is provided, the caller receives the expression as the function's value. Here's an example of a function that computes the sum of the elements of a list, and returns the sum as the value of the function. listsum: function(lst) { local i, sum, len := length(lst); for (i := 1, sum := 0 ; i <= len ; i++) sum += lst[i]; return(sum); } Note that the brackets are optional. Thus the following form is perfectly legal: return nil; In fact, recent versions of adv.t use the bracketless style as the preferred form. ------------------------------------------------------------ *>if *>else The general form of the conditional in TADS is: if ( expression ) statement [ else statement ] Here and elsewhere, a statement can be either a single statement or a series of statements enclosed in braces. The expression should evaulate to either a number, in which case zero counts as false and anything else counts as true, or to a truth value, true or nil. Note that the optional else clause is grouped with the most recent if statement when if statements are nested. For example, if (self.islit) if (film.location = Me) "Oops! You've exposed the film! "; else "It's dark in here. "; The author of this code obviously intended the else clause to go with the first if, but remember that an else goes with the most recent if, so it actually is grouped with the second if statement. This problem can be easily rectified by using braces to make the grouping explicit: if (self.islit) { if (film.location = Me) { "Oops! You've exposed the film! "; } else { "It's dark in here. "; } } ------------------------------------------------------------ *>switch *>case *>default The switch statement lets you set up the equivalent of a large if-else tree, but is considerably easier to read and is more efficient to execute. A switch statement allows you to test a particular value against several alternatives, and execute a group of statements accordingly. The form of the switch statement is: switch ( expression ) { [ case-list ] [ default-clause ] } The form of the case-list is: case constant-expression : [ statements ] [ case-list ] The form of the default-clause is: default: [ statements ] In the diagrams, statements means that zero or more statements can follow a case or default. You do not need to supply any case labels at all, and the default is also optional. The expression evaluates to a number, string, object, list, or true or nil. The value of the expression is then tested against each case value. If the value matches one of the case values, the statements following the matching case are executed. If the value does not match any case value, and a default case is defined, the statements following the default are executed. If the value does not match any case value, and there is no default case, the entire switch statement is skipped, and execution resumes following the closing brace of the switch. Note that execution is not interrupted when another case is enountered. Instead, it just continues into the statements following the case label. If you wish to stop executing statements in the switch at the end of the statements for a single case, you must use the break statement. The break statement has a special meaning within a switch statement: it indicates that execution should break out of the switch statement and resume following the closing brace of the switch. Here's an example of a switch statement. f: function(x) { switch(x) { case 1: "x is one"; break; case 2: case 3: "x is either 2 or 3"; break; case 4: "x is 4"; case 5: "x is either 4 or 5"; case 6: "x is 4, 5, or 6"; break; case 7: "x is 7"; break; default: "x is not in 1 through 7"; } } ------------------------------------------------------------ *>while The while statement defines a loop: a set of statements that is executed repeatedly as long as a certain condition is true. while ( expression ) statement As with the if statement, the statement may be a single statement or a set of statements enclosed in braces. The expression should be a number (in which case 0 is false and anything else is true), or a truth value (true or nil). The expression is evaluated before the first time through the loop; if the expression is false at that time, the statement or statements in the loop are skipped. Otherwise, the statement or statements are executed once, and the expression is evaluated again; if the expression is still true, the loop executes one more time and the cycle is repeated. Once the expression is false, execution resumes at the next statement after the loop. ------------------------------------------------------------ *>do The do-while statement defines a slightly different type of loop than the while statement. This type of loop also executes until a controlling expression becomes false (0 or nil), but evaluates the controlling expression after each iteration of the loop. This ensures that the loop is executed at least once, since the expression isn't tested for the first time until after the first iteration of the loop. The general form of this statement is: do statement while ( expression ); The statement may be a single statement or a set of statements enclosed in braces. The expression should be a number (in which case 0 is false and anything else is true), or a truth value (true or nil). ------------------------------------------------------------ *>for The for statement defines a very powerful and general type of loop. You can always use while to construct any loop that you can construct with for, but the for statement is often a much more compact and readable notation for the same effect. The general form of this statement is: for ( init-expr ; cond-expr ; reinit-expr ) statement As with other looping constructs, the statement can be either a single statement, or a block of statements enclosed in braces. The first expression, init-expr, is the "initialization expression." This expression is evaluated once, before the first iteration of the loop. It is used to initialize the variables involved in the loop. The second expression, cond-expr, is the condition of the loop. It serves the same purpose as the controlling expression of a while statement. Before each iteration of the loop, the cond-expr is evaluated. If the value is true (a non-zero number, or true), the body of the loop is executed; otherwise, the loop is terminated, and execution resumes at the statement following the loop body. Note that, like the while statement's controlling expression, the cond-expr of a for statement is evaluated prior to the first time through the loop (but after the init-expr has been evaluated), so a for loop will execute zero times if the cond-expr is false prior to the first iteration. The third expression, reinit-expr, is the "re-initialization expression." This expression is evaluated after each iteration of the loop. Its value is ignored; the only purpose of this expression is to change the loop variables as necessary for the next iteration of the loop. Usually, the re-initialization expression will increment a counter or perform some similar function. Any or all of the three expressions may be omitted. Omitting the expression condition is equivalent to using true as the expression condition; hence, a loop that starts "for ( ;; )" will iterate forever (or until a break statement is executed within the loop). A for statement that omits the initialization and re-initialization expressions is the same as a while loop. Here's an example of using a for statement. This function implements a simple loop that computes the sum of the elements of a list. sumlist: function(lst) { local len := length(lst), sum, i; for (sum := 0, i := 1 ; i <= len ; i++) sum += lst[i]; } Note that an equivalent loop could be written with an empty loop body, by performing the summation in the re-initialization expression. We could also move the initialization of len within the initialization expression of the loop. sumlist: function(lst) { local len, sum, i; for (len := length(lst), sum := 0, i := 1 ; i <= len ; sum += lst[i], i++); } ------------------------------------------------------------ *>break A program can get out of a loop early using the break statement: break; This is useful for terminating a loop at a midpoint. Execution resumes at the statement immediately following the innermost loop in which the break appears. The break statement also is used to exit a switch statement. In a switch statement, a break causes execution to resume at the statement following the closing brace of the switch statement. ------------------------------------------------------------ *>continue The continue statement does roughly the opposite of the break statement; it resumes execution back at the start of the innermost loop in which it appears. The continue statement may be used in for, while, and do-while loops. In a for loop, continue causes execution to resume at the re-initialization step. That is, the third expression (if present) in the for statement is evaluated, then the second expression (if present) is evaluated; if the second expression's value is non-nil or the second expression isn't present, execution resumes at the first statement within the statement block following the for, otherwise at the next statement following the block. ------------------------------------------------------------ *>goto The goto statement is used to transfer control unconditionally to another point within the same function or method. The target of a goto is a label; a label is defined by placing its name, followed by a colon (:), preceding a statement. Note that labels have function- or method-scope; that is, they are visible within the entire function or method in which they are defined. This is different from local variables, which are visible only within the block (the group of statements enclosed in braces) in which they are defined. Labels are not visible outside the function or method in which they are defined. An example of using goto: f: function { while (true) { for (x := 1 ; x < 5 ; x++) { /* do some stuff */ if (myfunc(3) < 0) /* did an error result? */ goto exitfunc; /* error - quit now */ /* do some more stuff */ } } /* come here if something goes wrong */ exitfunc: ; } This use of goto avoids the need for testing a flag in the outer (while) loop, which makes the code a little simpler and easier to understand. The goto statement is widely considered among civilized computer scientists to be an evil and malevolent feature of ancient and unusable languages, and the esteem of TADS within serious computer language design circles has undoubtedly been fatally injured by the inclusion of this construct. So, you may wish to use this statement sparingly, if at all, especially if you're hoping to impress a civilized computer scientist with your coding efforts. However, many software engineers look upon goto as a highly useful statement when in the hands of a seasoned professional, and scoff at the blanket indictment by the more-elegant-than-thou academic establishment, most of whom probably haven't written a line of code since their TA's were chastising them for using goto in their Pascal programs, excepting perhaps some algorithms written in pseudo-code that always end in "the rest is left as an exercise for the reader" anyway. The author of TADS doesn't wish to take sides in this heated controversy, but hopes that both camps will be pleased, by gaining either the utility of using goto with wild abandon or the sense of virtue of knowing they could have used it but overcame the unclean temptation. With TADS, the choice is yours. ------------------------------------------------------------ *>pass A method can decide to inherit the behavior of its parent class by using the pass statement: pass method-name; The method-name must be the same as the name of the method that the pass statement occurs in. When a pass statement is executed, the method that would have been inherited if the object had not overridden it is called with the same arguments, if any, as the method called in the first place. The self object is unchanged; that is, the superclass method is run, but self is the object that was originally sent the message being passed. ------------------------------------------------------------ *>abort *>exit During processing of a player's command, the game can terminate the normal sequence of events and return to the get another command from the player in three different ways. The abort statement stops all processing, and gets the next command; it is normally used by "system" functions, such as saving the game, that should not count as a turn, and therefore shouldn't run any daemons or fuses. The exit statement, on the other hand, skips everything up to the fuses and daemons and so is used when an actor wishes to stop further processing, and other similar cases. Finally, the exitobj function does what exit does except that it skips the remaining processing only for the current object in a command and proceeds to the next object. Note that messages to objects scheduled with the notify() function are treated the same as other daemons and fuses, so these are also skipped by the abort statement. You can use the abort statement within a daemon or fuse, and it will work as expected, by terminating the current routine and skipping any remaining fuses or daemons on this turn. (this wasn't allowed in older versions of TADS) ------------------------------------------------------------ *>askdo *>askio These statements interrupt processing of a turn like abort, skipping fuses and daemons, but allow the user to enter more information. The askdo command asks the user to enter a direct object; the system displays a message asking the user for an object. The user can either enter an object in response to the request, or can simply type a new command. For example, if the verb is "take," executing an askdo command will cause the system to display "What do you want to take?" and wait for an object or a new command. The askio command is similar, but it takes a preposition as a parameter; this preposition is added to the command, and the system prompts the player for an indirect object. For example, if the verb is "unlock," and the following command is executed: askio(withPrep); then the system displays "What do you want to unlock it with?" and prompts the player for an object or a new command. The askio command can only be used when a direct object is already present. In either case, if the player responds to the request with an object, the command is tried again from the start. If the player types a new command, the command that resulted in the askdo or askio is discarded. Note that control never comes back to the statement after the askdo or askio command, regardless of user input; in either case, command processing starts from the top. Note that, in some cases, the system won't actually ask the player for a new object. Instead, the system will attempt to find a default object, using exactly the same mechanisms that it uses to find default objects normally (see the section on the parser's default object mechanisms in chapter four). ------------------------------------------------------------ *>self When code associated with a property is being executed, a special object is defined, called self. This special object refers to the object whose property is being evaulated. This may not sound too useful, but consider the case of an object whose superclass defines a property which refers to other properties of the object: class book: object description = { "The book is << self.color >>."; } ; redbook: book color = "red" ; bluebook: book color = "blue" ; In this example, the general object, book, knows how to describe a book given its color. The books that are defined, the redbook and bluebook objects, take advantage of this by simply defining their color, and letting the description property of their superclass be used to describe them. So, when you attempt to evaluate redbook.description, you get The book is red. ------------------------------------------------------------ *>inherited A special pseudo-object called inherited allows you to call a method in the current self object's superclass. This pseudo-object is similar to the pass statement, but much more useful in several ways. First, with inherited, you can simply call the superclass method, and regain control when it returns; with pass, the current method never regains control. Second, you can use inherited in an expression, so any value returned by the superclass method can be determined and used by the current method. Third, you can pass arguments to the property invoked with the inherited pseudo-object. You can use inherited in an expression anywhere that you can use self. Here is an example of using inherited. myclass: object sdesc = "myclass" prop1(a, b) = { "This is myclass's prop1. self = << self.sdesc >>, a = << a >>, and b = << b >>.\n"; return(123); } ; myobj: myclass sdesc = "myobj" prop1(d, e, f) = { local x; "This is myobj's prop1. self = << self.sdesc >>, d = << d >>, e = << e >>, and f = << f >>.\n"; x := inherited.prop1(d, f) * 2; "Back in myobj's prop1. x = << x >>\n"; } ; When you call myobj.prop1(1, 2, 3), the following will be displayed: This is myobj's prop1. self = myobj, d = 1, e = 2, and f = 3. This is myclass's prop1. self = myobj, a = 1, and b = 3. Back in myobj's prop1. x = 246. Note one feature of inherited that is the same as pass: the self object that is in effect while the superclass method is being executed is the same as the self object in the calling (subclass) method. This makes inherited very different from calling the superclass method directly (i.e., by using the superclass object's name in place of inherited). TADS 2.2.4 added a new syntax that lets you specify the name of the superclass after the `inherited' keyword, but is otherwise similar to the normal `inherited' syntax: inherited fixeditem.doTake(actor); This specifies that you want the method to inherit the doTake implementation from the fixeditem superclass, regardless of whether TADS might normally have chosen another superclass as the overridden method. This is useful for situations involving multiple inheritance where you want more control over which of the base classes of an object should provide a particular behavior for the subclass. ------------------------------------------------------------ *>argcount The pseudo-variable argcount returns the number of arguments to the current function. This can be used for functions that take a variable number of arguments to learn the number of arguments that need to be processed. Note that argcount isn't really a variable, so you can't assign a value to it, but otherwise you can use it as though it were an ordinary variable. ------------------------------------------------------------ *>replace *>modify Most game authors find that, when writing a substantial game, they can't avoid modifying adv.t. While there's nothing intrinsically wrong with this, it creates a problem when a new version of TADS is released, because you must either continue to use the old version of adv.t, which means that any bug fixes or enhancements in the new version are not available, or take the time to reconcile your changes to your custom adv.t with those made in the standard version. The replace and modify mechanism can help you deal with this problem. These keywords allow you to make changes to objects that have been previously defined. In other words, you can #include the standard adv.t file, and then make changes to the objects that the compiler has already finished compiling. Using these keywords, you can make three types of changes to previously-defined objects: you can replace a function entirely, you can replace an object entirely, or you can add to or change the methods already defined in an object. To replace a function that's already been defined, you simply preface your replacement definition with the keyword replace. Following the keyword replace is an otherwise normal function definition. The following example replaces the scoreStatus function defined in adv.t with a new function that customizes the status line score display. #include replace scoreStatus: function( points, turns ) { setscore( cvtstr( pts ) + ' points/' + cvtstr( turns ) + ' moves' ); } You can do exactly the same thing with objects. For example, you can entirely replace the fastenVerb defined in adv.t: #include /* we don't want "buckle", so replace adv.t's fastenVerb */ replace fastenVerb: deepverb verb = 'fasten' sdesc = "fasten" prepDefault = toPrep ioAction( toPrep ) = 'FastenTo' ; Replacing an object entirely deletes the previous definition, including all inheritance information and vocabulary. The only properties of a replaced object are those defined in the replacement; the original definition is entirely discarded. You can also modify an object, retaining its original definition (including inheritance information, vocabulary, and properties). This allows you to add new properties and vocabulary. You can also override properties, simply by redefining them in the new definition. The most common addition to an object from adv.t will probably be new verb associations and added vocabulary. modify pushVerb verb = 'nudge' ioAction( withPrep ) = 'PushWith' ; Note several things about this example. First, no superclass information can be specified in a modify statement; this is because the superclass list for the modified object is the same as for the original object. Second, note that vocabulary has been added. The additional vocabulary does not replace the original vocabulary, but simply adds to the previously-defined vocabulary. Further note that verb association pseudo-properties, such as doAction and ioAction, are legal in a modify definition. Any new doAction or ioAction definitions are added to the original set of definitions. In a method that you redefine with modify, you can use pass and inherited to refer to the replaced method in the original definition of the object. In essence, using modify renames the original object, and then creates a new object under the original name; the new object is created as a subclass of the original (now unnamed) object. (There is no way to refer to the original object directly; you can only refer to it indirectly through the new replacement object.) Here's an example of using pass with modify. class testClass: object sdesc = "testClass" ; testObj: testClass sdesc = { "testObj..."; pass sdesc; } ; modify testObj sdesc = { "modified testObj..."; pass sdesc; } ; Evaluating testObj.sdesc results in this display: modified testObj...testObj...testClass You can also replace a property entirely, erasing all traces of the original definition of a property. The original definition is entirely forgotten - using pass or inherited will refer to the method inherited by the original object. To do this, use the replace keyword with the property itself. In the example above, we could do this instead: modify testObj replace sdesc = { "modified testObj..."; pass sdesc; } ; This would result in a different display for testObj.sdesc: modified testObj...testClass The replace keyword before the property definition tells the compiler to completely delete the previous definitions of the property. This allows you to completely replace the property, and not merely override it, meaning that pass and inherited will refer to the property actually inherited from the superclass, and not the original definition of the property. ------------------------------------------------------------ Built-in Functions The system has a set of built-in functions to facilitate writing programs. The functions are described in this section. They operate just like ordinary functions, with the exception that built-in functions which don't take arguments don't require parentheses; this simplifies coding of some special cases. ------------------------------------------------------------ *>addword Call: addword(obj, &prop, word) Adds the word (a single-quoted string value) to the object as the given part of speech. The prop parameter can be noun, adjective, plural, verb, article, or preposition. You can add words to any object, including objects defined statically in your game, as well as objects created dynamically at run-time. For examples of using this function, see the section on Dynamic Vocabulary. ------------------------------------------------------------ *>askfile Call: askfile(prompt_text, prompt_type_code, file_type_code, flag) Added: Type code parameters added with TADS 2.3.0. Flag value added with TADS 2.5.0 This function asks the user to enter a filename, in a system-dependent manner. The system's standard file dialogue, if the computer has one, will be used; otherwise, the user may simply be prompted to type a filename. The prompt (a single-quoted string value) may or may not be used, depending on system conventions; for systems without any defined standard file dialogue, it will be displayed to prompt the user for a filename. This function is primarily useful for operations such as saving and restoring games which require that the user enters a system filename. Prompt strings passed to askfile can contain \n and \t sequences. These sequences are converted properly for display in the dialogue. TADS 2.3 added two additional, optional parameters that let you specify what type of prompt to show and what type of file to request. These arguments are hints to the system-specific code that displays the "open file" dialogue; by specifying this new information, you help the system code show the correct type of dialogue. The prompt_type_code tells the open-file dialogue whether you're opening an existing file or saving a file. On some systems (Windows and Macintosh included), the user interface uses one type of dialogue for opening an existing file, and a different type of dialogue for saving a file; you can use this parameter to select the appropriate dialogue type on systems that make this distinction. This parameter can have one of the following values, defined in adv.t: ASKFILE_PROMPT_OPEN - open an existing file for reading ASKFILE_PROMPT_SAVE - open a file for saving information On some systems, the open-file dialogue will filter the files it displays so that the player only sees files of the particular type being requested. The file_type_code parameter lets you specify the type of file you're interested in, so that the dialogue can use the appropriate filtering on systems that support this. The file_type_code can be one of the following values, defined in adv.t: FILE_TYPE_GAME - a game data file (.gam) FILE_TYPE_SAVE - a saved game (.sav) FILE_TYPE_LOG - a transcript (log) file FILE_TYPE_DATA - general data file (used for fopen()) FILE_TYPE_CMD - command input file FILE_TYPE_TEXT - text file FILE_TYPE_BIN - binary data file FILE_TYPE_UNKNOWN - unknown file type If you leave out the type code arguments in a call to askfile(), the function will behave as it did with previous versions of TADS. This means that your prompt string must contain the word "save" or "write" in order to show a "save file" dialogue rather than an "open file" dialogue on those systems that differentiate between these dialogue types. The optional fourth argument added with TADS 2.5.0 lets you specify additional flags to the askfile function. The possible flag values, defined in adv.t, are: +--------------------+--------------------------------+ | ASKFILE_EXT_RESULT | Return extended result codes | | | (described below). If this | | | flag is provided, the | | | function returns extended | | | results; if this flag is not | | | specified, the function | | | returns the traditional | | | results. | +--------------------+--------------------------------+ In order to specify the new flag value argument, you must specify the prompt type and file type arguments as well; if you omitted the prompt or file type argument, the askfile function would not be able to tell that you meant the last argument as the flags value. If you omit the flags argument, askfile uses a default value of zero, which makes the function behave the same as in past versions. Because older code never specifies a flags value, the function will always behave compatibly with past versions when called from older code. Before the release of 2.5.0, askfile returned a string on success, or nil for any type of failure. However, this didn't permit the caller to determine exactly what kind of failure occurred, and in particular did not allow the caller to distinguish between an actual error and the player cancelling the file selector dialogue. When ASKFILE_EXT_RESULT is specified, the function will return additional information that allows the caller to distinguish these cases. When the ASKFILE_EXT_RESULT flag is specified, askfile returns a list that contains two elements. The first element is a number which indicates the status of the file selection; the second element is a string if a file was successfully chosen, or nil if not. The possible values for the first element of the returned list, defined in adv.t, are: +-----------------+-----------------------------------+ | ASKFILE_SUCCESS | A file was successfully chosen. | | | The second element of the list | | | contains a string giving the | | | chosen filename. | +-----------------+-----------------------------------+ | ASKFILE_FAILURE | An error occurred prompting for | | | a filename. This usually | | | indicates that the file selector | | | dialogue could not be shown for | | | some reason (insufficient | | | memory, for example). | +-----------------+-----------------------------------+ | ASKFILE_CANCEL | The user canceled the file | | | selector dialogue. On the | | | Macintosh, for example, this | | | means that the user clicked the | | | "Cancel" button. This indicates | | | that the user does not wish to | | | proceed with whatever operation | | | is in progress, so the operation | | | should be aborted. Since the | | | user explicitly chose to cancel | | | the operation, the program | | | should not indicate that an | | | error occurred, but simply that | | | the operation will be terminated | | | in accordance with the user's | | | request. | +-----------------+-----------------------------------+ Here's an example, from the "restore" command's implementation in adv.t, of using the extended results. local savefile; savefile := askfile('File to restore game from', ASKFILE_PROMPT_OPEN, FILE_TYPE_SAVE, ASKFILE_EXT_RESULT); switch(savefile[1]) { case ASKFILE_SUCCESS: return mainRestore(savefile[2]); case ASKFILE_CANCEL: "Cancelled. "; return nil; case ASKFILE_FAILURE: default: "Failed. "; return nil; } ------------------------------------------------------------ *>caps Call: caps() Forces the next non-space character to be displayed to be capitalized. This is useful for formatting output when it is not known in advance whether an item will be displayed at the start of a sentence or not. Note that displaying the sequence "\^" has the same effect as calling caps(). Do not confuse this function with the upper(string) built-in function, which converts all of the letters in a string to upper-case. The caps() function takes no arguments, and affects only the next character output. ------------------------------------------------------------ *>car Call: car(list) Returns the first element of a list, or nil if the list is empty. Note that the same value can be retrieved with the expession list[1], which uses the list indexing operator to retrieve the first element of list. The primary difference between using car() and the list indexing operator is the style of your program; using car() and cdr() to decompose a list, you can iterate or recurse until you run out of list to process, at which time car() will return nil. With the list indexing operator, however, you need to know in advance how many elements are in the list; this information can be learned with the length() built-in function. The choice of one of these methods over another is a matter of personal preference. ------------------------------------------------------------ *>cdr Call: cdr(list) Returns the end of a list; that is, the list of everything after the list's car(). With car() and cdr() you can decompose a list into its individual elements. ------------------------------------------------------------ *>clearscreen Call: clearscreen() Clears the screen. This function may have no effect when called under some versions of the TADS interpreter. For example, when the DOS runtime is operating in plain ASCII mode, clearscreen() will have no effect. Other interpreters clear the screen by displaying a screenful of blank spaces, allowing you to view previously-displayed text in the scrollback. Others may simply reset the text window, so you lose everything that was in the scrollback. This function has a special meaning in multimedia-enabled versions of the TADS interpreter. The function clears the screen but retains the contents of the screen as a chapter. You can thus flip from one chapter to the next by using the appropriate menu items. ------------------------------------------------------------ *>cvtnum Call: cvtnum(string) This function converts a string that contains the text version of a number into a numeric value. For example, cvtnum('1234') returns the number 1234. Note that the special strings `true' and `nil' are also accepted by the function, and are converted to the logical values true and nil, respectively. ------------------------------------------------------------ *>cvtstr Call: cvtstr(value) This function converts a numeric or logical value into its string representation. For example, cvtstr(1234) returns the string `1234', cvtstr(true) returns the string `true', and cvtstr(nil) returns the string `nil'. ------------------------------------------------------------ *>datatype Call: datatype(value) This function returns the type of a value. It is useful primarily with functions that take a variable number of arguments, but could also be useful for inspecting lists whose contents vary. This function returns a numeric value based on the datatype. 1 - Number 2 - Object 3 - String 5 - nil 7 - List 8 - true 10 - Function Pointer 13 - Property Pointer ------------------------------------------------------------ *>debugTrace Call: debugTrace(1, flag) This form of debugTrace lets you turn a new player command parser diagnostic mode on and off. If flag is true, this function activates the diagnostic mode; if flag is nil, it turns the diagnostic mode off. When the diagnostic mode is active, the parser generates a series of progress messages as it analyzes the player's command. These messages provide information on how the parser interprets the words in the command. When first reading a sentence, the parser displays all possible parts of speech for each word. As the parser further analyzes the sentence, it displays information on each noun phrase: the words involved, the part of speech that the parser uses for each word in the noun phrase (when a word can be used as multiple parts of speech, the parser chooses one part of speech as it reads the noun phrase), and the objects that match the words in the noun phrase. The parser diagnostic mode may help you track down problems in which the parser refuses to recognize certain noun phrases that you would expect to be valid. Since the parser chooses among ambiguous interpretations of words, it's frequently helpful to understand exactly how the parser is interpreting your commands; this new debugging mode should make it easier to gain this understanding. This mode is available in the standard runtime as well as the debugger, so debugTrace(1, flag) always succeeds. The function returns no value. ------------------------------------------------------------ *>defined Call: defined(object, propPointer, flag) Added: Flag argument added with TADS 2.5.1. This function allows you to determine if a property is defined or inherited by an object, or if the object doesn't have any value for the property. This function returns true if the the object has a definition for the property (either explicitly in the object, or inherited from a superclass), or nil if the object doesn't have any definition for the property. Note that the propPointer argument must be a property pointer. You can obtain a property pointer using the & operator with a property name. For example, to determine if the player's current location has an ldesc property defined, you would do this: x := defined(Me.location, &ldesc); In this example, x is true if the current location has an ldesc property defined, and nil otherwise. The third argument is optional. If provided, it returns more specific information about the property definition, and it can be one of the following values, defined in adv.t: +-------------------+---------------------------------+ | DEFINED_ANY | This is the default, and has | | | the same effect as omitting | | | the flag argument. The | | | function returns true if the | | | object defines or inherits the | | | property, nil if not. | +-------------------+---------------------------------+ | DEFINED_DIRECTLY | The function returns true only | | | if the object directly defines | | | the property. If the object | | | doesn't define the property at | | | all, or merely inherits the | | | definition from a superclass, | | | the function returns nil. | +-------------------+---------------------------------+ | DEFINED_INHERITS | The function returns true only | | | if the object inherits the | | | property. If the object | | | doesn't define the property, | | | or defines the property | | | directly rather than | | | inheriting it from a | | | superclass, the function | | | returns nil. | +-------------------+---------------------------------+ | DEFINED_GET_CLASS | The function returns the class | | | where the property is defined. | | | If the object directly defines | | | the property, the function | | | returns the object itself. If | | | the object inherits the | | | property from a superclass, | | | the function returns the | | | superclass from which the | | | property is inherited. If the | | | object doesn't define or | | | inherit the property, the | | | function returns nil. | +-------------------+---------------------------------+ For example, to determine if the object redBook directly defines verDoTake, you could use this code: if (defined(redBook, &verDoTake, DEFINED_DIRECTLY)) "verDoTake is overridden directly in redBook. "; ------------------------------------------------------------ *>delword Call: delword(obj , &prop, word) Deletes the word (a single-quoted string value) from the object's vocabulary for the given part of speech. The prop parameter can be noun, adjective, plural, verb, article, or preposition. You can delete words from any object, including objects defined statically in your game, as well as objects created dynamically at run-time. Furthermore, you can delete words that were added dynamically, as well as words that were statically defined in your game. For examples of using this function, see the section on Dynamic Vocabulary. ------------------------------------------------------------ *>endCommand Call: endCommand(actor, verb, dobj_list, prep, iobj, status) Added: TADS 2.5.0 This function lets you write code that the parser calls at the end of a turn, just after running all of the fuses and daemons for the turn. The parser invokes the endCommand after all of the fuses and daemons have finished running at the end of a turn. The function is called once per command, not per object. In a command with multiple direct objects, this function is called only once, just as fuses and daemons are called only once for the entire command. The "status" parameter has the same meaning as the status code parameter to postAction. The other parameters have the same values as they did in the call to preCommand that the parser makes at the start of the execution phase for the command. endCommand is always invoked at the end of a turn. If an abort statement is executed in the course of a turn, the parser skips directly to endCommand, because abort skips the daemons and fuses. This means that endCommand is executed at the end of a turn even when fuses and daemons are skipped. The endCommand function returns no value. ------------------------------------------------------------ *>execCommand Call: execCommand(actor, verb, dobj, prep, iobj, flags) Added: TADS 2.4.0 This function gives a game program direct access to the parser's command execution system. The function doesn't provide direct access to the string-parsing portion of the parser, but to the command execution portion, which takes the objects involved in the command and executes the command, performing object validation (validDo, validIo), room notification (roomAction), actor notification (actorAction), direct and indirect object checks (dobjCheck and iobjCheck), general object handling (dobjGen and iobjGen), object validation (verIoVerb and verDoVerb), and object action processing (ioVerb, doVerb, or verb.action, as appropriate). For full documentation on execCommand consult Chapter Five. ------------------------------------------------------------ *>exitobj Call: exitobj() Added: TADS 2.4.0 This function provides a new method for skipping the remaining processing for a command at any point. It's very similar to the "exit" statement, but differs in one respect: whereas the "exit" statement terminates all further processing for a command and skips directly to the fuses and daemons, "exitobj" skips the remaining processing only for the current object in a command and proceeds to the next object. This difference is significant when the player types in a command involving multiple objects. For example, suppose that you define a roomAction method in the current room as follows: roomAction(actor, verb, dobj, prep, iobj) = { /* * when the player touches anything not already in their inventory, * make it vanish */ if (dobj != nil && !dobj.isIn(actor)) { "\^<> disappears in a flash of light!\n"; dobj.moveInto(nil); exit; } } Now consider the following transcript: >take ball The ball disappears in a flash of light! >take hammer and chisel The hammer disappears in a flash of light! The first response makes sense, but the second isn't exactly what you wanted. The problem is that the "exit" statement tells the parser to skip processing of any objects other than the current one. To change this, you can simply change the "exit" statement in the code listing above to "exitobj". The result will be more sensible: >take hammer and chisel The hammer disappears in a flash of light! The chisel disappears in a flash of light! "exitobj" is useful when you want to skip the remaining processing for a command for the current object, but you still want the command to be considered successful. "exit" is more suited for situations where the outcome of the command is something less than total success, and you want to skip further processing of other objects involved in the command. "exitobj" is particularly useful with the execCommand() built-in function (see above), because it allows you to completely redirect the processing of a command, skipping all or part of the normal processing for the original command without telling the parser that the original command was unsuccessful. You an use exitobj anywhere you can use exit. ------------------------------------------------------------ *>fclose Call: fclose(filehandle) Closes the file indicated by filehandle. Once the file is closed, no further operations on filehandle are valid. For more information see the section on file operations. ------------------------------------------------------------ *>find Call: find(value, target) If value is a list; the function returns the offset (starting at 1 for the first element) in the list of the target item within the value list. If the target is not found, nil is returned. For example, find([4 5 6], 5) returns 2. If value is a string, in which case target must also be a string, the function will return the offset within the string value of the substring target, or nil if the substring is not found. The offset of the first character in the target string is 1. For example, find('abcdefghij', 'cde') returns 3. ------------------------------------------------------------ *>firstobj Call: firstobj() Alternative Call: firstobj(class) This function is used in conjunction with nextobj(object) to loop over all non-class objects in the game. The firstobj() function returns the first non-class object, and nextobj(object) is used to retrieve subsequent objects in a loop. The order of the objects returned by these functions is arbitrary, but calling firstobj() and then sequentially calling nextobj(object) until nil is returned will guarantee that each non-class object is retrieved exactly once. These functions are useful primarily during initialization to construct lists of all objects satisfying certain search criteria; these lists can be used later in the game to expedite searches for objects with such criteria. For example, code that determines if a room is dark will have to always check to see if a light-providing object, such as a lamp or a burning candle, is present. It will be faster during game play to check only those objects known to be light-providing than to check all objects in the game; to accomplish this, we could set a property called islamp to true in every potential light-providing object, then construct a list of all such objects during initialization with the example code below. getlamps: function { local obj, l; l := []; obj := firstobj(); while(obj <> nil) { if (obj.islamp) l := l + obj; obj := nextobj(obj); } global.lamplist := l; } After initialization, then, it is only necessary to check for the presence of one of the objects in the list global.lamplist, rather than checking all objects in the game, to determine if a room is lit or not. Note that firstobj() will return nil if the game has no non-class objects. Likewise, nextobj(object) returns nil when the last non-class object has been retrieved. The alternative form of this function, with a single argument giving a class object, allows you to restrict the objects returned by firstobj() and nextobj() to those that are subclasses of a particular class. This can save a great deal of time by ignoring objects that are not important to your search. For the example above, you could make the loop execute much more quickly by rewriting it as follows. getlamps: function { local obj, l; l := []; obj := firstobj(lampitem); while(obj <> nil) { l := l + obj; obj := nextobj(obj, lampitem); } global.lamplist := l; } Note that the test for obj.islamp is no longer necessary, because only objects that are subclasses of the class lampitem will be used in the loop. By iterating over a much smaller set of objects, the loop will execute substantially faster. ------------------------------------------------------------ *>firstsc Call: firstsc(obj) Returns the first superclass of the given object. Returns nil if the object has no superclass (which will only be the case if the object was defined as being of type object). This function is provided primarily to facilitate handling equivalent objects. In conjunction with the isEquivalent property, this function lets you determine if two objects are indistinguishable from one another. If two objects have the same immediate superclass, and they both have the isEquivalent property set to true, the two objects are equivalent. ------------------------------------------------------------ *>fopen Call: fopen(filename, mode) Added: Text mode compatibility added with TADS 2.2.4 Opens the file, either text or binary, whose name is given by the single-quoted string value filename. Files written in text mode can be used by other applications (text editors, Web browsers, etc.) as ordinary text files, but binary mode files should only be used by TADS. The file is opened in a manner according to the mode argument, which is a single-quoted string value: `b' - open as a binary file. If you don't specify the file mode, binary is assumed as the default mode, for compatibility with past versions. `r' - open file for reading; file must already exist. `r+' - open file for reading and writing; the file is created if it doesn't already exist. Not currently allowed in text mode. `t' - open as a text file. You can use the text mode in conjunction with `r' (read) and `w' (write) modes - `t' is not currently allowed with `r+' or `w+' modes. Note that text file functionality was added with version 2.2.4 of TADS. `w' - create a new file for writing; the file is deleted if it already exists. `w+' - create a new file for reading and writing; the file is deleted if it already exists. Not currently allowed in text mode. The function returns a file handle that is used in subsequent file operations (fwrite(), fread(), fclose(), and the like) to refer to the open file. If the operation fails, fopen() returns nil. This function can fail for a number of reasons; for example, if you attempt to open a file that doesn't exist with mode `r', the operation will fail because this mode can only be used to open an existing file. For more information on file operations, consult the section at the end of this chapter. ------------------------------------------------------------ *>fread Call: fread(filehandle) Added: Text mode compatibility added with TADS 2.2.4 Reads the next data item from the text or binary file and returns its value. The value will be of the same type as that originally written at the current position in the file with fwrite(). If an error occurs, this function returns nil; this usually indicates that you are attempting to read past the end of the file. When operating in text mode (see fopen() ), fread() always reads a line of text from the file. If the end of the file is not reached, the line returned will end with a `\n' sequence (as with fwrite(), fread() translates newlines according to local conventions, and always returns the TADS `\n' sequence to represent a newline in the file). If fread() encounters the end of the file in the middle of a line, it will return the text up to the end of the file, with no trailing newline. The subsequent call will return nil to indicate that the end of the file has been reached. ------------------------------------------------------------ *>fseek Call: fseek(filehandle, byteposition) Seeks to a byte position in the file. The byteposition value should be a value previously returned by ftell(), since other values may not correspond to a value in the file. ------------------------------------------------------------ *>fseekeof Call: fseekeof(filehandle) Positions the file at its end. ------------------------------------------------------------ *>ftell Call: ftell(filehandle) Returns the current seek position in the file, given as a number of bytes from the beginning of the file. ------------------------------------------------------------ *>fwrite Call: fwrite(filehandle, value) Added: Text file compatibility added with TADS 2.2.4 Writes the given value to the text or binary file. The value argument must be a number, a single-quoted string, or true. Returns nil on success, true on failure; a true return value usually indicates that the disk is full. When a file is opened in text mode, (see fopen()), fwrite() can only be used with string values. Strings passed to fwrite() can contain the escape characters `\t', `\n', and `\\'; other escapes are not allowed. `\t' is translated to a tab, `\n' is translated to a newline (using the appropriate local conventions for the current system), and `\\' is translated to a single backslash. fwrite() does not add any newlines to the text you provide, so you must explicitly include any newlines you want to write to the file. Because TADS obeys local newline conventions, fwrite() always produces the correct sequence of characters for the current machine when you include `\n' in a string, so you don't have to worry about how newlines are handled on each platform. See the section on file handling at the end of this chapter for more information about security modes when writing files in TADS. ------------------------------------------------------------ *>getarg Call: getarg(argnumber) This function returns the argument given by argnumber, which is a number from 1 to the number of arguments to the function (which can be learned with the pseudo-variable argcount). getarg(1) returns the first argument to the current function, getarg(2) returns the second argument, and so forth, up to getarg(argcount). This function can be used to retrieve the arguments to functions taking a variable number of arguments. ------------------------------------------------------------ *>getwords Call: getwords(obj, &prop) This function returns a list of single-quoted strings giving the vocabulary words defined for the given part of speech for the specified object. The prop parameter can be noun, adjective, plural, verb, article, or preposition. For examples of using this function, see the section on Dynamic Vocabulary at the end of this chapter. ------------------------------------------------------------ *>getfuse Call: getfuse(funcptr, parm) This function lets you determine if the indicated fuse is still active, and if so, how many turns are left until it is activated. If the fuse is not active (either it has already fired, or it has been removed with a call to remfuse), this function returns nil. Otherwise, it returns the number of turns before the fuse is activated. Alternative Call: getfuse(obj, &msg) This form of getfuse lets you check on a fuse set with the notify function. If the fuse has already been fired, or has been removed with unnotify, this function returns nil. Otherwise, it returns the number of turns before the fuse is activated. ------------------------------------------------------------ *>gettime Call: gettime(optional argument) Added: Additional information support added with TADS 2.3.0 Returns the current system clock time. The time is returned as a list of numeric values for easy processing. +---------+-------------------------------------------+ | year | calendar year (e.g., 1992). | +---------+-------------------------------------------+ | month | month number (January = 1, February = 2, | | | etc.) | +---------+-------------------------------------------+ | day | number of the day within the current | | | month | +---------+-------------------------------------------+ | weekday | day of the week (1 = Sunday, 2 = Monday, | | | etc.) | +---------+-------------------------------------------+ | yearday | day of the year (1 = Jan 1) | +---------+-------------------------------------------+ | hour | hour of the day on 24-hour clock | | | (midnight = 0, noon = 12, 3 PM = 15, | | | etc.) | +---------+-------------------------------------------+ | minute | minute within the hour (0 to 59) | +---------+-------------------------------------------+ | second | second within the minute (0 to 59) | +---------+-------------------------------------------+ | elapsed | the number of seconds since January 1, | | | 1970, 00:00:00 GMT. This last value is | | | useful for computing the difference | | | between two points in time. | +---------+-------------------------------------------+ In addition, TADS 2.3 added the ability to return additional system real-time clock information. The function optionally takes an argument specifying what type of information to return. Constants for the argument values are defined in adv.t: GETTIME_DATE_AND_TIME - this returns the traditional date and time information that gettime() returned in the past. This is the same information that the function returns if called with no arguments (thus ensuring that existing code that calls gettime() will continue to work unchanged). GETTIME_TICKS - this returns the number of milliseconds since an arbitrary zero point, which is usually some system event, such as starting the current session of the TADS interpreter, or turning on the computer. The actual zero point is arbitrary, but it will remain fixed for a particular session, so you can use this form of gettime() to compute relative times between events over a short period of time. For example, if you're reading events with the new inputevent() function, you can use this time value to set a limit on how long you read events. For example: local max_time, cur_time, evt; /* process events for no more than 5 seconds (5000 milliseconds) */ max_time := gettime(GETTIME_TICKS) + 5000; for (;;) { /* check to see if we've reached our time limit */ cur_time := gettime(GETTIME_TICKS); if (cur_time >= max_time) break; /* get events, but time out if we exceed our time limit */ evt := inputevent(max_time - cur_time); /* process the event */ switch(evt[1]) // and so on } ------------------------------------------------------------ *>incturn Call: incturn() Increments the turn counter. Normally, a daemon is present to call this function once per player command. The turn counter is used to time the execution of fuses, so the user must call incturn() once per turn. This function is not called automatically by the system, since the turn counter should not be incremented after certain events. For example, most system commands, such as saving a game, should not count as a turn. This function is provided so that the game program can decide when to increment the counter. Alternative Call: incturn(num) This form allows you to run a series of turns all at once. You can specify a numeric argument to incturn(); the argument gives the number of turns that should pass. An argument of 1 is equivalent to calling incturn() with no arguments. When an argument higher than 1 is specified, the function runs all of the fuses that are set to turn down within the number of turns specified, but not after that number of turns. Note that the normal incturn() doesn't actually execute any fuses, but simply burns all fuses down by one turn. For example, if you call incturn(2), the system will first run any fuses that are set to burn down after 1 turn, then will shorten all remaining fuses by one turn. Similarly, incturn(3) first runs any fuses that are set to burn down after 1 turn, then runs any fuses set to burn down after 2 turns, then shortens any remaining fuses by 2 turns. Fuses set with setfuse() and notify() are both affected by this routine. Note that this function has no effect on daemons. ------------------------------------------------------------ *>input Call: input() This function stops and waits for the user to enter a line of text (terminated by the return key and edited as normal for command lines), and returns a string containing the text the user enters. This function does not display any prompt, so it is up to the game program to prompt the user before calling this function. As a stylistic point, this function should normally be avoided except for special system functions, since the game will present a more consistent interface if the command line is used for most player input. One possible use is given by the example below. (Note that this code is somewhat simplified; in an actual game, you would also want to call setscore() at the appropriate points, and provide other useful feedback.) die: function { "*** You have died *** \bDo you wish to RESTART, RESTORE, or QUIT? >"; while (true) { local response; response := upper(input()); if (response = 'RESTART') restart(); else if (response = 'RESTORE') { response := askfile(); if (restore(response)) "Failed. "; else abort; } else if (response = 'QUIT') { quit(); abort; } else "Please enter RESTORE, RESTART, or QUIT: >"; } } ------------------------------------------------------------ *>inputdialog Call: inputdialog(icon, prompt, response_list, default_idx, cancel_idx) Added: TADS 2.5.0 This function lets you ask the player a multiple-choice question. On graphical systems, inputdialog() displays a system dialogue box, and lets the user respond by clicking a button. On text systems, this function displays a textual prompt and lets the user respond with the keyboard. The "icon" parameter indicates which icon, if any, to display. The icon will only be displayed on graphical systems; text-only systems ignore this parameter. These icon constants are defined in adv.t: +---------------------+-------------------------------+ | INDLG_ICON_NONE | Do not use any icon. | +---------------------+-------------------------------+ | INDLG_ICON_WARNING | Show a "warning" icon. This | | | indicates a potential or | | | minor problem. (On Windows, | | | this displays an | | | exclamation-point icon.) | +---------------------+-------------------------------+ | INDLG_ICON_INFO | Show an "information" icon. | | | This indicates to the user | | | that the dialogue is being | | | displayed to inform them of | | | the status of an operation. | | | (On Windows, this displays | | | an icon with a small letter | | | "i" in a circle.) | +---------------------+-------------------------------+ | INDLG_ICON_QUESTION | Show a "question" icon. This | | | indicates that additional | | | information from the user is | | | required. (On Windows, this | | | displays a question-mark | | | icon.) | +---------------------+-------------------------------+ | INDLG_ICON_ERROR | Show an "error" icon. This | | | indicates that a problem has | | | occurred. (On Windows, this | | | displays a stop-sign icon.) | +---------------------+-------------------------------+ The "prompt" parameter is the message string to display. For graphical systems, this message is displayed in a dialogue box; for text systems, it's simply displayed on the terminal. The "response_list" is a list of strings giving the valid responses. Each entry in the list is a string giving one possible response. On graphical systems, one button is displayed in the dialogue for each response string; the response string is the button's label. On text systems, the responses are displayed to the player after the prompt string. Each string in the response list can optionally include an ampersand character ("&") before the character that serves as a keyboard short-cut for the response. The ampersand is not displayed in the button label or response list displayed to the player. For example, the response list string `&Yes' makes the "Y" key a short-cut for the button, which is labeled "Yes" in the dialogue. On some systems the short-cut key will be indicated visually in the dialogue; on Windows, for example, the "Y" in the "Yes" button would be underlined to indicate that the letter "Y" is the short-cut for the button. If no ampersand appears in a response list item, the item has no short-cut. On text-only systems, the keyboard short-cut will be indicated visually by enclosing the short-cut letter in parentheses when displaying the list of possible responses to the player. If a response item has no short-cut key, the player must enter a sufficiently long leading substring of the response item so that the response is unambiguous with the other valid responses. Each element of the list can be a number, rather than a string. If an element is a number, it specifies that the button should use a pre-defined standard label. You should use standard labels when possible, because these labels will follow local system conventions and will be localized to the player's language settings; these labels are read from external resources on platforms with appropriate operating system support, so they can be localized easily. To select a standard label, use one of the following values, defined in adv.t: +------------------+----------------------------------+ | INDLG_LBL_OK | "OK", or local system or | | | language equivalent | +------------------+----------------------------------+ | INDLG_LBL_CANCEL | "Cancel" | +------------------+----------------------------------+ | INDLG_LBL_YES | "Yes" | +------------------+----------------------------------+ | INDLG_LBL_NO | "No" | +------------------+----------------------------------+ The strings shown above do not necessarily reflect the actual button text that the player will see, because the actual label will vary by platform and by language. Whatever label is displayed, though, will convey to the user the same meaning. You can also select an entire standard set of buttons, rather than specifying each button individually. If the response_list parameter is a number, rather than a list, it indicates that a standard set of buttons is to be used, selected from a pre-defined list. The advantage of using one of these pre-defined button sets when possible is that the buttons will automatically follow local system conventions and be localized to the player's language settings, on platforms with appropriate operating system support. To select a pre-defined button set, use one of the following values, defined in adv.t, for the response_list parameter: +-------------------+---------------------------------+ | INDLG_OK | The dialogue will display an | | | "OK" button, properly | | | localized. | +-------------------+---------------------------------+ | INDLG_OKCANCEL | The dialogue will display an | | | "OK" button and a "Cancel" | | | button, properly localized. | +-------------------+---------------------------------+ | INDLG_YESNO | The dialogue will display an | | | "Yes" button and a "No" | | | button, properly localized. | +-------------------+---------------------------------+ | INDLG_YESNOCANCEL | The dialogue will display a | | | "Yes" button, a "No" button, | | | and a "Cancel" button, | | | properly localized. | +-------------------+---------------------------------+ The "default_idx" parameter gives the index in the response list of the default response. If the user presses the "Return" key, or performs any other action appropriate to the system user interface that by local convention accepts the default response to a dialogue, this response will be used. The first list entry is at index 1. Pass nil in this parameter if there is no default response, in which case TADS will require the user to select a specific button. (Note that, on some systems, passing nil for this parameter will not make a noticeable difference; on Windows, for example, one of the buttons will always have keyboard focus, so pressing the Return key will always select one of the buttons.) The "cancel_idx" parameter gives the index in the response list of the cancellation response. Most GUI systems have a standard way of cancelling a dialogue; the Escape key has this effect on Windows, for example, as does the Command-Period key combination on the Macintosh. If the user performs the appropriate system-specific action to cancel the dialogue, this response is used. The first list entry is at index 1. Pass nil in this parameter if there is no cancel response, in which case TADS will not allow the player to cancel the dialogue. The dialogue returns the index of the response that the player selects: 1 for the first response in the response list, 2 for the second entry in the list, and so on. For the standard response lists (INDLG_YESNO and so on), the response are in the order described for the constant name: INDLG_YESNO has a "Yes" button at index 1 and a "No" button at index 2, for example. Here's an example of using this function. ret := inputdialog(INDLG_ICON_WARNING, 'What would you like to do next?', ['&Restore', 'Re&start', '&Quit'], nil, 3); switch(ret) { case 1: /* restore a game... */ break; case 2: /* restart the game */ restart(); break; case 3: /* quit */ quit(); break; } On a graphical system, this would display a dialogue with the message text "What would you like to do next?", and three buttons: one with the label "Restore", one with the label "Restart", and one with the label "Quit". If the user presses the "R" key, the "Restore" button would be selected; if the user presses "S", the "Restart" button would be selected; if the user presses "Q", or cancels the dialogue (by pressing the Escape key on a Windows machine, for example), the "Quit" button would be selected. On a text-only system, TADS would display this text on the terminal, on a new line (TADS would output a "\n" sequence to start a new line): What would you like to do next? (R)estore/Re(s)tart/(Q)uit > TADS would then wait for the player to enter a line of text (as with the input() built-in function). If the player enters one letter, TADS would check the letter against each response's short-cut, and return the one that matches. If the player enters more than one letter, TADS would check the string against the leading substring of each possible response; if the string matches one of the responses unambiguously, TADS would return that response. If the player enters something invalid or ambiguous, TADS would redisplay the prompt and await another response. inputdialog() has certain limits. The prompt string can be no longer than 256 characters. There can be no more than ten responses, and the total length of the text in all of the responses must not exceed 256 characters. In addition, to ensure portability, you should choose a reasonably short label for each button; some systems use buttons of a fixed size, so a long label name might not fit in the available space on some systems. Whenever possible, use a single word for each button label. ------------------------------------------------------------ *>inputevent Call: inputevent(timeout) Alternative call: inputevent() Added: TADS 2.3.0 This function multiple types of events and can also apply a timeout to limit the how long it waits for an event to occur. The inputevent() function takes zero or one arguments. With no arguments, inputevent() simply waits until an event occurs. With one argument, which must be a number, inputevent() waits until an event occurs, or until the number of milliseconds specified by the argument has elapsed without an event occurring, in which case the function "times out" and returns without any event having occurred. Note that the timeout value, if given, may not always be obeyed to the exact millisecond. Different types of computers have different system clock resolutions, and additionally multi-user and multi-tasking systems often have unpredictable latencies for event processing. As a result, if you specify a timeout value, the actual time that elapses before the function times out and returns may be slightly longer than the specified timeout value. Any additional latency should be no more than a few hundred milliseconds in most cases, so this shouldn't be noticeable for most purposes. The function returns a list value describing the event that occurred. The first element of the list is a number that specifies the type of the event. The rest of the list varies according to the event type. Constants for the event codes are defined in adv.t. The possible event codes are: +-----------------------+-----------------------------+ | INPUT_EVENT_KEY | The user pressed a key. | | | The second element of the | | | list returned by | | | inputevent() in this case | | | is a string containing the | | | key that the user pressed. | | | The string is the same | | | that would be returned by | | | inputkey() for the same | | | keystroke. | +-----------------------+-----------------------------+ | INPUT_EVENT_HREF | The user clicked on an | | | link. This event is only | | | returned by an HTML TADS | | | interpreter, never by a | | | character-mode TADS | | | interpreter. The second | | | element of the return list | | | is a string containing the | | | text of the HREF that the | | | user clicked. | +-----------------------+-----------------------------+ | INPUT_EVENT_TIMEOUT | No event occurred before | | | the specified timeout | | | elapsed. The return list | | | contains no additional | | | elements. | +-----------------------+-----------------------------+ | INPUT_EVENT_EOF | This indicates that the | | | TADS interpreter is | | | terminating or an error | | | occurred reading an event. | +-----------------------+-----------------------------+ | INPUT_EVENT_NOTIMEOUT | This is not actually an | | | event, but an error | | | indicating that the | | | current system does not | | | support the timeout | | | feature of inputevent(). | | | If this occurs, you can | | | still use inputevent(), | | | but you cannot specify a | | | timeout. The DOS TADS | | | interpreters (TR, TRX, | | | TR32) all support | | | timeouts, as does HTML | | | TADS for Windows. | | | Interpreters on most | | | systems should be able to | | | support this feature, but | | | a few systems may not be | | | able to. | +-----------------------+-----------------------------+ ------------------------------------------------------------ *>inputkey Call: inputkey() Added: Extended keys added with TADS 2.3.0, others added 2.5.0 Reads a single keystroke from the keyboard, and returns a string consisting of the character read. inputkey() takes no arguments. When called, the function first flushes any pending output text, then pauses the game until the player hits a key. Once a key is hit, a string containing the character is returned. inputkey() takes its input directly from the keyboard. It does not look at the command line In addition, the function returns a portable representation for certain extended keys. Before TADS 2.3, keys outside of the normal ASCII character set were almost impossible to use with inputkey(), because TADS returned a useless "raw" format for extended keys, such as cursor navigation keys and function keys. TADS now uses a portable string format to represent many common keys. Each extended key is represented as a string containing a key name enclosed in square brackets. The key name are: +--------------+--------------------------------------+ | [up] | Up arrow | +--------------+--------------------------------------+ | [down] | Down arrow | +--------------+--------------------------------------+ | [right] | Right arrow | +--------------+--------------------------------------+ | [left] | Left arrow | +--------------+--------------------------------------+ | [end] | End of line | +--------------+--------------------------------------+ | [home] | Home | +--------------+--------------------------------------+ | [del-eol] | Delete to end of line | +--------------+--------------------------------------+ | [del-line] | Delete line | +--------------+--------------------------------------+ | [del] | Del (delete character) | +--------------+--------------------------------------+ | [page up] | Page up | +--------------+--------------------------------------+ | [page down] | Page down | +--------------+--------------------------------------+ | [top] | Top of file | +--------------+--------------------------------------+ | [bottom] | Bottom of file | +--------------+--------------------------------------+ | [fN] | Function key N (N is replaced by a | | | number, 1 through 10) | +--------------+--------------------------------------+ | [tab] | Tab | +--------------+--------------------------------------+ | [word-left] | Word left | +--------------+--------------------------------------+ | [word-right] | Word right | +--------------+--------------------------------------+ | [del-word] | Delete word | +--------------+--------------------------------------+ | [bksp] | Backspace | +--------------+--------------------------------------+ | [esc] | Escape | +--------------+--------------------------------------+ | [ctrl-X] | Control-X (X is replaced by a | | | lower-case letter: Control-C is | | | [ctrl-c]) | +--------------+--------------------------------------+ | [alt-X] | Alt-X or Meta-X (X is replaced by a | | | lower-case letter: Alt-F is [alt-f]) | +--------------+--------------------------------------+ In addition, "control" keys (i.e., keys entered by holding down the "control" or "ctrl" key on the keyboard and pressing an alphabetic key) are returned as "[ctrl-X]", where "X" is the lower-case letter key; and "alt" keys are returned as "[alt-X]". Finally, the "Return" and "Enter" keys are returned as "\n", and the tab key is returned as "\t". Even though these key names are portable, be aware that not every computer has all of these keys, so you can't count on the player actually being able to enter them. The only keys that you can always count on being present are the regular ASCII keys, Enter/Return, Tab, and Backspace. So, if you're using these extended keys, you should always be sure to provide an alternative for each extended key using an ordinary key. For example, if you want to implement a menu system that uses the up and down arrow keys to navigate through a set of choices, you could use "N" (for "next") and "P" (for "previous") as synonyms for [down] and [up], respectively. The arrow keys ([up], [down], [left], and [right]) are probably the most portable of the extended keys, since most computers and terminals have some sort of arrow keys. The function keys ([f1] through [f10]) are also available on many systems, although some systems use some or all of the function keys for special purposes; for example, Windows uses the F10 key to begin navigating the menu bar, so your game will never receive the [f10] extended key when running on Windows. The ALT and CONTROL keys are also very non-portable. Here's an example of using the arrow keys. numberVerb: deepverb verb = 'number' action(actor) = { local num; local done; local changed; "Press the Up or Down arrow keys, or the + or - keys, to change the number. Press Enter when finished.\b"; num := 5; changed := true; for (done = nil ; !done ; ) { if (changed) { "\nCurrent value = <>"; changed := nil; } switch(inputkey()) { case '\n': done := true; break; case '+': case '[up]': ++num; changed := true; break; case '-': case '[down]': --num; changed := true; break; } } "\bThe final value was <>. "; } ; ------------------------------------------------------------ *>intersect Call: intersect( list1, list2 ) Returns the intersection of two lists - that is, the list of items in both of the two lists provided as arguments. For example: intersect( [ 1 2 3 4 5 6 ], [ 2 4 6 8 10 ] ) yields the list [ 2 4 6 ] Note that the behavior for lists with repeated items is not fully defined with respect to the number of each repeated item that will appear in the result list. In the current implementation, the number of repeated items that is present in the shorter of the two source lists will be the number that appears in the result list; however, this behavior may change in the future, so you should try not to depend on it. ------------------------------------------------------------ *>isclass Call: isclass(object, class) This function determines if an object is a subclass of a given class. It returns true if object is a subclass of class, nil otherwise. Note that the function scans the entire class tree, so class need not be in the immediate superclass list for object, but can be a superclass of a superclass of object, or a superclass of a superclass of a superclass, and so on. This function should be used to determine if an object is a member of a class, rather than using a special property value that the object inherits from the class. Using isclass is more efficient than using a special inherited property value, because the property value doesn't need to be stored, and because it is much faster to scan the inheritance tree than to check each object in the tree for the property value. ------------------------------------------------------------ *>length Call: length(item) If the item is a string, length() returns the number of characters in the string. If item is a list, length() returns the number of elements in the list. ------------------------------------------------------------ *>logging Call: logging(value) If the value is a string, it specifies the name of an operating system file which will be created (or truncated to zero length if it already exists) and to which subsequent information displayed on the screen is duplicated. That is, a complete log of the play will be copied to the file for later inspection. If the value is nil, it indicates that a log file, if in effect, should be closed and logging halted. Logging is automatically terminated when the player quits the game, but other operations (including saving, restoring, and restarting a game) do not affect logging. This function has no return value. ------------------------------------------------------------ *>lower Call: lower(string) This function returns a copy of string with all of the upper-case letters converted to lower-case. See also the upper(string) function. ------------------------------------------------------------ *>morePrompt Call: morePrompt() Added: TADS 2.2.4 This function allows your game to explicitly display the system MORE prompt. You can use this for such special effects as a dramatic pause prior to a chapter change. The function takes no arguments and returns no value. ------------------------------------------------------------ *>nextobj Call: nextobj(object) Alternative Call: nextobj(object, class) This function is used to retrieve the next object in a search started with firstobj(). The object is the value returned by the call to firstobj() or by the previous nextobj(object) call. The next non-class object after object is returned, and nil is returned if object was the last non-class object. The alternative form, with a second argument giving a class object, returns the next object that is a subclass of the given class. This can be used to restrict the search to a particular class of objects. For an example of the use of this function, see the description of the firstobj() built-in function. ------------------------------------------------------------ *>nocaps Call: nocaps() This function is the opposite of caps() - it specifies that the next character displayed will be converted to lower case. Note that you can use the escape sequence "\v" to achieve the same effect. Note that calls to caps() and nocaps() override one another; if you call caps(), and then immediately call nocaps(), the next character displayed will be lower-case - the call to caps() is forgotten after the call to nocaps(). ------------------------------------------------------------ *>notify Call: notify(object, &message, turns) This function sets up to send the message to the object after the given number of turns has elapsed, or, if turns is zero, after each turn. Note the & before the message parameter; this is required to indicate that the message is to be stored rather than being sent immediately. The message is simply the name of a method defined by the object. The method receives no parameters when it is called. There is a limit of 200 simultaneously pending notifications. With a non-zero number of turns, the notify() function is similar to the setfuse() function, in that it waits a given number of turns and then sends the specified message to the given object. With turns set to zero, notify is similar to setdaemon(), in that the message is sent to the object after each turn. Note that the notify() function is generally the better function to use, since most fuses and daemons are directed at objects, and it is better to keep all code that affects the object in the object's definition, rather than move some into a separate function. See also the unnotify() function, which cancels the effect of the notify() function. An example of the notify() function is shown below. In the example, we define two objects: a bomb, and a button on the bomb. When the button is pushed, it calls notify() to specify that the explode method in the object bomb is to be called in three turns. Notice that we call the bomb's `explode' method with an ampersand. That means we're simply taking its address - we aren't evaluating it at this point. It will actually be evaluated when the notifier is called, in three turns. bomb: item location = bombroom sdesc = "bomb" noun = 'bomb' ldesc = { "The bomb seems to have a small button marked \"detonator\" on it. "; if (self.isActive) "It's ticking loudly. "; } explode = { "The bomb explodes! "; self.moveInto(nil); } ; bombButton: buttonItem location = bomb sdesc = "detonator button" adjective = 'detonator' doPush(actor) = { "The bomb starts ticking. "; notify(bomb, &explode, 3); // Bomb explodes in three turns bomb.isActive := true; } ; ------------------------------------------------------------ *>objwords Call: objwords( num ) Provides a list of the actual words the user typed to refer to an object used in the current command. The argument num specifies which object you're interested in: 1 for the direct object, or 2 for the indirect object. The return value is a list of strings; the strings are the words used in the command (converted to lower case, stripped of any spaces or punctuation). If a special word such as "it," "them," or "all" was used to specify the object, the list will have a single element, which is the special word used. For example, if the player types "take all," then objwords(1) will return [ 'all' ] and [ objwords2 ] will return []. Note that objwords(1) will return [ 'all' ] even if the player typed a variation on "all," such as "take everything" or "take all but the book." If the player types "put all in red box," the objwords(1) returns [ 'all' ] and objwords(2) returns [ 'red' 'box' ]. If the player uses multiple direct objects, the function will return the current object's words only. For example, if the player types "put blue folder and green book in red box," objwords( 1 ) will return [ 'blue' 'folder' ] while the first direct object is being processed, and [ 'green' 'book' ] while the second object is being processed. This function could potentially be useful in such cases as "ask actor about object," because it allows you to determine much more precisely what the player is asking about than would otherwise be possible. You can call objwords() during a verb verification or action method (verDoVerb, verIoVerb, doVerb, ioVerb), and during a doDefault method. Note that the return value is slightly different during a doDefault method: if the word"all" is used in the command, the function will return the list [ 'A' ] rather than [ 'all' ], due to the internal order of processing of the word list. objwords( 1 ) words with doDefault You can also call objwords( 1 ) from within the doDefault method, as described above. Until version 2.2, you could call objwords( 1 ) only from verb verification and action methods. ------------------------------------------------------------ *>outcapture Call: outcapture( stat ) TADS lets you capture the text displays that are generated by double-quoted strings and by the say() built-in function. This feature allows you to examine the values of methods such as ldesc and sdesc. Since these methods display text, there is no direct way of manipulating their text as strings. The output capture feature makes it possible for you to examine and manipulate any text that ordinarily would simply be displayed. To use this feature, you first tell TADS that you wish to begin capturing output by calling the built-in outcapture function: stat := outcapture( true ); This begins capturing output. The return value is a status code that you use in a subsequent call to turn off output capturing. This status code allows output capturing calls to be "nested," since the status code allows the capturing status that was in effect on a call to begin capturing to be restored on the subsequent call to end capturing. This status code is for use by TADS only - the only thing you do with it is use it in the subsequent call to end capturing. While output is being captured, any text that would normally be displayed is instead saved by the system. The user does not see any text displayed while capturing is in effect. After you begin capturing, simply call any methods whose displays you want to examine. For example, you could call redbook.sdesc if you want to examine the short description of the object redbook. After you've finished calling the methods whose displays you want to examine, end capturing by calling outcapture() again, passing as the argument the status code returned by the first call: str := outcapture( stat ); This second call tells TADS to stop capturing output. The return value of this function is a (single-quoted) string containing all of the text that was displayed since the corresponding call to outcapture(true). When text is being captured, TADS expands all format strings (strings such as "%you%"), and processes "\^" and "\v" sequences (which convert the next character to upper- and lower-case, respectively). However, all other escape sequences (such as "\n" and "\t") are left in the string intact. You can display the string returned from the call to outcapture(stat) using the say() built-in function. This should result in the same text display that would have occurred if you hadn't turned on capturing. You can nest calls to outcapture(). As explained above, the status code returned from the first call is used to restore the capturing status of the previous call. The string returned by a call to turn off capturing includes only text that was generated after the corresponding call to turn on capturing. Because calls to outcapture() can be nested, you don't have to worry when using text capturing about whether any methods you're calling also use the function. Note that the system automatically turns off output capturing whenever it prompts the user for a command, for a missing direct or indirect object, or for disambiguation information. When the system turns off capturing, it clears the captured text, so any subsequent calls to turn off capturing return empty strings. You therefore can only capture text within a single command line. ------------------------------------------------------------ *>outhide Call: outhide( flag ) Turns hidden output on or off, simulating the way the parser disambiguates objects. The parameter flag is either true or nil. When you call outhide(true), the system starts hiding output. Subsequent output is suppressed - it is not seen by the player. When you call outhide(nil), the system stops hiding output - subsequent output is once again displayed. outhide(nil) also returns a value indicating whether any (suppressed) output was generated since the call to outhide(true), which allows you to determine whether any output would have resulted from the calls made between outhide(true) and outhide(nil). This is the same mechanism used by the parser during disambiguation, so it should not be called by a verDoVerb or verIoVerb method. This function is provided to allow you to make calls to verDoVerb and verIoVerb to determine if they will allow a particular verb with an object, just as the parser does. There is no way to recover the text generated while output is being hidden. The only information available is whether any text was generated. Alternative Call: outhide(flag) - nested calls When you call outhide(true), the function returns a status indicator, which is a value that can be used in a subsequent call to outhide() to restore output hiding to the state it was in before the outhide(true). This allows you to nest text hiding - you can hide output in a subroutine or method call, without interfering with the routine that called you or any routines you call. To use the nested form, save the return value of outhide(true), and then use the saved value as the parameter - in place of nil - to the subsequent call to outhide(). The value returned by the second outhide() indicates whether any text output occurred between the nested calls. For example: local old_stat1, old_stat2; local new_stat1, new_stat2; old_stat1 := outhide(true); "This is some hidden test."; old_stat2 := outhide(true); // don't write any text here new_stat2 := outhide(old_stat2); new_stat1 := outhide(old_stat1); Because outhide(old_stat2) indicates whether any output occurred during the nested outhide(true), new_stat2 is nil. However, new_stat1 is true, since output occurred after the first outhide(true). Consider another sequence: old_stat1 := outhide(true); // write no text here old_stat2 := outhide(true); "This is some hidden text."; new_stat2 := outhide(old_stat2); new_stat1 := outhide(old_stat1); In this case, both new_stat1 and new_stat2 will be true, because hidden output occurred within both nested sections (even though only a single message was displayed, it was within both nested sections). The general form of a nested outhide() section is: { local old_stat; local new_stat; old_stat := outhide(true); // do whatever you want here // output will be hidden new_stat := outhide(old_stat); } The new_stat will indicate whether any output occurred between the outhide(true) and the outhide(old_stat). In addition, output hiding will be restored to the same state as it was prior to the outhide(true). ------------------------------------------------------------ *>parseAskobjIndirect Call: parseAskobjIndirect(actor, verb, prep, noun_info) Added: TADS 2.5.1 This function provides more control over the message that the parser uses to ask the player to supply an indirect object when none is provided in the command but the verb requires one. It supplements the existing parseAskobj() and parseAskobjActor() functions, and is fully documented in Chapter Six. ------------------------------------------------------------ *>parseNounList Call: parseNounList(wordlist, typelist, startingIndex, complainOnNoMatch, multi, checkActor) Added: TADS 2.4.0 This function allows the game to invoke the parser's internal function to parse a noun list. This can be used in conjunction with the parseUnknownVerb to allow the function to interpret part of the word list as a noun list. The function is documented in Chapter Five. ------------------------------------------------------------ *>parseNounPhrase Call: parseNounPhrase(wordlist, typelist, current_index, complain_on_no_match, is_actor_check) Added: TADS 2.4.0 This parser hook lets the game program parse noun phrases and perform the initial resolution to possible matching objects. For more information on this function consult the parseNounPhrase section in Chapter Six. ------------------------------------------------------------ *>parserDictLookup Call: parserDictLookup(tokenList, typeList) Added: TADS 2.4.0 This function lets you perform a parser dictionary look-up to obtain the list of objects that define a set of vocabulary words. This function can be used to perform your own noun-phrase parsing, and is documented fully in Chapter Five. ------------------------------------------------------------ *>parserGetMe Call: parserGetMe( ) Added: TADS 2.2.4 The parser provides a way to change the player character dynamically during game play. The parser in earlier versions of TADS used the object named "Me" to represent the player character; there was no way to change this. This made it difficult to write a game with different personas for the player character. TADS now lets you specify other player characters, and you can use the built-in function parserGetMe() to get the parser's current player character object. This function takes no arguments. Note that adv.t and std.t no longer refer to the "Me" object directly in code related to the player character's status (such as inventory or room location descriptions). Instead, adv.t and std.t use the new parserGetMe() function to get the player character object. If you use parserSetMe() (see below) in your game, you should be careful not to refer to the "Me" object directly in contexts where you really want the current player character object. Use parserGetMe() instead to get the correct object from the parser. Note that existing games should not be affected by this change; if you don't call parserSetMe(), then parserGetMe() will always return the "Me" object, so you can safely use a fixed "Me" object. ------------------------------------------------------------ *>parserGetObj Call: parserGetObj(argument) Added: TADS 2.4.0 This function lets the game program learn the objects involved in the current command. The function takes a single argument, which indicates which object you want, using the following constants (defined in adv.t): PO_ACTOR - the current command's actor PO_VERB - the deepverb object for the command's verb PO_DOBJ - the direct object PO_PREP - the preposition object introducing the indirect object PO_IOBJ - the indirect object The return value is an object, or nil if there is no such object for the current command. For example, a command with no indirect object will return nil for PO_PREP and PO_IOBJ. Other aspects of this function are documented in detail in Chapter 6. ------------------------------------------------------------ *>parserGetTokTypes Call: parserGetTokTypes(tokenList) Added: TADS 2.4.0 This function lets you obtain a list of token types given a list of tokens. You can use this function to get the types of the tokens in a list returned by parserTokenize(), for example. It's documented fully in Chapter Five. ------------------------------------------------------------ *>parserSetMe Call: parserSetMe(newMe) Added: TADS 2.2.4 As noted above, you can switch the player character when you desire. The parser still uses "Me" as the initial player character, but you can change to a different object at any time using the new built-in function parserSetMe(newMe). The argument to parserSetMe() is the new object to use to represent the player character. ------------------------------------------------------------ *>parserResolveObjects Call: parserResolveObjects(actor, verb, prep, otherobj, usageType, verprop, tokenList, objList, silent) Added: TADS 2.4.0 This function lets you access the parser's object resolution and disambiguation subsystem programmatically. You can use the object resolver in conjunction with parseNounList() or with your own noun-list parser to implement your own command parsing system. It's documented in Chapter Five. ------------------------------------------------------------ *>parserTokenize Call: parserTokenize(commandString) Added: TADS 2.4.0 This function lets the game invoke the parser's tokenizer to obtain a token list for a string of text. The function is documented extensively in Chapter Five. ------------------------------------------------------------ *>parseUnknownVerb Call: parseUnknownVerb(actor, wordlist, typelist, errnum) Added: TADS 2.4.0 This parser hook lets the game take control whenever the parser encounters an unknown verb, or unknown sentence syntax. `actor' is the current actor object. The `wordlist' parameter is a list with the strings of the words in the command, in the same format as the list that is passed to preparseCmd. The `errnum' parameter is the parser error number for the condition that caused the call to parseUnknownVerb; this is the same error number that is passed to parseError (and related functions). The `typelist' argument is a list of the types of the words in the `wordlist' parameter. Each element of `typelist' gives the word type of the corresponding element of `wordlist' (so typelist[3] gives the type of the word in wordlist[3], for example). Each type is a number, which can contain any number of the values below combined with the bitwise OR operator ("|"). To test for a particular type, use an expression like this: ((typelist[3] & PRSTYP_NOUN) != 0). For more information consult the parseUnknownVerb section in Chapter Six. ------------------------------------------------------------ *>postAction Call: postAction(actor, verb, dobj, prep, iobj, status) Added: TADS 2.5.0 This function lets you write code that the parser calls immediately after the "action" method, and before any fuses or daemons. The parser calls postAction once for each object in a command with multiple direct objects, just after it calls the "action" method for the object. If the command is terminated early with exit, exitobj, or abort, the parser invoked postAction immediately after the exit, exitobj, or abort statement executes. The first five parameters specify the current command; any of the objects except "actor" and "verb" can be nil. The "status" parameter has the same meaning as the return codes from the execCommand built-in function; it can be one of the following values, defined in adv.t: +------------+----------------------------------------+ | EC_SUCCESS | The command executed successfully, | | | which indicates that everything up | | | through and including the command's | | | "action" method (verb.action, | | | dobj.doAction, or iobj.ioAction, as | | | appropriate). | +------------+----------------------------------------+ | EC_EXIT | An exit statement was executed. | +------------+----------------------------------------+ | EC_EXITOBJ | An exitobj statement was executed. | +------------+----------------------------------------+ | EC_ABORT | An exit statement was executed. | +------------+----------------------------------------+ The postAction function returns no value. ------------------------------------------------------------ *>preCommand Call: preCommand(actor, verb, dobj_list, prep, iobj) Added: TADS 2.5.0 At the start of the execution phase, before calling verbAction() for the first direct object in the command, the parser invokes this function. The "actor", "verb", "prep", and "iobj" parameters are objects giving the actor, verb, preposition, and indirect object in the command, respectively. The "dobj_list" parameter is a list of the direct objects in the command, in the same order as they appear in the command. This function can use exit to skip to fuses and daemons, or it can use abort to cancel the command entirely, in which case the parser will skip directly to the endCommand function (see below). If this function simply returns, the parser continues processing the command as normal. ------------------------------------------------------------ *>proptype Call: proptype(object, propPointer) This function determines the datatype of a property of an object, without actually evaluating the property. This can be useful if you want to find out what kind of value the property has, without activating any side effects of evaluating the property (for example, displaying a double-quoted string contained by the property). The return value is a number, and the possible values are a superset of those returned by the datatype() built-in function. 1 - Number 2 - Object 3 - String 5 - nil 6 - Code (executable statements enclosed in braces) 7 - List 8 - true 9 - Double-quoted string 10 - Function Pointer 13 - Property Pointer Note that the propPointer argument must be a property pointer. You can get a property pointer by using the & operator on a property name. For example, to determine the type of the north property of the player's current location, you would do this: x := proptype(Me.location, &north); Note that using proptype() is very different from using the datatype() built-in function on the value of the property, because proptype() does not evaluate the property in order to determine its type. This has several implications. First, and most importantly, it allows you to avoid side effects of evaluating the property (such as displaying a double-quoted string value) if you don't desire the side effects. Second, it means that you will find out if a property contains code, but you will not learn what type of value (if any) the code will return. ------------------------------------------------------------ *>quit Call: quit() Terminates the game. Actually, this function flags that the game is to be ended the next time a command will be parsed, so normal return codes should be passed back when using quit(). Hence, after calling quit(), you should always immediately execute an abort statement. See the example given in the description of the input() built-in function for an illustration of the use of quit(). ------------------------------------------------------------ *>rand Call: rand(upper-limit) This function returns a random number from 1 to the upper-limit given. (The upper-limit must be a positive number.) See also the discussion of randomize(), below. ------------------------------------------------------------ *>randomize Call: randomize() This function "seeds" the random number generator with a new value. The value is implementation-defined, and comes from such a source as the system clock. Until randomize() is called, the random number generator will always produce a fixed sequence of random numbers; once randomized, however, the sequence is unpredictable. For testing purposes, you can leave the call to randomize() out of your program, because the game will always run repeatably. Once the game is working, you can simply put a call to randomize() in your init() function in order to make the game unpredictable for your players. ------------------------------------------------------------ *>reGetGroup Call: reGetGroup() Added: TADS 2.3.0 Note: this function is dependent upon the reSearch() function. Please read the documentation on that first. reGetGroup(), lets you retrieve the matching text for parenthesized groups within regular expressions. reGetGroup() returns information about the last call to reSearch(). The function takes one argument, which is a number giving the group number to retrieve: the first parenthesized expression is group 1, the second is group 2, and so on. (When groups are nested, the position of the open parenthesis is used to determine the group numbering. The leftmost open parenthesis is numbered as group 1.) reGetGroup() returns nil if there is no such group in the most recent regular expression or if the last call to reSearch() did not match the expression. Otherwise, reGetGroup() returns a list with three elements identifying the group. The first element is a number giving the character position within the original search string of the start of the text that matched the group. The second element is the length of the text that matched the group. The third element is the actual text that matched the group. Here's a code example: ret := reSearch('d(.*)h', 'abcdefghi'); if (ret != nil) { grp := reGetGroup(1); if (grp != nil) "Start = <>, len = <>, text = \"<>\". "; } This will display the following: Start = 5, len = 3, text = "efg". You can use regular expression grouping to carry out complicated transformations on strings with relatively little code. Since you determine exactly where in a string a particular group in a regular expression occurs, you can take the group text out of the string and put it back into the string in a different order or with other changes. For example, suppose you want to write a preparse() function that finds sentences of the form "tell to " and converts them to the normal TADS actor command format, ", ". You can use regular expression grouping to find this pattern of text and build the new command from pieces of the original: ret := reSearch('^ *tell%> *(.*)% *(.*)', cmd); if (ret != nil) cmd := reGetGroup(1)[3] + ', ' + reGetGroup(2)[3]; Or, suppose you have a telephone in your game, and you want to let the player dial numbers on the phone using normal North American telephone number notation, including an area code. The TADS parser won't normally let you do this, since it would try to parse the number as several words. You could solve this problem using preparse: after the player enters a command, find anything that looks like a telephone number, and enclose it in quotation marks; this will make the parser treat the phone number as a quoted string, so you can write your "dial" verb so that it uses strObj as the direct object. Here's how you could write the preparse routine: ret := reSearch('(%([0-9][0-9][0-9]%) *)?' + '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]', cmd); if (ret != nil) cmd := substr(cmd, 1, ret[1] - 1) + ' "' + ret[3] + '" ' + substr(cmd, ret[1] + ret[2], length(cmd)); ------------------------------------------------------------ *>remdaemon Call: remdaemon(function, value) Removes a daemon set previously with setdaemon(). The daemon will no longer be called. Note that if a given function has been set as a daemon multiple times, a call to remdaemon() only removes the first such daemon; hence, you must call remdaemon() as many times as you called setdaemon() on a particular function if you wish to cancel all calls to it. Note also that the value given must match the value used in the original call to setdaemon(); this allows you to set a daemon several times, with different contexts (the context being given by the value), and selectively remove the daemons later. Also, removing a daemon that isn't running does nothing. ------------------------------------------------------------ *>remfuse Call: remfuse(function, value) See also the setfuse() built-in function. This function removes a fuse previously set, or does nothing if the named function has not been set as a fuse. The fuse will not be executed if removed prior to burning down. Note that if a function has been set as a fuse multiple times, remfuse() will remove only the first such fuse set; hence, you must call remfuse() on a function as many times as you called setfuse() on the function if you wish to cancel all calls to it. Note also that the value given must match the value used in the original call to setdaemon(); this allows you to set a fuse several times, with different contexts (the context being given by the value), and selectively remove the fuses later. Removing a fuse that isn't set does nothing. ------------------------------------------------------------ *>reSearch Call: reSearch(pattern, string_to_search) Added: TADS 2.3.0 TADS has a built-in regular expression matching facility. Regular expressions provide a powerful and simple way to search for a complex pattern within a text string. This feature is particularly useful for writing preparse() and preparseCmd() functions, since it allows you to perform complex pattern matching and replacing with very little code. The function reSearch() searches for the first occurrence of a regular expression pattern within a string. It returns nil if the pattern is not found. If the pattern is found, the function returns a list, the first element of which is a number giving the character position within the string of the start of the match (the first character is at position 1), the second element giving the number of characters in the match, and the third element a string giving the actual text of the match. The pattern is specified using regular expression syntax similar to that used by "grep" and other similar utilities. Here are the basic building blocks of the regular expression syntax: +------+-----------------------------------------+ | | | Alternation: matches the expression on | | | the left or the expression on the | | | right. This operator affects as many | | | characters as it can, out to the | | | nearest parentheses. | +------+-----------------------------------------+ | ( ) | Groups an expression. | +------+-----------------------------------------+ | + | Indicates that the immediately | | | preceding character or parenthesized | | | expression repeats one or more times. | +------+-----------------------------------------+ | * | Indicates that the immediately | | | preceding character or parenthesized | | | expression repeats zero or more times. | +------+-----------------------------------------+ | ? | Indicates that the immediately | | | preceding character or parenthesized | | | expression can occur zero or one time. | +------+-----------------------------------------+ | . | (a period/full stop) Wildcard: matches | | | any single character. | +------+-----------------------------------------+ | ^ | Matches the beginning of the string. | +------+-----------------------------------------+ | $ | Matches the end of the string. | +------+-----------------------------------------+ | % | Quotes the following character, | | | removing the special meaning of these | | | characters: | . ( ) * ? + ^ $ % [ Also | | | introduces the special sequences | | | listed later. | +------+-----------------------------------------+ | [ ] | Indicates a character list or range | | | expression. Matches any one of the | | | listed characters. A range can be | | | specified by following a character | | | with `-' and another character; this | | | matches all of the characters between | | | and including these two characters. | | | For example, [a-z] matches any one | | | lower-case letter, and [0-9] matches | | | any one digit. Ranges and single | | | characters can be combined; for | | | example, [a-zA-Z] matches any letter, | | | upper- or lower-case. To include the | | | character `]' in a list, make it the | | | first character after the opening | | | bracket; to include `-', make it the | | | next character after that. For | | | example, []] matches just `]', [-] | | | matches just `-', and []-] matches `-' | | | and `]'. | +------+-----------------------------------------+ | [^ ] | Exclusionary character list or range. | | | This matches any character except the | | | ones listed. For example, [^0-9] | | | matches anything single character | | | except a digit. | +------+-----------------------------------------+ | %1 | This matches the same text that | | | matched the first parenthesized | | | expression. For example, consider the | | | pattern `(a*).*%1'. The string | | | `aaabbbaaa' will match, because the | | | first three characters match the | | | parenthesized `a*' expression, which | | | causes `%1' to match the last three | | | characters; the middle three | | | characters are matched by the `.*' | | | expression. | +------+-----------------------------------------+ | %2 | Matches the text matching the second | | | parenthesized expression. | +------+-----------------------------------------+ | | And so on through... | +------+-----------------------------------------+ | %9 | Matches the text matching the ninth | | | parenthesized expression. | +------+-----------------------------------------+ | %< | Matches at the beginning of a word. | | | Words are considered to be contiguous | | | groups of letters and numbers. | +------+-----------------------------------------+ | %> | Matches at the end of a word. For | | | example, `%' matches the "and" | | | in `ball and box' and `and then', but | | | not in `rubber band' or `the android'. | | | Note that %< and %> do not actually | | | contribute any characters to the match | | | - they simply ensure that they fall on | | | a word boundary. So, searching for | | | `%' in `ball and box' matches | | | the string `and' - the spaces are not | | | included in the match. | +------+-----------------------------------------+ | %w | Matches any word character (a letter | | | or a digit). | +------+-----------------------------------------+ | %W | Matches any non-word character | | | (anything but a letter or digit). | +------+-----------------------------------------+ | %b | Matches at any word boundary | | | (beginning or end of a word). | +------+-----------------------------------------+ | %B | Matches except at a word boundary. | +------+-----------------------------------------+ Any character other than those listed above simply matches the exact same character. For example, `a' matches `a'. Here are some examples of simple regular expressions, to help clarify the meanings of the basic building blocks: +-----------+------------------------------------+ | abc|def | either `abc' or `def' | +-----------+------------------------------------+ | (abc) | `abc' | +-----------+------------------------------------+ | abc+ | `abc', `abcc', `abccc', etc. | +-----------+------------------------------------+ | abc* | `ab', `abc', `abcc', `abccc', etc. | +-----------+------------------------------------+ | abc? | `ab' or `abc' | +-----------+------------------------------------+ | . | any single character | +-----------+------------------------------------+ | ^abc | `abc', but only at the start of | | | the string | +-----------+------------------------------------+ | abc$ | `abc', but only at the end of the | | | string | +-----------+------------------------------------+ | %^abc | literally `^abc' | +-----------+------------------------------------+ | [abcx-z] | `a', `b', `c', `x', `y', or `z' | +-----------+------------------------------------+ | []-] | `]' or `-' | +-----------+------------------------------------+ | [^abcx-z] | any character except `a', `b', | | | `c', `x', `y', or `z' | +-----------+------------------------------------+ | [^]-q] | any character except `]', `-', or | | | `q' | +-----------+------------------------------------+ Here are some more complicated examples: (%([0-9][0-9][0-9]%) *)?[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9] This matches a North American-style telephone number, either with or without an area code in parentheses. If an area code is present, it can optionally be separated by spaces from the rest of the number: `(415)555-1212', `555-1212', `(415) 555-1212'. [-+]?([0-9]+%.?|([0-9]*)%.[0-9]+)([eE][-+]?[0-9]+)? This matches a floating-point number in the notation used by C and some other programming languages: either a string of digits optionally ending with a decimal point, or zero or more digits followed by a decimal point followed by one or more digits; optionally followed by an exponent specified with the letter "E" (upper- or lower-case), an optional sign (`+' or `-'), and one or more digits; all of this can be preceded by an optional sign. This matches: `3e9', `.5e+10', `+100', `-100.', `100.0', `-5e-9', `-23.e+50'. ^ *tell%>(.*)%(.*) This matches the word "tell" at the beginning of the string, preceded only by zero or more spaces, followed by any text, followed by the word "to", followed by any more text. This matches `tell bob to go north' and `tell teeterwaller to give me the mask'. Here's a code example: ret := reSearch('d.*h', 'abcdefghi'); if (ret = nil) "No match."; else "Start = <>, length = <>, text = \"<>\". "; When run, this code will display the following: Start = 4, length = 5, text = "defgh". ------------------------------------------------------------ *>resourceExists Call: resourceExists(resource) Added: TADS 2.5.1 This function allows you to determine whether a named resource (such as a JPEG image or an MP3 audio file) can be loaded. This function takes as its single argument a string giving the name of a resource to find; the function returns true if the resource can be loaded, nil if not. The function returns nil if the interpreter is simply not capable of loading resources at all (the text-only interpreters thus always return nil for this function), or if the resource can't be found. The function returns true only if the interpreter is capable of loading resources at all, and the resource is available. If you're writing a multimedia game for the HTML interpreters, but you also want your game to work on text-only systems, you can use this function for finer control over the presentation on the text systems; you might, for example, want to provide additional text to make up for missing graphics or sounds. You can also use this function if you're planning to distribute your game in different subset versions in order to provide players with different options for download and install sizes; if you make some of your graphics optional (by bundling some into a separate ".RSn" resource file, for example), you can use resourceExists() to detect at run-time which resources the player has chosen to install, and customize the presentation accordingly. Here's an example that checks to see if a JPEG image is available for display: if (!resourceExists('images/title.jpg')) { // the title graphic isn't available - display a text version ... } ------------------------------------------------------------ *>restart Call: restart() This function starts the game over again from the beginning. It does not return. Alternative Call: restart( funcptr, param ) This form of restart() allows you to specify a function to be called after restarting the game, but before the init() function is invoked. This new feature has been added because it would otherwise be impossible to pass any information across a restart operation: the restart() function does not return, and all game state is reset to its initial state by restart(). You can use this function if you want a restarted game to have different startup behavior than the game has when it's first started. Note that adv.t passes a pointer to the initRestart function (defined in adv.t when it invokes restart() in response to a "restart" command; the adv.t implementation of initRestart() simply sets the flag global.restarting to true to indicate that the game is being restarted rather than first entered. The param value is simply passed as the parameter to the function to be called, and this allows you to pass information through the reset. For example, if you start the game with a questionnaire asking the player's name, sex, and age, you could pass a list containing the player's responses to your restart function, and have the restart function store the information without making the player answer the questionnaire again. The call to restart() in adv.t uses global.initRestartParam as the parameter for the initRestart() function; so, if you provide your own version of initRestart() that makes use of the parameter information, you can simply store the necessary information in global.initRestartParam to ensure that it's passed to your function at the appropriate time. ------------------------------------------------------------ *>restore Call: restore(filename) Added: Return values altered 2.5.0 The filename is a string specifying an operating system file which was created with the save() function. The state of the previously saved game is restored, so that the players can continue where they left off. If successful, nil is returned; otherwise, an error occurred restoring the game from the file, and a numeric code wil be returned. Note that the game to be restored must have been saved by the identical version of the TADS software and of the game itself. The system will refuse to restore a game saved by a different version of the TADS software, or by a different version of your game program (as determined by a timestamp stored in the game by the compiler). As of TADS 2.5.0 the return value in the event of an error is a number. Different values are returned for different error conditions, which makes it possible to provide better information to the player about the specific problem that caused the operation to fail. The return values, defined in adv.t, are: +------------------------+----------------------------+ | RESTORE_SUCCESS | Success | +------------------------+----------------------------+ | RESTORE_FILE_NOT_FOUND | The file to be restored | | | does not exist (or could | | | not be opened for some | | | other reason). | +------------------------+----------------------------+ | RESTORE_NOT_SAVE_FILE | The file is not a valid | | | saved game. | +------------------------+----------------------------+ | RESTORE_BAD_FMT_VSN | The file was saved by an | | | incompatible version of | | | the TADS Interpreter | +------------------------+----------------------------+ | RESTORE_BAD_GAME_VSN | The file was saved by a | | | different game, or by a | | | different version of the | | | same game. | +------------------------+----------------------------+ | RESTORE_READ_ERROR | An error occurred reading | | | the file. This could | | | indicate that the file | | | was corrupted, or that | | | the physical medium | | | containing the file is | | | damaged. | +------------------------+----------------------------+ | RESTORE_NO_PARAM_FILE | No parameter file has | | | been specified. This is | | | returned only when | | | restore(nil) is called to | | | attempt to load the file | | | specified by a start-up | | | parameter; it indicates | | | that there is in fact no | | | parameter file to load. | +------------------------+----------------------------+ For compatibility with older versions of TADS, RESTORE_SUCCESS is defined as 0, and all of the other values are non-zero. In most cases, this should allow existing code (that assumes the nil/true return value) to continue working without changes, since if (restore(fname)) will continue to have the same effect with this change. Only old code that explicitly compared the return value to nil or true will need to be changed. Alternative Call: restore( nil ) This form of the restore() function allows you to choose the time during startup that the player's saved game is restored when the player started your game with a saved game already specified. When you call restore(nil), the system checks to see if a saved game was specified by the player at startup, and if so, immediately restores the game and returns nil. If no game was specified, the function returns true. It is possible for a player to start a game in this manner only on windowed GUI TADS runtimes, such as those that run on the Macintosh and under Windows, but the new restore() functionality will work correctly on all platforms. On the Macintosh, the operating system allows an application to be started by opening one of the application's documents from the desktop. When this happens the application is started and informed that the user wishes to open the specified file. Saved game files on the Macintosh are associated with the game executable that created them in such a way that the game is executed when a saved game is opened. This is simply a convenience feature on the Macintosh that allows a player to run a game and restore a saved position in a single operation. You can use restore(nil) in your init function to choose the point at which the saved game is restored. If your game has extensive introductory text, you could call restore(nil) (and return if the function returns nil) prior to displaying the introductory text, since the player has presumably already seen it anyway. The reason that the system doesn't restore the saved game prior to calling your init function is that you may want parts of your init function to be invoked regardless of whether a game is going to be restored or not. For example, you may wish to display your copyright message, or ask a question for copy protection, every time the game starts, even when a saved game is going to be restored. If you do not make a call to restore(nil) in your init function, the system will automatically restore the saved game specified by the player at startup immediately after your init function returns. Hence, omitting the call to restore(nil) doesn't do any harm; this function is provided to give you greater control ------------------------------------------------------------ *>rundaemons Call: rundaemons() Runs all of the daemons. The function runs daemons set both with setdaemon() and notify(). This function returns no value. ------------------------------------------------------------ *>runfuses Call: runfuses() Runs all expired fuses, if any. Returns true if any fuses expired, nil otherwise. This function runs fuses set both with setfuse() and notify(). ------------------------------------------------------------ *>save Call: save(filename) The filename is a string specifying an operating system file to be used to save the current status of the game. Everything about the game is saved in the file, so that the exact point in the game can be later restored with the restore() function. If successful, nil is returned; otherwise, an error occurred saving the file. ------------------------------------------------------------ *>say Call: say(value) The value can be either a number or a string. If it's a number, its decimal value is printed. A string is displayed as it is. There is no return value. ------------------------------------------------------------ *>setdaemon Call: setdaemon(function, value) Sets a daemon. Each turn, all outstanding daemons are called once, after all player command processing is completed. The value given is passed to the function as its sole parameter each time the function is called. Note: the function must be known when the compiler reads this statement, which means the function must have been defined or at least called prior to the setdaemon() call in the source file. You can forward-declare a function by using a statement like this: test: function; Note that the actual definition of the function test will appear later in the file; the statement above only declares it as a function so it can be used in the setdaemon() or similar call. At most 100 daemons can be running simultaneously; a run-time error will result if a call to setdaemon() is made when 100 daemons are already running. There is no return value. See also the notify() function. ------------------------------------------------------------ *>setfuse Call: setfuse(function, time, value) Sets a fuse of the specified length. The function is the identifier naming a function. The time is a positive number specifying the number of turns before the fuse burns down. After the specified number of turns, function is called with value as its sole parameter. Note: the function must be known as a function when the compiler reads this statement, which means the function must have been defined or at least called prior to the setfuse() call in the source file. See the note in the description of setdaemon(), above, for more information. There is limit of 100 simultaneously pending fuses; a run-time error results if setfuse() is called when 100 fuses are already pending. There is no return value from setfuse(). Note that fuses differ from daemons in that a fuse is only called once, at a time determined when the fuse is set; once called, it is automatically removed from the list of pending fuses. Daemons, on the other hand, are called once per turn until explicitly removed. ------------------------------------------------------------ *>setit Call: setit(object) Sets the antecedant of "it," as used in a player's command, to the specified object. This is useful if a complaint refers to an object which will draw the player's attention; consider the following dialogue: >go north The metal door is closed. >open it There is a padlock preventing you from opening the door. >unlock it What do you want to unlock the padlock with? >the key The padlock swings open. In this example, setit() has been used each time a complaint refers to an object which the user might wish to manipulate due to the complaint. There is no return value. Alternative Call: setit( nil ) You can use nil as the argument to setit(). This prevents the player from using "it" in subsequent commands. Alternative Call: setit( obj, num ) You can also specify which pronoun you want to set. The optional parameter num specifies the pronoun: 1 for "him" and 2 for "her." When num is not specified, setit() sets "it" as usual. Note that nil can be used for the obj argument to clear "him" or "her." Alternative Call: setit( list ) Finally, you can set "them" directly, simply by passing a list of objects (rather than a single object) to setit(). Calling setit() with a list argument has the side effect of clearing "it." ------------------------------------------------------------ *>setscore Call: setscore(score, turns) Alternative Call: setscore(stringval) This function is used to inform the system of the current score and turn count. On versions of TADS that have a status line, this function updates the values on the status line. On versions of TADS without a status line, this call is ignored. Your game should call this function whenever the score or turncounter change, so that the status line is always kept up to date. Note that this means that it should be called after using restore() to load an old game, and before calling restart() to start over from the beginning, in addition to being called whenever the player earns points or completes a turn. Generally, if you use the function incscore() defined in adv.t to add points to the score, and you use a standard turn counter function (such as the turncount() function defined in adv.t), you will not have to worry about calling setscore() too often. The alternative form of the setscore() call allows you to display any text string in the score/turn counter area of the status line. In older versions of TADS, only the first form could be used, and the status line display was fixed with the score/turn counter format. In new versions, however, you can place whatever string you want on the right portion of the status line, simply by passing the string you wish to display to the setscore() function. You could, for example, display the time of day (within your game), or you could display the player's rank, experience, or other statistics rather than the score. The text string used in setscore() is displayed right-justified on the status line. Note that several routines in adv.t (about four in all) call the numeric form of setscore(). If you use the string version, you should change the setscore() calls in adv.t to call a routine that you provide that displays the status line in the format you prefer. ------------------------------------------------------------ *>setversion Call: setversion(version) Note: This function is no longer needed, although TADS continues to support it for compatibility with existing games written with very old versions of TADS. In older version of TADS, this function was used to identify the version of your game that created a saved game file. It was necessary to use setversion so that a saved position file created by one version of your game couldn't accidentally be used with another version of your game, because the information in a saved position file is only usable by the same version of a game that created it. The compiler now automatically generates a unique version identifier whenever your game is compiled, eliminating the need for you to do this manually. You no longer need to call this function, and it has no effect if you do. ------------------------------------------------------------ *>skipturn Call: skipturn(num) This function is similar to incturn(num). This function skips the given number of turns, which must be at least 1. The difference between skipturn() and incturn() is that skipturn() doesn't run any of the fuses that burn down during the turns being skipped - instead, it simply removes them without running them. Fuses set with setfuse() and notify() are both affected by this routine. Note that this function has no effect on daemons. ------------------------------------------------------------ *>substr Call: substr(string, offset, length) This function returns the substring of string starting at the character position given by offset (the first character's position is 1) and containing up to length characters. For example, substr('abcdefghi', 4, 3) returns the string 'def'. If the given offset is past the end of string, a null string is returned; hence, substr('abcdef', 10, 3) returns a null string. (Note that a null string, specified by two single quotes, ", is not the same as nil.) If the string is too short to satisfy the requested length, the substring from the given offset to the end of the string is returned; for example, substr( 'abcdefg', 4, 10 ) returns the string 'defg'. ------------------------------------------------------------ *>systemInfo Call: systemInfo( __SYSINFO_xxx ) Added: TADS 2.2.4. __SYSINFO_HTML_MODE and __SYSINFO_MPEG_AUDIO were added with 2.3.0. This built-in function allows your game to determine programmatically whether the TADS run-time that is currently executing the game has certain capabilities. This function is called like this: result = systemInfo( __SYSINFO_xxx ); where __SYSINFO_xxx is one of a number of pre-defined constants, listed below, defined automatically by the compiler. The result tells you about the particular run-time that is executing your game, so you can customize the game, if you wish, for certain system capabilities. For example, you might want to change some text in your game slightly depending on whether sound effects can be played. It's also useful if you need to know whether a given function (for example, regular expression parsing) is available. Before calling systemInfo() with any of the constant codes, you must check to see if systemInfo() itself is supported. Versions of the run-time prior to 2.2.4 do not support this function, so the return codes are meaningless. Fortunately, you can determine if systemInfo() is itself supported using the following code fragment: if (systemInfo(__SYSINFO_SYSINFO) = true) { /* * systemInfo IS supported by this run-time - other * systemInfo codes will return meaningful results */ } else { /* * systemInfo is NOT supported by this run-time */ } Only one version of HTML TADS (version 2.2.3) was ever released without systemInfo support, and this was the first public beta release. So, it should be fairly safe to assume that any system that doesn't support systemInfo() doesn't support any of the multimedia features. The predefined __SYSINFO_xxx codes are: +------------------------+---------------------------------+ | __SYSINFO_VERSION | Returns a string with the | | | run-time version number. This | | | will be a string such as | | | `2.2.4'. | +------------------------+---------------------------------+ | __SYSINFO_HTML | Returns 1 if HTML markup is | | | supported, 0 if not. 0 | | | indicates that this is a | | | standard text-mode run-time | | | system. If this returns 0, | | | then JPEG, PNG, WAV, MIDI, | | | WAV/MIDI overlap, WAV overlap, | | | and the images, sounds, music, | | | and links preference items can | | | all be assumed to be | | | unsupported, since these | | | features are only provided by | | | HTML TADS. | | | | | | Note that __SYSINFO_HTML | | | returns 0 (HTML not supported) | | | for the character-mode | | | run-time, even for the newer | | | versions of the run-time that | | | do provide some limited HTML | | | support, because this | | | information code is intended | | | to indicate whether the full | | | HTML feature set is supported. | | | The character-mode version | | | only supports a limited subset | | | of HTML features, so it | | | indicates that HTML is not | | | supported. | +------------------------+---------------------------------+ | __SYSINFO_HTML_MODE | Returns true if the | | | interpreter is currently | | | interpreting HTML markups, nil | | | if not. Note that this new | | | code has nothing to do with | | | whether the interpreter is a | | | full multimedia system (such | | | as HTML TADS) or a text-only | | | system (such as the DOS "TR" | | | interpreters); this code | | | instead indicates only whether | | | or not a "\H+" sequence is | | | currently in effect. | +------------------------+---------------------------------+ | __SYSINFO_OS_NAME | Returns a string with the name | | | of the operating system on | | | which the run-time is | | | currently executing. (The name | | | is the same as the string that | | | the compiler uses to | | | pre-define the | | | __TADS_SYSTEM_NAME | | | preprocessor symbol, but this | | | lets you determine what system | | | is executing at run-time, | | | rather than the system that | | | was used to compile the game.) | +------------------------+---------------------------------+ | __SYSINFO_JPEG | Returns 1 if JPEG images are | | | supported, 0 if not. | +------------------------+---------------------------------+ | __SYSINFO_PNG | Returns 1 if PNG images are | | | supported, 0 if not. | +------------------------+---------------------------------+ | __SYSINFO_PNG_TRANS | Tests for PNG transparency | | | support. Some newer versions | | | of the HTML interpreter | | | support PNG transparency, | | | which allows parts of a PNG | | | image to be designated as | | | transparent so that the | | | background shows through. | | | systemInfo(__SYSINFO_PNG_TRANS) | | | returns true if transparency | | | is supported when displaying | | | PNG images, nil if not. This | | | will never return true under | | | interpreters that don't | | | support PNG images at all. | | | Note that this flag indicates | | | only simple transparency | | | support, which allows pixels | | | to be designated as fully | | | opaque or fully transparent, | | | and does not imply support of | | | full alpha blending; some | | | interpreters (such as the | | | Windows HTML interpreter | | | 2.5.4) support only simple | | | transparency but not alpha | | | blending. This code was added | | | with TADS 2.5.4 and is not | | | supported with earlier | | | versions. | +------------------------+---------------------------------+ | __SYSINFO_PNG_ALPHA | Tests for PNG "alpha blending" | | | support, which allows | | | individual pixels of a PNG | | | image to be designated as | | | partially transparent. | | | Currently, none of the | | | interpreters support alpha | | | blending in PNG images; this | | | flag has been added for | | | possible future use. This code | | | was added with TADS 2.5.4 and | | | is not supported with earlier | | | versions. | +------------------------+---------------------------------+ | __SYSINFO_WAV | Returns 1 if WAV sounds are | | | supported, 0 if not. | +------------------------+---------------------------------+ | __SYSINFO_MIDI | Returns 1 if MIDI music is | | | supported, 0 if not. | +------------------------+---------------------------------+ | __SYSINFO_MIDI_WAV_OVL | Returns 1 if MIDI and WAV | | | sounds can be played back | | | simultaneously (overlapped), 0 | | | if not. If this returns 0, it | | | means that WAV playback will | | | suspend MIDI playback. | +------------------------+---------------------------------+ | __SYSINFO_WAV_OVL | Returns 1 if multiple WAV | | | sounds can be played back | | | simultaneously (overlapped), 0 | | | if not. If this returns 0, it | | | means that any WAV played back | | | in a foreground layer will | | | suspend a WAV being played in | | | any background layer. | +------------------------+---------------------------------+ | __SYSINFO_MPEG_AUDIO | Returns 1 if MPEG 2.0 audio | | | support of any kind is | | | present, 0 if not. It is | | | possible that some systems may | | | support some types of MPEG | | | audio, but not all three | | | layers. This feature code | | | indicates whether MPEG audio | | | of any kind is supported; the | | | specific layer codes below can | | | be used to check for each | | | individual layer. | +------------------------+---------------------------------+ | __SYSINFO_MPEG_AUDIO_1 | Returns 1 if MPEG 2.0 layer I | | | is supported | +------------------------+---------------------------------+ | __SYSINFO_MPEG_AUDIO_2 | Returns 1 if MPEG 2.0 layer II | | | is supported | +------------------------+---------------------------------+ | __SYSINFO_MPEG_AUDIO_3 | Returns 1 if MPEG 2.0 layer | | | III is supported. This media | | | type is commonly known as | | | `MP3'. | +------------------------+---------------------------------+ | __SYSINFO_PREF_IMAGES | Returns 1 if the user | | | preferences are set to allow | | | images to be displayed, 0 if | | | not. Note that, even if this | | | preference is set so that | | | images are not displayed, the | | | preferences for JPEG and PNG | | | images will still return 1 for | | | each of those image types that | | | are supported by the platform. | | | The image format codes | | | (__SYSINFO_PNG and | | | __SYSINFO_JPEG) indicate | | | whether the image formats are | | | supported at all, whereas this | | | preference code indicates | | | whether images are currently | | | allowed for display. | +------------------------+---------------------------------+ | __SYSINFO_PREF_SOUNDS | Returns 1 if the user | | | preferences are set to allow | | | digitized sound effects (WAV | | | files) to play back, 0 if not. | +------------------------+---------------------------------+ | __SYSINFO_PREF_MUSIC | Returns 1 if the user | | | preferences are set to allow | | | music (MIDI files) to play | | | back, 0 if not. | +------------------------+---------------------------------+ | __SYSINFO_PREF_LINKS | Returns 0 if the user | | | preferences are set so that | | | links are not highlighted at | | | all (in which case they'll be | | | displayed as ordinary text; | | | they won't be highlighted as | | | links and won't be active for | | | the mouse); returns 1 if links | | | are highlighted and active; | | | and returns 2 if links are set | | | to a "hot key" mode, in which | | | case links aren't highlighted | | | except when the user is | | | holding down a special key to | | | explicitly illuminate the | | | links. | +------------------------+---------------------------------+ Version 2.5.2 also introduced the ability to add live Internet links. The systemInfo() function accepts several capability codes that let you discover whether the system is capable of following URL-style hypertext links in HTML-formatted text. The codes are: +------------------------+----------------------------+ | __SYSINFO_LINKS_HTTP | Checks if the system can | | | follow HTTP (Hypertext | | | transfer protocol) links | | | to display Web pages. | | | HTTP URLs start with | | | "http:". | +------------------------+----------------------------+ | __SYSINFO_LINKS_FTP | Checks if the system can | | | follow FTP (file transfer | | | protocol) links, which | | | start with "ftp:". | +------------------------+----------------------------+ | __SYSINFO_LINKS_NEWS | Checks if the system can | | | follow links to Usenet | | | newsgroups. News URLs | | | start with "news:". | +------------------------+----------------------------+ | __SYSINFO_LINKS_MAILTO | Checks if the system can | | | follow links to send | | | email, which start with | | | "mailto:". | +------------------------+----------------------------+ | __SYSINFO_LINKS_TELNET | Checks if the system can | | | follow telnet links, | | | which start with | | | "telnet:". | +------------------------+----------------------------+ For example, suppose you want to display a link to a web page you've created for your game, so that players can check for updates and hints on your web site. You can use __SYSINFO_LINKS_HTTP to check the user's TADS interpreter for HTTP link capability. If the interpreter can follow HTTP links, you can show your link with a friendly display, and fall back on spelling out the URL when the interpreter can't directly follow the link. "You can check for updates to this game at "; if (systemInfo(__SYSINFO_LINKS_HTTP)) "my web site"; else "my web site (http://www.mysite.com/mygame.htm)" ". This site also has hints and some background information about the game. "; ------------------------------------------------------------ *>timeDelay Call: timeDelay(time) Added: TADS 2.3.0 This built-in function lets you pause the game for a specified time interval. You can use this function in the middle of displaying a long text passage, for example, to create a dramatic pause effect, without making the user press a key to continue. The function takes a single argument giving the number of milliseconds to pause the game. For example, to pause the game for five seconds, use this: timeDelay( 5000 ); ------------------------------------------------------------ *>undo Call: undo() This function undoes all changes made since the last "savepoint" in the undo records. A savepoint is entered at the beginning of each turn automatically by the player command parser. Calling undo() once restores the game to its state at the very beginning of the current turn. Calling undo() again returns it to the state at the beginning of the previous turn, and so forth. You should call undo() once in any situation where you only want to undo the current turn. For example, in a routine that gives the player options after being killed by an action taken on the current turn, you'd only want to call undo() once if the player elects to undo the action that killed him, since it's just the current turn that needs to be taken back. You should call undo() twice in your verb that implements an "undo" command, if you have one in your game. The first call undoes the current turn (the "undo" command itself), and the second call undoes the previous turn - this is appropriate behavior, since the "undo" command should back up one turn. The default undoVerb defined in adv.t implements exactly this behavior. The undo() function returns true if the undo operation was successful, or nil if no more undo information is available. A return value of nil means either that the player has already undone turns all the way back to the beginning of the game, or that he has undone turns as far back as the undo records go. The default undo area size will hold about a hundred turns in a typical game, but the actual number of turns that can be undone will vary by game, depending on the amount of work that needs to be undone. The undo mechanism will always keep as many of the most recent turns as possible, discarding the oldest turns in the undo log when it runs out of space. ------------------------------------------------------------ *>unnotify Call: unnotify(object, &message) This function cancels the effect of a previous call to notify() with the same object and message. Note that the & before message is required. See also the notify() function. ------------------------------------------------------------ *>upper Call: upper(string) This function returns a copy of string with all of the lower-case letters converted to upper-case. Do not confuse this function with the caps() built-in function, which affects only the next character displayed. ------------------------------------------------------------ *>verbinfo Call: verbinfo(deepverb) Alternative Call: verbinfo(deepverb, prep) This function lets you retrieve information on the doAction and ioAction definitions for the given deepverb object. This function returns a list giving the verification and actor property pointers for the appropriate verb template. Refer to the section on getting verb information for details on this function. ------------------------------------------------------------ *>yorn Call: yorn() Waits for the user to type a character, and returns 1 if the player typed "Y", 0 if the player typed "N", and -1 for anything else. A suitable prompt should be issued prior to calling this function. ------------------------------------------------------------ Dynamic Object Creation *>new *>delete TADS allows you to create and delete objects dynamically at run-time. This is done through two operators: new and delete. To create a new object, use this syntax: x := new bookItem; This dynamically creates a new object whose superclass is bookItem. When this statement is executed, the runtime creates a new object, assigns its superclass to be bookItem, and executes the construct method in the new object; this method can perform any creation-time setup that you wish to do. The default construct method in the class thing in adv.t simply moves the new object into its location - this is necessary so that the contents list of the location is updated to include the new object. An expression involving the operator new applied to a class can be used wherever an object expression can be used. When the expression is evaluated, a new object of the given class is created. Note that you can create a new object that has no superclass by using the keyword object: x := new object; If you're familiar with C++ constructors, you should note an important difference between the construct method and C++ constructors. A C++ constructor in a derived class will automatically call the construct in the base class (except for a virtual base class). The TADS construct method does not automatically call superclass construct methods; instead, construct works exactly the same as any other TADS method. You can, of course, call the superclass construct method explicitly using the inherited mechanism, just as with any other method. The same is true of the destruct method (described below). A new object inherits all of the vocabulary of its superclass. To destroy an object create with new, use this syntax: delete x; This first calls the destruct method of the object to notify it that it is about to be deleted, then destroys the object. Further references to the object are illegal, since its memory has been released (and thus may be given to an other object). The default destruct method in the class thing in adv.t moves the object into nil, which removes it from its container's contents list - this is necessary so that the reference to the object in that list is removed. Only objects created with new can be destroyed with delete. Objects that are defined statically in your game's source file cannot be deleted at run-time. Object creation and deletion works correctly with the UNDO mechanism. If the player uses UNDO after a move that created an object, the object will be destroyed; likewise, if a player uses UNDO after a turn that deletes an object, the object will be re-created with the same property values it had prior to deletion. Similarly, dynamically-created objects are preserved across SAVE and RESTORE operations. Note that TADS does not perform any garbage collection on dynamically-created objects. The system is not capable of determining whether an object is accessible or not. Hence, if you lose track of any objects you create with new, they will remain part of the game forever - they will even be saved along with saved games and restored when the games are restored. You must be careful to keep track of all objects you create to avoid filling all available memory (and the swap file) with unreachable objects. Even though TADS will eventually swap inaccessible objects out of memory, the objects will consume object identifiers, which are a limited resource: only 65535 objects can be created within a game, including both static and dynamically-created objects. ------------------------------------------------------------ Indistinguishable Objects *>isEquivalent *>listcont *>itemcnt *>isIndistinguishable *>sayPrefixCount The ability to create new objects at run-time leads to some interesting problems involving indistinguishable objects. Although you could use addword() to make your newly-created objects distinguishable from one another, this will not always be desirable. For example, if you create new gold pieces that serve as currency, you will probably not want them to be uniquely named. To support indistinguishable objects, especially those created dynamically at run-time, the system has a property that you can set to indicate to the parser that an object does not need to be distinguished from others of the same class. The property is isEquivalent. When isEquivalent returns true for an object, all other objects with the same immediate superclass are considered interchangeable by the parser. When a player uses one of these objects in a command, the parser will simply pick one arbitrarily and use it, without asking the player which one. If a player uses a noun that is ambiguous with multiple equivalent items and one or more other items, the parser will need to disambiguate the objects as usual. In such cases, the parser's question will list the distinguishable items only once. For example, assume we have five gold coins that are all equivalent (in other words, they all have isEquivalent set to true, and they all are immediate subclasses of the same class). Assume further that a silver coin and a bronze coin are also present in the room. Treasure Room You see a bronze coin, five gold coins, and a silver coin here. >get coin Which coin do you mean, the bronze coin, a gold coin, or the silver coin? Note that the objects which appear only once are listed with "the" (using the property thedesc), while the indistinguishable objects are listed only once, with "a" (using adesc). The functions listcont and itemcnt in adv.t list equivalent objects intelligently. The functions isIndistinguishable and sayPrefixCount are used to count and list indistinguishable objects correctly. The contents of the Treasure Room are listed in the example above in the new format. Refer to these functions in adv.t for examples of recognizing objects as equivalent, counting equivalent objects, and treating a set of equivalent objects together as a set. Note that listcont uses the property pluraldesc to display the name of an object when more than one equivalent object is present. In the example above, pluraldesc was used to list the gold coins. This property is in the class thing in adv.t, but you may need to override it for some objects - the default implementation simply displays the sdesc plus the letter "s." ------------------------------------------------------------ Preprocessor Directives TADS lets you define preprocessor symbols. The #define directive creates a preprocessor symbol definition: #define TEST "This is a test! " This defines the symbol TEST, and specifies that this symbol is to be replaced by the text "This is a test!" whenever it appears (outside of strings or comments) in your source code. Note that the definition of a symbol need not be a string - the entire text of the rest of the line is assigned to the symbol. You can use a preprocessor symbol that you've defined simply by putting the symbol in your code: sdesc = { TEST; } When the compiler encounters the preprocessor symbol, it replaces the symbol with its definition, so the compiler treats this the same as: sdesc = { "This is a test! "; } You can delete a preprocessor symbol that you've defined using the #undef directive: #undef TEST The compiler automatically defines a number of preprocessor symbols: +----------------------+------------------------------+ | __TADS_VERSION_MAJOR | Defined as the major | | | version number of the | | | current compiler. (Note | | | that this symbol starts | | | with two underscore | | | characters, as do most of | | | the symbols that the | | | compiler defines for you.) | | | Currently, this is defined | | | as 2. | +----------------------+------------------------------+ | __TADS_VERSION_MINOR | The minor version number; | | | currently 5. | +----------------------+------------------------------+ | __TADS_SYSTEM_NAME | A single-quoted string | | | giving the name of the | | | current system. For DOS | | | systems, this is 'MSDOS'; | | | for Macintosh systems, this | | | is 'Macintosh'. TADS also | | | defines the value of this | | | symbol as a symbol itself - | | | on MSDOS systems, the | | | compiler defines the symbol | | | MSDOS, and on Macintosh | | | systems, the compiler | | | defines Macintosh. (The | | | value of the system name | | | symbol is simply 1; its | | | purpose is to allow for | | | conditional compilation, so | | | the value isn't important.) | +----------------------+------------------------------+ | __DEBUG | Defined if the game is | | | being compiled for | | | debugging (with the -ds | | | command line option). By | | | testing this symbol with | | | #ifdef, you can easily | | | include parts of your game | | | (such as special "magic" | | | verbs that you use for | | | debugging) only in testing | | | versions of your game, | | | without having to worry | | | about removing them | | | manually when producing the | | | final version of the game | | | for players. The value of | | | this symbol is 1 if it's | | | defined; the purpose of | | | this symbol is to allow for | | | conditional compilation, so | | | its value isn't important. | +----------------------+------------------------------+ | __DATE__ | Defined as a single-quoted | | | string giving the date when | | | the current compilation | | | began, in the format 'Jan | | | 01 1994'. | +----------------------+------------------------------+ | __TIME__ | Defined as a single-quoted | | | string giving the time when | | | the current compilation | | | began, in the 24-hour | | | format '01:23:45'. The time | | | isn't updated during the | | | course of a compilation - | | | it's always the time when | | | compilation began. | +----------------------+------------------------------+ | __FILE__ | Defined as a single-quoted | | | string giving the name of | | | the current source file | | | being compiled. | +----------------------+------------------------------+ | __LINE__ | Defined as a number giving | | | the line number currently | | | being compiled. | +----------------------+------------------------------+ You can define symbols from the compiler command line. The -D option lets you specify a symbol to define, and optionally a value. Specify the symbol, then an equals sign, then the value; if you omit the equals sign, the default value is 1. For example, to define the symbol DEMO from the command line, you could use this command: tc -DDEMO mygame You can also specifically delete preprocessor symbols that the compiler defines (other than __FILE__ or __LINE__). You can also undefine any symbols defined earlier on the command line with -D options, which may be useful if you're using a configuration file that defines certain symbol. To undefine a symbol, use the -U option: tc -UDEMO mygame If the symbol DEMO was defined earlier on the command line, the definition is deleted. The Macintosh compiler has a new preprocessor options dialogue box that you can access from the "Options" menu. You can use this dialogue to enter symbols to define; in the text box, simply list the symbols (one per line) that you wish to define. If you want to assign values to these symbols, use an equals sign, followed by the value. Another text box in the same dialogue lets you list pre-defined symbols that you want to delete; simply list the symbols to be deleted, one per line. You can test for the existence of a preprocessor symbol with #ifdef ("if defined") and you can test to see if a preprocessor symbol is undefined with the #ifndef ("if not defined") directive. These directives let you conditionally include code in your program, depending on whether or not a symbol is defined. For example: #ifdef TEST sdesc = { TEST; } #endif The code between the #ifdef and the #endif is included only if the preprocessor symbol TEST is defined. There's also a #else directive, which lets you include a block of lines if the most recent condition failed. Conditional compilation is particularly useful with the symbols you define from the command line with the -D option, since it allows you to write your game in such a way that certain features are enabled when you use a particular set of -D options. This allows you to use a single set of source files, but produce a variety of different .GAM files. For example, if you want to be able to generate a subset of your game as a demo, you could use conditional compilation to include or discard sections of the game depending on whether you're compiling the demo or the full game. Similarly, you could use conditional compilation to include certain features only when you're compiling a debugging version of your game; note that the compiler makes this even easier, because it defines the symbol __DEBUG when you're compiling with debugging symbols. I've also added the #error directive. This directive lets you generate an error from within your game. Any text following the #error on the line is displayed to the user as the text of the error message. For example: #ifndef TEST #error TEST is not defined! #endif ------------------------------------------------------------ File Operations TADS has support for reading and writing files from within your game program. This feature is intended to let you save information independently of the game-saving mechanism, which only really allows you to transfer information between sessions of a game. The file functions can save files in a proprietary binary format, which can be used to store TADS data, or as plain text files, which can be opened and read by any text editor, word processor or Web browser application. Operations on files are all performed through a file handle. This is a special value, generated by TADS, that you use to refer to a file. TADS creates a file handle when you open a file; once you've opened a file and obtained its file handle, you can read and write the file. Once you're done with the file, you close the file, which deletes the file handle. To open a file, use the fopen() function. This function takes two arguments: a (single-quoted) string giving the name of the file to open, using local file system conventions, and a "mode." The mode argument is one or two of these single-quoted string values: `b' - open as a binary file. If you don't specify the file mode, binary is assumed as the default mode, for compatibility with past versions. `r' - open file for reading; file must already exist. `r+' - open file for reading and writing; the file is created if it doesn't already exist. Not currently allowed in text mode. `t' - open as a text file. You can use the text mode in conjunction with `r' (read) and `w' (write) modes - `t' is not currently allowed with `r+' or `w+' modes. Note that text file functionality was added with version 2.2.4 of TADS. `w' - create a new file for writing; the file is deleted if it already exists. `w+' - create a new file for reading and writing; the file is deleted if it already exists. Not currently allowed in text mode. For maximum portability you should avoid using volume names, directories, folders, or other path information in filenames, because any such information might be specific to your computer. The return value of fopen() is a file handle - you must save the file handle so you can use it later to perform operations on the file. If the operation fails, and the file cannot be opened as requested, fopen() returns nil. Opening a file could fail for a number of reasons. For example, if you attempt to open a non-existent file with mode `r', fopen() will return nil, because this mode requires that the file already exists. This example opens a new file called TEST.OUT for writing: fnum := fopen( 'test.out', 'w' ); To close an open file, use fclose(): fclose( fnum ); Once you've closed a file, the file handle is no longer valid. TADS may re-use the same file handle on a subsequent fopen() call. Note that the TADS runtime allows only a limited number of files (currently 10) to be open simultaneously, so you should close a file when you're done with it. To write to a file, use fwrite(). This function takes a file handle, and a value to write; the value can be a string, a number, or true. The value can't be nil (this is because the fread() function returns nil to indicate failure; if you could write nil to a file, there would be no way to distinguish reading a valid nil from an error condition). fwrite() stores the value, along with information on its type. The fwrite() function returns nil on success, true on failure. If the function returns true, it usually means that the disk is full. if ( fwrite( fnum, 'string value!' ) or fwrite( fnum, 123 ) ) "Error writing file!"; If the file is open for reading, you can read from the file with the fread() function. This function takes a file handle, and it returns a value it reads from the file. The value returned is of the same type as the value originally written at this position in the file with fwrite(). If this function returns nil, it indicates that an error occurred; this usually means that no more information is in the file (which is the case once you've read to the end of the file). res := fread( fnum ); say( res ); You can get the current byte position in the file with the ftell() function: "The current seek position is "; ftell( fnum ); ". "; The ftell() function returns a number giving the byte position that will be read or written by the next file operation. You can set the file position with fseek() and fseekeof(). The fseek() function moves the file position to a particular byte position, relative to the beginning of the file. For example, this seeks to the very beginning of a file: fseek( fnum, 0 ); The fseekeof() function positions the file at its end (EOF stands for "end of file"): fseekeof( fnum ); Note that you must be careful with fseek(). You should only seek to positions that you obtained with the ftell() function; other positions may be in the middle of a string or a number in the file, so seeking to an arbitrary location and writing could render the file unusable by partially overwriting existing data. Of the two formats, TADS binary files were not designed with any provisions for creating or reading formatted files, or for exchanging information with programs other than TADS games. TADS' binary format is portable across platforms - you can take a binary file written using DOS TADS and read it on the Macintosh with Mac TADS; however, the TADS format isn't designed to be interchanged with other programs. If you want portable files, save your files in the text format. These files are plain ASCII and can be used by any other application on your computer. Some game players may be uncomfortable about the possibility that a malicious game author could harm their systems by writing a game that, for example, modifies their AUTOEXEC.BAT files. To address any concerns that players may have, TADS 2.2.4 and higher provide a "file safety level" setting. This is an optional setting that allows the player to control the amount of access that a game has to files on the system at run-time. The file safety level is set through a command line option (note that HTML TADS and HyperTADS provide this setting through the "Preferences" dialogue). Use the new -s option to specify one of the possible safety levels: -s0 - (default) minimum safety - read and write in any directory -s1 - read in any directory, write in current directory -s2 - read-only access in any directory -s3 - read-only access in current directory only -s4 - maximum safety - no file I/O allowed If the game attempts a file operation that is not allowed by the current safety level, the fopen() function returns nil to indicate that the file open failed. These options affect only explicit file I/O operations performed by the game. Operations handled by the system, such as saving and restoring games and logging a transcript to a file, are not affected by the file safety level setting. Our life is frittered away by detail Simplify, simplify. HENRY DAVID THOREAU, Where I Lived, and What I Lived For (1854) The statements was interesting but tough. MARK TWAIN, Adventures of Huckleberry Finn (1884) ------------------------------------------------------------ Adventure Definitions ------------------------------------------------------------ This file is part of the TADS Author's Manual. Copyright (c) 1987 - 2001 by Michael J. Roberts. All rights reserved. Edited by N. K. Guy, tela design. ------------------------------------------------------------ Appendix A ------------------------------------------------------------ Adventure Definitions The TADS run-time system provides a great deal of support for text adventure games, but it doesn't pre-define very much specific to adventures. To make the game author's job easier, the file adv.t provides definitions for a great many basic objects and classes that underlie most adventure games. These classes are very general, and most games will customize them further, but they vastly reduce the amount of work required to write a game. This section describes these classes, what they do, and how to use them to write a game. Note that in addition to everything listed in the following pages, adv.t also defines the articles "a," "an," and "the." These definitions become part of your game when you include the standard adventure definitions file adv.t in your source file. Note, however, that this appendix provides only a partial description of each object class in adv.t. For a more complete understanding of how they work check out the adv.t file itself. It's fully commented, and reading the source code is always a good way to learn the details. ------------------------------------------------------------ Objects, Classes, and Functions This section lists the objects and functions defined in the standard adventure definitions file adv.t. The descriptions of the objects indicate the object's superclass or superclasses. For complete information on an object, you need to refer to the description of its superclasses as well. The descriptions also indicate the main properties defined in the object; in customizing the object, you will generally need to override one or more of these properties. For example, most descendants of thing will specify an sdesc to provide the object with a name. The function descriptions provide information on how to call the functions, what they do, and what they return. For clarity, the objects are described in inheritance order; that is, the lower-level (parent) classes are described first, then the descendants of those classes, then the descendants of those, and so on. (This is the same order in which they appear in adv.t.) -------------------------------------------------- *>itemcnt itemcnt: function(list ): Returns a count of the "listable" objects in list. An object is listable (that is, it shows up in a room's description) if its isListed property is true. This function is useful for determining how many objects (if any) will be listed in a room's description. -------------------------------------------------- *>listcont listcont: function(obj ): This function displays the contents of an object, separated by commas. The thedesc properties of the contents are used. It is up to the caller to provide the introduction to the list (usually something to the effect of "The box contains" is displayed before calling listcont) and finishing the sentence (usually by displaying a period). An object is listed only if its isListed property is true. -------------------------------------------------- *>listfixedcontcont listfixedcontcont: function(obj ): List the contents of the contents of any fixeditem objects in the contents list of the object obj. This routine makes sure that all objects that can be taken are listed somewhere in a room's description. This routine recurses down the contents tree, following each branch until either something has been listed or the branch ends without anything being listable. This routine displays a complete sentence, so no introductory or closing text is needed. -------------------------------------------------- *>listcontcont listcontcont: function(obj ): This function lists the contents of the contents of an object. It displays full sentences, so no introductory or closing text is required. Any item in the contents list of the object obj whose contentsVisible property is true has its contents listed. An Object whose isqcontainer or isqsurface property is true will not have its contents listed. -------------------------------------------------- *>scoreStatus scoreStatus: function(parm ): This function simply calls setscore() with the same arguments. All other calls to setscore() in adv.t have been replaced by calls to scoreStatus(), which makes it easy to provide a new scoring format simply by using the replace keyword to substitute your own implementation of scoreStatus() for the one in adv.t. Also, note scoreFormat below. -------------------------------------------------- *>scoreFormat Added: TADS 2.5.2. The way that adv.t displays the status line was changed slightly in version 2.5.2, in order to simplify coding of special formats. A new function, scoreFormat(), is now responsible for formatting the string to display in the right half of the status line. scoreFormat() takes the current score and the current turn counter as arguments, and returns a string to display in the right half of the status line. The default implementation in adv.t simply returns a string consisting of the score, a slash (`/'), and the turn count, which provides the same format that adv.t has traditionally used for the status line. The older function scoreStatus() calls scoreFormat() to generate the display. In addition, the code in room.statusLine that generates the HTML version of the status line now calls scoreFormat() as well. This makes scoreFormat() the single point in adv.t where the right half of the status line is formatted. The advantage of this new mechanism is that you can customize the display of the right half of the status line for both text-only and HTML-enabled games simply by replacing the scoreFormat() function. Of course, HTML-enabled games are not limited to using the traditional status line display; if you want a completely custom status line display when running under an HTML-enabled interpreter, you can replace adv.t's room.statusLine and generate your own display. The scoreFormat() mechanism, however, is convenient when you simply want to customize the right half of the status line, but still use the traditional single-line status format. -------------------------------------------------- *>turncount turncount: function(parm ): This function can be used as a daemon (normally set up in the init function) to update the turn counter after each turn. This routine increments global.turnsofar, and then calls setscore to update the status line with the new turn count. -------------------------------------------------- *>addweight addweight: function(list ): Adds the weights of the objects in list and returns the sum. The weight of an object is given by its weight property. This routine includes the weights of all of the contents of each object, and the weights of their contents, and so forth. -------------------------------------------------- *>addbulk addbulk: function(list ): This function returns the sum of the bulks (given by the bulk property) of each object in list. The value returned includes only the bulk of each object in the list, and not of the contents of the objects, as it is assumed that an object does not change in size when something is put inside it. You can easily change this assumption for special objects (such as a bag that stretches as things are put inside) by writing an appropriate bulk method for that object. -------------------------------------------------- *>incscore incscore: function(amount ): Adds amount to the total score, and updates the status line to reflect the new score. The total score is kept in global.score. Always use this routine rather than changing global.score directly, since this routine ensures that the status line is updated with the new value. -------------------------------------------------- *>initSearch initSearch: function. Initializes the containers of objects with a searchLoc, underLoc, and behindLoc by setting up searchCont, underCont, and behindCont lists, respectively. You should call this function once in your preinit (or init, if you prefer) function to ensure that the underable, behindable, and searchable objects are set up correctly. -------------------------------------------------- *>initRestart initRestart(param): function. This function is used when adv.t calls the restart() built-in function to start the game over from the beginning. The initRestart() in adv.t simply sets the property global.restarting to true. Your game can inspect this flag in the init() function (or elsewhere) to take a different course of action when restarting a game than when starting up for the first time. The parameter is not used by adv.t implementation of the function, but if you replace the initRestart function defined in adv.t, you might find the parameter useful for passing information through the restart. The "restart" verb passes a pointer to the initRestart function when it calls the restart() built-in function. This causes initRestart() to be invoked after the game has been restarted, but before init(). Note that the call to restart() passes global.initRestartParam as the parameter to the initRestart function. If you replace initRestart() with your own function, and you need to pass some information to this function, simply store the necessary information in global.initRestartParam at any time before restarting, and the information will automatically be passed to initRestart() when it's invoked. -------------------------------------------------- *>nestedroom nestedroom: room. A special kind of room that is inside another room; chairs and some types of vehicles, such as inflatable rafts, fall into this category. Note that a room can be within another room without being a nestedroom, simply by setting its location property to another room. The nestedroom is different from an ordinary room, though, in that it's an "open" room; that is, when inside it, the actor is still really inside the enclosing room for purposes of descriptions. Hence, the player sees "Laboratory, in the chair." In addition, a nestedroom is an object in its own right, visible to the player; for example, a chair is an object in a room in addition to being a room itself. The property statusPrep displays an appropriate preposition for status line displays while the player is in the nested room. The property outOfPrep displays the correct preposition when leaving the nested room. The default values are statusPrep = "in" and outOfPrep = "out of". The class beditem provides default values of "on" and "out of" instead. If you're defining new subclasses of nestedroom, you can override these properties to provide the most appropriate messages for the subclasses. -------------------------------------------------- *>chairitem chairitem: fixeditem, nestedroom, surface. Acts like a chair: actors can sit on the object. While sitting on the object, an actor can't go anywhere until standing up, and can only reach objects that are on the chair and in the chair's reachable list. By default, nothing is in the reachable list. Note that there is no real distinction made between chairs and beds, so you can sit or lie on either; the only difference is the message displayed describing the situation. -------------------------------------------------- *>beditem beditem: chairitem. This object is the same as a chairitem, except that the player is described as lying on, rather than sitting in, the object. -------------------------------------------------- *>thing thing: object. The basic class for objects in a game. The property contents is a list that specifies what is in the object; this property is automatically set up by the system after the game is compiled to contain a list of all objects that have this object as their location property. The contents property is kept consistent with the location properties of referenced objects by the moveInto method; always use moveInto rather than directly setting a location property for this reason. The adesc method displays the name of the object with an indefinite article; the default is to display "a" followed by the sdesc, but objects that need a different indefinite article (such as "an" or "some") should override this method. Likewise, thedesc displays the name with a definite article; by default, thedesc displays "the" followed by the object's sdesc. The sdesc simply displays the object's name ("short description") without any articles. The pluraldesc simply adds an "s" to the end of the sdesc property. The pluraldesc property is used by listcont() when multiple equivalent objects are present in a list. (for details, see the information on the changes to listcont() described in the section on indistinguishable objects.) The ldesc is the long description, normally displayed when the object is examined by the player; by default, the ldesc displays "It looks like an ordinary sdesc." The isIn(object) method returns true if the object's location is the specified object or the object's location is an object whose contentsVisible property is true and that object's isIn(object) method is true. Note that if isIn is true, it doesn't necessarily mean the object is reachable, because isIn is true if the object is merely visible within the location. The thrudesc method displays a message for when the player looks through the object (objects such as windows would use this property). The moveInto(object) method moves the object to be inside the specified object. To make an object disappear, move it into nil. -------------------------------------------------- *>item item: thing. A basic item which can be picked up by the player. It has no weight (0) and minimal bulk (1). The weight property should be set to a non-zero value for heavy objects. The bulk property should be set to a value greater than 1 for bulky objects, and to zero for objects that are very small and take essentially no effort to hold - or, more precisely, don't detract at all from the player's ability to hold other objects (for example, a piece of paper). -------------------------------------------------- *>lightsource lightsource: item. A portable lamp, candle, match, or other source of light. The light source can be turned on and off with the islit property. If islit is true, the object provides light, otherwise it's just an ordinary object. -------------------------------------------------- *>hiddenItem hiddenItem: object. This is an object that is hidden with one of the hider classes. A hiddenItem object doesn't have any special properties in its own right, but all objects hidden with one of the hider classes must be of class hiddenItem so that initSearch can find them. -------------------------------------------------- *>hider hider: item. This is a basic class of object that can hide other objects in various ways. The underHider, behindHider, and searchHider classes are examples of hider subclasses. The class defines the method searchObj(actor, list), which is given the list of hidden items contained in the object (for example, this would be the underCont property, in the case of an underHider), and "finds" the object or objects. Its action is dependent upon a couple of other properties of the hider object. The serialSearch property, if true, indicates that items in the list are to be found one at a time; if nil (the default), the entire list is found on the first try. The autoTake property, if true, indicates that the actor automatically takes the item or items found; if nil, the item or items are moved to the actor's location. The searchObj method returns the list with the found object or objects removed; the caller should assign this returned value back to the appropriate property (for example, underHider will assign the return value to underCont). Note that because the hider is hiding something, this class overrides the normal verDoSearch method to display the message, "You'll have to be more specific about how you want to search that." The reason is that the normal verDoSearch message ("You find nothing of interest") leads players to believe that the object was exhaustively searched, and we want to avoid misleading the player. On the other hand, we don't want a general search to be exhaustive for most hider objects. So, we just display a message letting the player know that the search was not enough, but we don't give away what they have to do instead. The objects hidden with one of the hider classes must be of class hiddenItem. -------------------------------------------------- *>underHider underHider: hider. This is an object that can have other objects placed under it. The objects placed under it can only be found by looking under the object; see the description of hider for more information. You should set the underLoc property of each hidden object to point to the underHider. Note that an underHider doesn't allow the player to put anything under the object during the game. Instead, it's to make it easy for the game writer to set up hidden objects while implementing the game. All you need to do to place an object under another object is declare the top object as an underHider, then declare the hidden object normally, except use underLoc rather than location to specify the location of the hidden object. The behindHider and searchHider objects work similarly. The objects hidden with underHider must be of class hiddenItem. -------------------------------------------------- *>behindHider behindHider: hider. This is just like an underHider, except that objects are hidden behind this object. Objects to be behind this object should have their behindLoc property set to point to this object. The objects hidden with behindHider must be of class hiddenItem. -------------------------------------------------- *>searchHider searchHider: hider. This is just like an underHider, except that objects are hidden within this object in such a way that the object must be looked in or searched. Objects to be hidden in this object should have their searchLoc property set to point to this object. Note that this is different from a normal container, in that the objects hidden within this object will not show up until the object is explicitly looked in or searched. The items hidden with searchHider must be of class hiddenItem. -------------------------------------------------- *>fixeditem fixeditem: thing. An object that cannot be taken or otherwise moved from its location. Note that a fixeditem is sometimes part of a movable object; this can be done to make one object part of another, ensuring that they cannot be separated. By default, the functions that list a room's contents do not automatically describe fixeditem objects (because the isListed property is set to nil). Instead, the game author will generally describe the fixeditem objects separately as part of the room's ldesc. -------------------------------------------------- *>distantItem distantItem: fixeditem. This class is essentially the same as a fixeditem, except that it's intended to be used for objects that are not actually part of a room, but are visible from the room. For example, the player might be able to see a distant mountain from a certain location, even though the player can't do anything to the mountain (except look at it). A distantItem object can be inspected, but any other attempts to manipulate the object will receive the response "It's too far away." -------------------------------------------------- *>readable readable: item. An item that can be read. The readdesc property is displayed when the item is read. By default, the readdesc is the same as the ldesc, but the readdesc can be overridden to give a different message. -------------------------------------------------- *>fooditem fooditem: item. An object that can be eaten. When eaten, the object is removed from the game, and global.lastMealTime is decremented by the foodvalue property. By default, the foodvalue property is global.eatTime, which is the time between meals. So, the default fooditem will last for one "nourishment interval." -------------------------------------------------- *>dialItem dialItem: fixeditem. This class is used for making "dials," which are controls in your game that can be turned to a range of numbers. You must define the property maxsetting as a number specifying the highest number to which the dial can be turned; the lowest number on the dial is always 1. The setting property is the dial's current setting, and can be changed by the player by typing the command "turn dial to number." By default, the ldesc method displays the current setting. -------------------------------------------------- *>switchItem switchItem: fixeditem. This is a class for things that can be turned on and off by the player. The only special property is isActive, which is nil if the switch is turned off and true when turned on. The object accepts the commands "turn it on" and "turn it off," as well as synonymous constructions, and updates isActive accordingly. -------------------------------------------------- *>room room: thing. A location in the game. By default, the islit property is true, which means that the room is lit (no light source is needed while in the room). You should create a darkroom object rather than a room with islit set to nil if you want a dark room, because other methods are affected as well. The isseen property records whether the player has entered the room before; initially it's nil, and is set to true the first time the player enters. The roomAction(actor, verb, directObject, preposition, indirectObject) method is activated for each player command; by default, all it does is call the room's location's roomAction method if the room is inside another room. The lookAround(verbosity) method displays the room's description for a given verbosity level; true means a full description, nil means only the short description (just the room name plus a list of the objects present). roomDrop(object) is called when an object is dropped within the room; normally, it just moves the object to the room and displays "Dropped." The firstseen method is called when isseen is about to be set true for the first time (i.e., when the player first sees the room); by default, this routine does nothing, but it's a convenient place to put any special code you want to execute when a room is first entered. The firstseen method is called after the room's description is displayed. -------------------------------------------------- *>darkroom darkroom: room. A dark room. The player must have some object that can act as a light source in order to move about and perform most operations while in this room. Note that the room's lights can be turned on by setting the room's lightsOn property to true; do this instead of setting islit, because islit is a method which checks for the presence of a light source. -------------------------------------------------- *>Actor Actor: fixeditem, movableActor. A character in the game. The maxweight property specifies the maximum weight that the character can carry, and the maxbulk property specifies the maximum bulk the character can carry. The actorAction(verb, directObject, preposition, indirectObject) method specifies what happens when the actor is given a command by the player; by default, the actor ignores the command and displays a message to this effect. The isCarrying(object) method returns true if the object is being carried by the actor. The actorDesc method displays a message when the actor is in the current room; this message is displayed along with a room's description when the room is entered or examined. The verGrab(object) method is called when someone tries to take an object the actor is carrying; by default, an actor won't let other characters take its possessions. If you want the player to be able to follow the actor when it leaves the room, you should define a follower object to shadow the character, and set the actor's myfollower property to the follower object. The follower is then automatically moved around just behind the actor by the actor's moveInto method. The isHim property should return true if the actor can be referred to by the player as "him," and likewise isHer should be set to true if the actor can be referred to as "her." Note that both or neither can be set; if neither is set, the actor can only be referred to as "it," and if both are set, any of "him," "her," or "it" will be accepted. -------------------------------------------------- *>movableActor movableActor: qcontainer. Just like an Actor object, except that the player can manipulate the actor like an ordinary item. Useful for certain types of actors, such as small animals. -------------------------------------------------- *>follower follower: Actor. This is a special object that can "shadow" the movements of a character as it moves from room to room. The purpose of a follower is to allow the player to follow an actor as it leaves a room by typing a "follow" command. Each actor that is to be followed must have its own follower object. The follower object should define all of the same vocabulary words (nouns and adjectives) as the actual actor to which it refers. The follower must also define the myactor property to be the Actor object that the follower follows. The follower will always stay one room behind the character it follows; no commands are effective with a follower except for "follow." The follower class's actorAction method executes an exit statement. In addition, the follower class uses dobjGen and iobjGen to provide sensible responses to most verbs. -------------------------------------------------- *>basicMe basicMe: Actor. A default implementation of the Me object, which is the player character. adv.t defines basicMe instead of Me to allow your game to override parts of the default implementation while still using the rest, and without changing adv.t itself. To use basicMe unchanged as your player character, include this in your game: "Me: basicMe;". The basicMe object defines all of the methods and properties required for an actor, with appropriate values for the player character. The nouns "me" and "myself" are defined ("I" is not defined, because it conflicts with the "inventory" command's minimal abbreviation of "i" in certain circumstances, and is generally not compatible with the syntax of most player commands anyway). The sdesc is "you"; the thedesc and adesc are "yourself," which is appropriate for most contexts. The maxbulk and maxweight properties are set to 10 each; a more sophisticated Me might include the player's state of health in determining the maxweight and maxbulk properties. -------------------------------------------------- *>decoration decoration: fixeditem. An item that doesn't have any function in the game, apart from having been mentioned in the room description. These items are immovable and can't be manipulated in any way, but can be referred to and inspected. Liberal use of decoration items can improve a game's playability by helping the parser recognize all the words the game uses in its descriptions of rooms. -------------------------------------------------- *>buttonitem buttonitem: fixeditem. A button (the type you push). The individual button's action method doPush(actor), which must be specified in the button, carries out the function of the button. Note that all buttons have the noun "button" defined. -------------------------------------------------- *>clothingItem clothingItem: item. Something that can be worn. By default, the only thing that happens when the item is worn is that its isworn property is set to true. If you want more to happen, override the doWear(actor) property. Note that, when a clothingItem is being worn, certain operations will cause it to be removed (for example, dropping it causes it to be removed). If you want something else to happen, override the checkDrop method; if you want to disallow such actions while the object is worn, use an exit statement in the checkDrop method. -------------------------------------------------- *>obstacle obstacle: object. An obstacle is used in place of a room for a direction property. The destination property specifies the room that is reached if the obstacle is successfully negotiated; when the obstacle is not successfully negotiated, destination should display an appropriate message and return nil. -------------------------------------------------- *>doorway doorway: fixeditem, obstacle. A doorway is an obstacle that impedes progress when it is closed. When the door is open (isopen is true), the user ends up in the room specified in the doordest property upon going through the door. Since a doorway is an obstacle, use the door object for a direction property of the room containing the door. If noAutoOpen is not set to true, the door will automatically be opened when the player tries to walk through the door, unless the door is locked (islocked = true). If the door is locked, it can be unlocked simply by typing "unlock door", unless the mykey property is set, in which case the object specified in mykey must be used to unlock the door. Note that the door can only be relocked by the player under the circumstances that allow unlocking, plus the property islockable must be set to true. By default, the door is closed; set isopen to true if the door is to start out open (and be sure to open the other side as well). otherside specifies the corresponding doorway object in the destination room (doordest), if any. If otherside is specified, its isopen and islocked properties will be kept in sync automatically. -------------------------------------------------- *>lockableDoorway lockableDoorway: doorway. This is just a normal doorway with the islockable and islocked properties set to true. Fill in the other properties (otherside and doordest) as usual. If the door has a key, set property mykey to the key object. -------------------------------------------------- *>vehicle vehicle: item, nestedroom. This is an object that an actor can board. An actor cannot go anywhere while on board a vehicle (except where the vehicle goes); the actor must get out first. -------------------------------------------------- *>surface surface: item. Objects can be placed on a surface. Apart from using the preposition "on" rather than "in" to refer to objects contained by the object, a surface is identical to a container. Note: an object cannot be both a surface and a container, because there is no distinction between the two internally. -------------------------------------------------- *>container container: item. This object can contain other objects. The iscontainer property is set to true. The default ldesc displays a list of the objects inside the container, if any. The maxbulk property specifies the maximum amount of bulk the container can contain. -------------------------------------------------- *>openable openable: container. A container that can be opened and closed. The isopenable property is set to true. The default ldesc displays the contents of the container if the container is open, otherwise a message saying that the object is closed. -------------------------------------------------- *>qcontainer qcontainer: container. A "quiet" container: its contents are not listed when it shows up in a room description or inventory list. The isqcontainer property is set to true. -------------------------------------------------- *>lockable lockable: openable. A container that can be locked and unlocked. The islocked property specifies whether the object can be opened or not. The object can be locked and unlocked without the need for any other object; if you want a key to be involved, use a keyedLockable. -------------------------------------------------- *>keyedLockable keyedLockable: lockable. This subclass of lockable allows you to create an object that can only be locked and unlocked with a corresponding key. Set the property mykey to the keyItem object that can lock and unlock the object. -------------------------------------------------- *>keyItem keyItem: item. This is an object that can be used as a key for a keyedLockable or lockableDoorway object. It otherwise behaves as an ordinary item. -------------------------------------------------- *>seethruItem seethruItem:item. This is a class for objects that the player can look through, and is intended for items such as windows or magnifying glasses. If the player looks through the object (with a command such as "look through the window"), the object calls its thrudesc method to display an appropriate description of what the player sees. You should customize this method to display an appropriate message for each seethruItem you create. The default method displays "You can't see much through the window," which obviously won't always be what you want. Note that seethruItem is not the same as the transparentItem below. The class transparentItem is for objects whose contents are visible, whereas seethruItem is for objects that the player can look through. Use seethruItem for windows, binoculars, magnifying glasses, and other non-containers. Use transparentItem for glass bottles and other transparent containers. -------------------------------------------------- *>transparentItem transparentItem: item. An object whose contents are visible, even when the object is closed. Whether the contents are reachable is decided in the normal fashion. This class is useful for items such as glass bottles, whose contents can be seen when the bottle is closed but cannot be reached. -------------------------------------------------- *>basicNumObj basicNumObj: object. This object provides a default implementation for numObj. To use this default unchanged in your game, include in your game this line: "numObj: basicNumObj". -------------------------------------------------- *>basicStrObj basicStrObj: object. This object provides a default implementation for strObj. To use this default unchanged in your game, include in your game this line: "strObj: basicStrObj". -------------------------------------------------- *>deepverb deepverb: object. A "verb object" that is referenced by the parser when the player uses an associated vocabulary word. A deepverb contains both the vocabulary of the verb and a description of available syntax. The verb property lists the verb vocabulary words; one word (such as 'take') or a pair (such as 'pick up') can be used. In the latter case, the second word must be a preposition, and may move to the end of the sentence in a player's command, as in "pick it up." The action(actor) method specifies what happens when the verb is used without any objects; its absence specifies that the verb cannot be used without an object. The doAction specifies the root of the message names (in single quotes) sent to the direct object when the verb is used with a direct object; its absence means that a single object is not allowed. Likewise, the ioAction(preposition) specifies the root of the message name sent to the direct and indirect objects when the verb is used with both a direct and indirect object; its absence means that this syntax is illegal. Several ioAction properties may be present: one for each preposition that can be used with an indirect object with the verb. The validDo(actor, object, seqno) method returns true if the indicated object is valid as a direct object for this actor. The validIo(actor, object, seqno) method does likewise for indirect objects. The seqno parameter is a "sequence number," starting with 1 for the first object tried for a given verb, 2 for the second, and so forth; this parameter is normally ignored, but can be used for some special purposes. For example, askVerb does not distinguish between objects matching vocabulary words, and therefore accepts only the first from a set of ambiguous objects. These methods do not normally need to be changed; by default, they return true if the object is accessible to the actor. The doDefault(actor, prep, indirectObject) and ioDefault(actor, prep) methods return a list of the default direct and indirect objects, respectively. These lists are used for determining which objects are meant by "all" and which should be used when the player command is missing an object. These normally return a list of all objects that are applicable to the current command. All of the system verbs in adv.t that use the abort statement have been designed in such a fashion that it's quite easy to augment their behavior with the modify statement. All of the processing other than the abort is not in the doVerb (or action) method but is elsewhere. For example, the action method in saveVerb looks like this: action( actor ) = { self.saveGame( actor ); abort; } The saveGame method in saveVerb performs all of the processing related to the actual save function itself. The benefit of this design is that you can modify the saveGame method, and inherit the standard behavior, without having to worry about an abort statement interfering with the order of operations. For example: modify restoreVerb restoreGame( actor ) = { // restore the game, and check for success if ( inherited.restoreGame( actor ) ) { // re-randomize the puzzle "The carnival hawker flashes a mischievous smile at you. \"There's no use trying to guess the answer,\" he says. \"I changed around the shells while you were busy restoring!\""; puzzle.answer := rand( 100 ); } } ; -------------------------------------------------- *>Prep Prep: object. A preposition. The preposition property specifies the vocabulary word. ------------------------------------------------------------ Verbs in the Adventure Definitions File In the list below, the alternative vocabulary words for the verb are listed with slashes (/) between them; the alternate syntaxes are listed with commas between. When the verb takes objects, the root of the message the parser generates in shown in parentheses following the syntax description; these are prefaced with do, verDo, io, and verIo, as appropriate. See the section on the parser in chapter 4 for details. againVerb: again/g askVerb: ask direct-object about indirect-object (AskAbout) attachVerb: attach direct-object to indirect-object (AttachTo) attackVerb: attack/kill/hit direct-object with indirect-object (AttackWith) boardVerb: get in/get into/board direct-object (Board) centerVerb: center direct-object (Center) cleanVerb: clean direct-object (Clean), clean direct-object with indirect-object (CleanWith) climbVerb: climb direct-object (Climb) closeVerb: close direct-object (Close) digVerb: dig/dig in direct-object with indirect-object (DigWith) drinkVerb: drink direct-object (Drink) dropVerb: drop/put down direct-object (Drop), drop/put down direct-object on indirect-object (PutOn) dVerb: down/d eatVerb: eat direct-object (Eat) eVerb: east/e fastenVerb: fasten direct-object (Fasten) getOutVerb: get out/get out of/get off/get off of direct-object (Unboard) giveVerb: give/offer direct-object to indirect-object (GiveTo) helloVerb: hello/hi/greetings inspectVerb: inspect/examine/look at/x direct-object (Inspect) inVerb: in/go in/enter iVerb: inventory/i jumpVerb: jump, jump direct-object (Jump) lieVerb: lie/lie on/lie in/lie down/lie down on/lie down in direct-object (Lieon) lockVerb: lock direct-object (Lock), lock direct-object with indirect-object (LockWith) lookBehindVerb: look behind direct-object (LookBehind) lookInVerb: look in direct-object (Lookin) lookThruVerb: look through/look thru direct-object (Lookthru) lookUnderVerb: look under/look beneath direct-object (LookUnder) lookVerb: l/look/look around moveNVerb: move north/move n/push north/push n direct-object (MoveN) Note: moveSVerb, moveEVerb, moveWVerb, moveNEVerb, moveNWVerb, moveSEVerb, and moveSWVerb are defined similarly. moveVerb: move direct-object (Move), move direct-object to indirect-object (MoveTo), move direct-object with indirect-object (MoveWith) nVerb: north/n neVerb: northeast/ne nwVerb: northwest/nw openVerb: open direct-object (Open) outVerb: out/go out/exit plugVerb: plug direct-object into indirect-object (PlugIn) pokeVerb: poke direct-object (Poke) pullVerb: pull direct-object (Pull) pushVerb: push direct-object (Push) putVerb: put/place direct-object in indirect-object (PutIn), put/place direct-object on indirect-object (PutOn) quitVerb: quit readVerb: read direct-object (Read) removeVerb: take off direct-object (Unwear) restartVerb: restart restoreVerb: restore direct-object (Restore) standOnVerb: stand on indirect-object (Standon) standVerb: stand/stand up/get up saveVerb: save direct-object (Save) sayVerb: say direct-object (Say) scoreVerb: score/status scriptVerb: script direct-object (Script) screwVerb: screw direct-object (Screw), screw direct-object with indirect-object (ScrewWith) seVerb: southeast/se showVerb: show direct-object to indirect-object (ShowTo) sitVerb: sit on/sit in/sit/sit down/sit down in/sit down on direct-object (Siton) sleepVerb: sleep sVerb: south/s swVerb: southwest/sw takeVerb: take/pick up/get/remove direct-object (Take), take/pick up/get/remove direct-object out of indirect-object (TakeOut), take/pick up/get/remove direct-object off/off of indirect-object (TakeOff) tellVerb: tell direct-object about indirect-object (TellAbout) terseVerb: terse throwVerb: throw/toss direct-object at indirect-object (ThrowAt) touchVerb: touch direct-object (Touch) turnOffVerb: turn off/deactivate/switch off direct-object (Turnoff) turnOnVerb: activate/turn on/switch on direct-object (Turnon) turnVerb: turn direct-object (Turn), turn direct-object with indirect-object (TurnWith), turn direct-object to indirect-object (TurnTo) typeVerb: type direct-object on indirect-object (TypeOn) undoVerb: undo unfastenVerb: unfasten/unbuckle direct-object (Unfasten) unlockVerb: unlock direct-object (Unlock), unlock direct-object with indirect-object (UnlockWith) unplugVerb: unplug direct-object (Unplug), unplug direct-object from indirect-object (UnplugFrom) unscrewVerb: unscrew direct-object (Unscrew), unscrew direct-object with indirect-object (UnscrewWith) unscriptVerb: unscript uVerb: up/u verboseVerb: verbose versionVerb: version waitVerb: wait/z wearVerb: wear direct-object (Wear) wVerb: west/w yellVerb: yell ------------------------------------------------------------ Prepositions in the Adventure Definitions Each preposition is shown with its object, and the list of vocabulary words associated with the object, with slashes between equivalent words. Note that the two-word prepositions, such as "down on," are actually written as one word, such as "downon," in the definition file, because the parser collapses two-word prepositions into a single word by removing the intervening space. aboutPrep: about aroundPrep: around atPrep: at behindPrep: behind betweenPrep: between/in between dirPrep: north/south/east/west/up/down/northe/ne/ northw/nw/southe/se/southw/sw inPrep: in/into/in to/down in fromPrep: from offPrep: off/off of onPrep: on/onto/on to/down on/upon outPrep: out/out of overPrep: over thruPrep: through/thru toPrep: to withPrep: with I hate definitions. BENJAMIN DISRAELI, Vivian Grey (1826) ------------------------------------------------------------