| This file contains a list of changes that have been made to TADS | |
| since from version 2.1.1 up through version 2.2.2. Most of the | |
| changes are fixes to bugs, so they don't change the documented | |
| behavior, but a few, as explained below, add new functionality to | |
| TADS. Releases are listed with the most recent release first; each | |
| release incorporates all new features and bug fixes of each prior | |
| release unless otherwise stated. | |
| Note: this file only covers versions 2.1.1 through 2.2.2. Please | |
| refer to the release notes files included with more recent releases | |
| for information on versions after 2.2.2, and refer to TADSV200.DOS | |
| for information releases prior to 2.1.1. | |
| 2.2.2/MS-DOS patchlevel 1.0 10/20/96 minor corrections | |
| - Added the numberedObject class to adv.t. This class (which is | |
| helpful in using the new numbered object feature) was accidentally | |
| omitted from the 2.2.1.0 release. | |
| - Fixed a problem in the showcontcont() function in adv.t that | |
| reversed the logic of "quiet surfaces" so that ordinary surface | |
| objects did not list their contents in room descriptions. | |
| - Fixed a problem in the parser that resulted in no response at all | |
| when the player typed "all" on a line by itself. The parser now | |
| replies with the usual "there's no verb" message. | |
| - When a number or string (numObj or strObj) is used as a direct | |
| object, the parser now sets 'it' to nil, because it's not sensible | |
| to refer to the number or string with 'it' in a future command. | |
| - Fixed a problem involving using "again" to repeat a command with | |
| a verb using disambigDobjFirst = True. In the past, the parser | |
| was incorrectly using the disambigDobjFirst setting of the "again" | |
| verb itself (which is, of course, set to nil) rather than for the | |
| verb being repeated. (Thanks to Stephen Granade for catching this | |
| problem and tracking down the errant code.) | |
| 2.2.1.0 10/01/96 minor corrections, numbered object enhancement | |
| - Note that there has never been an "official" version 2.2.0.5; | |
| some versions with that designation have appeared on a few | |
| platforms to expedite the release of some bug fixes, but 2.2.0.5 | |
| was never universally released. To eliminate any confusion, this | |
| release is designated 2.2.1.0, and incorporates all of the bug | |
| fixes of and is upwardly compatible with any versions labeled | |
| 2.2.0.5. | |
| - The compiler, runtime, and debugger now create the swap file | |
| according to new rules intended to provide better support for | |
| multitasking environments and read-only media. In the past, if the | |
| user didn't specify a name for the swap file, the swap file was | |
| called TADSSWAP.DAT, and was in the current working directory. | |
| Now, the system looks for the environment variables TEMPDIR, TMPDIR, | |
| TEMP, and TMP (in that order), and takes the value of the first one | |
| defined as the directory to use; it then tries to find a filename | |
| that isn't currently used by any file in that directory. | |
| - The compiler and runtime use a slightly different method to pick | |
| the name of the .GAM file. First, if the name of the output file | |
| is specified to the compiler, the compiler does not attempt to add | |
| a .GAM extension to the file, unless the name matches the input | |
| file name exactly. Second, the run-time will first try to add a | |
| .GAM extension to the name of its input file, but if there is no | |
| such file, it will use the original name exactly as specified. | |
| These new rules are intended to make TADS more flexible on systems | |
| with long, unstructured filenames, such as the Macintosh or Windows | |
| 95, since it is often desirable to name files on these systems without | |
| using an extension in the DOS sense. | |
| - The player command parser has a new feature that allows you to use | |
| numbered objects without specifying in advance an object for each | |
| possible number. For example, if you want to design an elevator | |
| with 100 buttons for floors, you can now create a single button | |
| for all of the 100 buttons. | |
| The new feature uses a special new adjective property setting, '#'. | |
| For example: | |
| elevatorButton: fixeditem | |
| noun = 'button' | |
| plural = 'buttons' | |
| adjective = '#' | |
| location = elevator | |
| ; | |
| The special adjective '#' tells the parser that the object can be | |
| used with any number. The parser accepts the syntax "button 100" | |
| and "100 button" as equivalent, just as it would if you had specifically | |
| included '100' as an adjective with the object -- in other words, | |
| the adjective '#' behaves the same way that regular numeric adjectives | |
| behave. | |
| When the player refers to the button in a command, the parser requires | |
| a number to be used. If the player refers to the button without a | |
| number, the parser displays error number 160, "You'll have to be more | |
| specific about which %s you mean" (where "%s" is replaced with the | |
| phrase the player entered that referred to the button). You can | |
| customize message 160 with parseError as with any other message. | |
| If the player properly enters a number with the object name (by | |
| typing, for example, "button 99"), the parser calls a new method in | |
| the object: newNumbered(actor, v, num) -- "actor" is the actor | |
| for the command (Me if the command was not explicitly directed to | |
| another actor), "v" is the verb object, and "num" is a number | |
| typed by the player. "num" can also be nil: if the player refers | |
| to the button with a plural ("look at buttons"), the parser calls | |
| newNumbered with nil instead of a number to indicate that the player | |
| wants to refer to all such objects collectively. | |
| The newNumbered method must return either an object or nil. If it | |
| returns nil, the method should first display an error message -- the | |
| parser will simply cancel the command without any further messages | |
| if newNumbered returns nil. If newNumbered returns an object, the | |
| parser uses this object (instead of the elevatorButton object itself) | |
| as the numbered object. | |
| adv.t includes a new class, numberedObject, that you can use to | |
| implement numbered objects. This class provides a newNumbered | |
| method that creates a copy of the object, and sets its value | |
| property to the number the player used to refer to the object -- | |
| for example, if the player types "button 99," newNumbered will | |
| create a copy of the button, and the copy's "value" method will | |
| return the numeric value 99. The newNumbered method defined in | |
| numberedObject calls another method, num_is_valid(num), to | |
| determine if the number is valid; if this method returns true, | |
| newNumbered proceeds, otherwise it returns failure. By default, | |
| num_is_valid(num) returns true; you can override this method if | |
| you want to restrict the range of valid numbers. For example, | |
| you would define the elevator button's num_is_valid as follows: | |
| num_is_valid(num) = | |
| { | |
| if (num < 1 or num > 100) | |
| { | |
| "The buttons are numbered from 1 to 100."; | |
| return nil; | |
| } | |
| else | |
| return true; | |
| } | |
| The newNumbered method returns the original object by default if | |
| the player refers to the object with a plural; if you wish to use | |
| another object to handle plural references, override the method | |
| newNumberedPlural(actor, v) to return a different object, or nil | |
| (after displaying an appropriate error message) if you don't want | |
| to allow plural references at all. You may want to handle plural | |
| references with a different object if you want plural references | |
| to work very differently -- in other words, if you want actions | |
| taken on all such objects collectively to have special handling. | |
| The numberedObject defines dobjGen and iobjGen routines that | |
| don't allow any actions to be taken on the object in the plural; | |
| they do this by checking to see if the value property is nil, | |
| which indicates that this is the original (plural) object. These | |
| methods simply display "You'll have to be more specific about | |
| which one you mean" and otherwise ignore the command when the | |
| player uses the plural object. You may want to override this | |
| behavior for some verbs. For example, the elevator button's | |
| dobjGen should probably be defined like this: | |
| dobjGen(a, v, i, p) = | |
| { | |
| if (self.value <> nil or v <> inspectVerb) | |
| inherited.dobjGen(a, v, i, p); | |
| else if (v == inspectVerb) | |
| { | |
| "The boxes are numbered 1000 to 9999."; | |
| exit; | |
| } | |
| } | |
| This allows the player to get some useful information from "look | |
| at buttons," but doesn't allow commands such as "push all buttons." | |
| Note that the implementation of newNumbered in numberedObject | |
| automatically sets a fuse to delete the newly created object at | |
| the end of the command. Therefore, you can't use the new objects | |
| to contain any state that you want to persist after the end of the | |
| command. | |
| For the elevator button example, the only thing you really need to | |
| remember is whether a particular button is lit up or not. You could | |
| do this with a list. When the game is first starting up, you could | |
| initialize this list like this: | |
| local i; | |
| elevator.button_list := []; | |
| for (i = 1 ; i < 10 ; ++i) | |
| elevator.button_list += [nil nil nil nil nil nil nil nil nil nil]; | |
| Now you can define the button's doPush method like this: | |
| doPush(actor) = | |
| { | |
| if (elevator.button_list[value]) | |
| "It's already lit."; | |
| else | |
| elevator.button_list[value] := true; | |
| } | |
| The parser uses one additional method in some circumstances. When | |
| the player uses "any" with an object whose adjective list contains | |
| '#', the parser evaluates the property anyvalue(n) for the object. | |
| The parameter "n" is a number indicating how many of these objects | |
| has been requested so far for this command. Currently, it's always | |
| 1; in the future, a command such as "push any 3 buttons" may call | |
| this method three times, with "n" set to 1, 2, and 3, respectively, | |
| for the three calls. For the time being, "push any 3 buttons" is | |
| treated by the parser as identical with "push buttons". The | |
| default implementation of anyvalue in numberedObject in adv.t | |
| simply returns the same number "n" that it received as a parameter; | |
| if your valid number range doesn't start with 1, you should override | |
| this method to return a number in your object's valid range. | |
| - The compiler now generates more useful errors for undefined objects. | |
| In particular, errors for objects that are used in class lists but | |
| never defined now include the name of the undefined object and the | |
| source file location of the first use. In addition, the compiler | |
| now lists all undefined object errors with a line number prefix giving | |
| the actual line of the error, rather than providing that information | |
| parenthetically in the error message as it did formerly. In | |
| addition, the compiler now checks for undefined objects even when | |
| other errors are encountered, increasing the likelihood that you | |
| can find all errors on the first compile. | |
| - The player command parser now allows a word to start with a dash or | |
| apostrophe (these characters have always been allowed within words; | |
| now they're allowed at the starts of words, too). | |
| - The system allows format strings (such as %you%) to be used while | |
| the init() function is active, and uses Me as the current actor for | |
| translating the format strings. | |
| - Under certain circumstances, the compiler stored the same object/word | |
| relationship twice for a vocabulary word, causing the parser to list | |
| the same object more than once in a disambiguation question ("which | |
| book do you mean..."). This has been corrected. | |
| - The parser's handling of pronouns (it, him, her) has been changed | |
| slightly. Previously, the parser always replied "I don't know what | |
| you're referring to with 'it'" if the antecedant was not accessible | |
| for the new command (the validDo/validIo routine returned a false | |
| indication). The parser now instead uses the standard cantReach | |
| mechanism that would use if the command were typed with the object | |
| explicitly. | |
| - The runtime now calls preinit() prior to calling the restart | |
| callback function if one was provided and the game was compiled for | |
| debugging. (The runtime previously called preinit() *after* calling | |
| the restart callback when the game was compiled for debugging, which | |
| meant that the game state as seen in the callback was different when | |
| a game was compiled for debugging than for the same game compiled | |
| with debugging off, because with debugging off, preinit is run by | |
| the compiler. This change makes the state seen by the restart | |
| callback function the same for both versions of the game.) | |
| - Assignments through property pointers now work properly. | |
| 2.2.0 10/30/94 new features, enhancements, bug fixes | |
| - TADS now has support for reading and writing files. This new | |
| feature is intended to let you save information independently | |
| of the game-saving mechanism, which allows you to transfer | |
| information between sessions of a game, or even between two | |
| different games. The TADS file operations are not designed | |
| as general-purpose file system operations; in particular, | |
| these new functions don't have any provisions for creating or | |
| reading formatted files, or for exchanging information with | |
| programs other than TADS games. | |
| 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." | |
| (For maximum portability, you should avoid using volume names, | |
| directories, folders, or other path information in filenames.) | |
| The mode argument is one of these single-quoted string values: | |
| 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 | |
| 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 | |
| The return value of fopen() is a "file handle"; this is simply | |
| a number that you you to perform subsequent operations on the | |
| file. For example, this opens a new file called TEST.OUT for | |
| writing: | |
| fnum := fopen('test.out', 'w'); | |
| To close an open file, use fclose(): | |
| fclose(fnum); | |
| 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 (you've read past 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: | |
| 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. | |
| - You can now "capture" displayed text to a string. This new feature | |
| allows you to examine and manipulate text displays, no matter how they | |
| are generated. To activate capturing, use the new built-in function | |
| outcapture(): | |
| stat := outcapture(true); | |
| This starts capturing text. The return value is a status code that | |
| you use in the subsequent call to end capturing; you don't need to do | |
| anything with this status code except pass it to outcapture() when | |
| you're finished capturing output. | |
| While capturing is in effect, everything that your game attempts to | |
| display -- through double-quoted strings or through the say() built-in | |
| function -- is hidden from the user and instead stored in a string. | |
| Once outcapture(true) is called, no text will be displayed to the | |
| user until you call this function: | |
| str := outcapture(stat); | |
| The second outcapture() call turns off capturing, and returns a string | |
| that contains all of the text that was generated since the corresponding | |
| outcapture(true). | |
| Note that the system automatically turns off capturing any time it | |
| prompts the user for information. The system turns off capturing | |
| when it starts a new command, or when it needs to prompt the user | |
| for information during a command, such as for disambiguation or to | |
| request a missing direct or indirect object. When the system turns | |
| off output capturing, it clears the capture buffer, so any subsequent | |
| call to outcapture(stat) will return an empty string. You can capture | |
| text only over the course of a single command line. | |
| The outcapture() function can be useful if you want to save some | |
| text for later. It's also useful if you want to be able to examine | |
| the text that would be generated by a method such as sdesc or ldesc. | |
| Since these methods directly display text, you can't directly obtain | |
| a string representation of their values; using outcapture(), however, | |
| you can let them "display" their values into a string that you can | |
| then examine. | |
| Note that the status value returned from outcapture(true) allows you | |
| to nest calls to outcapture(). Since the outcapture(stat) call | |
| restores the capturing status that was in effect on the corresponding | |
| call to outcapture(true), you don't have to worry when using outcapture() | |
| about whether any of the methods you're calling are also using it. | |
| - TADS now allows you to create and delete objects dynamically | |
| at run-time. This is done through two new 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's desired. The default thing.construct 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. | |
| A new object inherits all of the vocabulary of its superclass. | |
| To destroy an object you have created, 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 another object). The default | |
| thing.destruct 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 in memory 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. | |
| - It is now possible to dynamically add to and delete from the | |
| vocabulary words of an object. You can also get the vocabulary | |
| words of an object at run-time. | |
| To add to an object's vocabulary, use the new "addword" built-in | |
| function. This function takes three arguments: an object, a | |
| vocabulary property pointer, and a word to add. For example, | |
| to add 'red' as an adjective to the object myBook, you would | |
| do this: | |
| addword(myBook, &adjective, 'red'); | |
| To delete the same word, you would write a similar call to the | |
| new built-in function "delword": | |
| delword(myBook, &adjective, 'red'); | |
| You can add to and delete from the words of any object, including | |
| both static objects (explicitly defined in your source code) and | |
| dynamically-created objects (created with the "new" operator). | |
| Changes made by addword and delword are tracked correctly by the | |
| UNDO mechanism, and are saved and restored along with saved games. | |
| To get the words belonging to an object, use the new "getwords" | |
| built-in function. This function takes two arguments: an object, | |
| and a property pointer; it returns a list of (single-quoted) | |
| strings, which are the vocabulary words for the object. For | |
| example, assume we define myBook as follows: | |
| myBook: item | |
| sdesc = "small red book" | |
| adjective = 'small' 'red' 'tiny' | |
| noun = 'book' | |
| location = room2 | |
| ; | |
| Also assume we haven't made any calls to addword() or delword() for | |
| myBook. In this case, | |
| getwords(myBook, &adjective) | |
| would return this list: | |
| ['small' 'red' 'tiny'] | |
| Note that the order of the words in the list is not predictable, | |
| so you shouldn't expect the words to be in the same order as they | |
| were when you defined them in the source file, or in the same | |
| order as they were added with addword(). | |
| - We've added a new function that lets you get information on a verb. | |
| The new function is verbinfo(). This function lets you get the | |
| verification and action properties for a verb. The new function | |
| takes one or two arguments: the first is the deepverb object whose | |
| information you want to retrieve; the optional second argument is | |
| a preposition object. If you call verbinfo() with only the verb | |
| argument, it returns the verification and action properties that | |
| are defined with the doAction definition for the verb. If you | |
| also include the preposition argument, it returns the properties | |
| that are defined with the ioAction definition for that preposition. | |
| The value returned by this function is a list. If you call verbinfo() | |
| with only the deepverb argument, the list has two elements: | |
| [1] direct object verification property pointer (verDoXxxx) | |
| [2] direct object action property pointer (doXxxx) | |
| If you call verbinfo() with both the verb and preposition arguments, | |
| the return value is a list with four elements: | |
| [1] direct object verification property pointer (verDoXxxx) | |
| [2] indirect object verification property pointer (verIoXxxx) | |
| [3] indirect object action property pointer (ioXxxx) | |
| [4] true if ioAction has [disambigDobjFirst] flag, nil otherwise | |
| In either case, if no matching doAction or ioAction definition | |
| exists for the verb, this function returns nil. | |
| Note that it is possible that additional flags (similar to | |
| disambigDobjFirst) may be added in the future; the returned list | |
| may be expanded to include information on any such added flags. | |
| So, for compatibility with future versions, we recommend that you | |
| don't write conditional code based on the length of the list. | |
| The lists will never shrink, but they may expand. | |
| For the removeVerb object defined in adv.t, you would get these | |
| results: | |
| verbinfo(removeVerb) | |
| = [&verDoUnwear &doUnwear] | |
| verbinfo(removeVerb, fromPrep) | |
| = [&verDoRemoveFrom &verIoRemoveFrom &ioRemoveFrom nil] | |
| - The ability to create new objects at run-time leads to some | |
| interesting problems involving indistinguishable objects. Although | |
| you should generally use addword (see below) 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 now 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 new 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 thedesc property), while the indistinguishable objects | |
| are listed only once, with "a" (using the adesc property). | |
| - The new property pluraldesc has been added to thing in adv.t. | |
| The definition in thing simply adds an "s" to the end of the | |
| sdesc property. This new property is used by listcont(obj) | |
| when multiple equivalent objects are present in a list; see the | |
| information on the changes to listcont(obj) for details. | |
| - The adv.t functions listcont(obj) and itemcnt(list) have been | |
| changed to support indistinguishable objects. To support this | |
| new functionality, the new functions isIndistinguishable(obj1, obj2) | |
| and sayPrefixCount(cnt), and the new property pluraldesc, have | |
| been added to adv.t. | |
| isIndistinguishable(obj1, obj2) returns true if the two objects | |
| obj1 and obj2 are equivalent for the purposes of listing. The | |
| two objects are considered equivalent if both have the same | |
| first superclass (the return value of the new built-in function | |
| firstsc(obj)), either they are both being worn or neither is | |
| worn, and either both are lit lightsources or neither is. This | |
| function doesn't test the isEquivalent property of either object, | |
| since it's assumed that it will only be called if an object has | |
| already been found whose isEquivalent property is set. | |
| sayPrefixCount(cnt) displays a number. If the parameter cnt is | |
| a small number (from one to twenty), the spelled-out number will | |
| be displayed (for example, "five" will displayed if cnt = 5). If | |
| the count is larger, the number will be displayed as digits ("35" | |
| will be displayed if cnt = 35). This function is used by | |
| listcont(obj) to show the number of equivalent items when more | |
| than one equivalent item is being listed. | |
| itemcnt(list) now returns the number of distinguishable items in | |
| the list that are to be listed. For each item in the list whose | |
| isEquivalent property is true, itemcnt(list) checks each other | |
| item in the list, and counts each set of equivalent items only once. | |
| Hence, if a list consists entirely of equivalent items, itemcnt(list) | |
| will return at most 1 (it will return 0 if none of the items are | |
| listable). | |
| listcont(obj) will list each set of indistinguishable items in the | |
| contents list only once, and will show the number of each such item. | |
| To display the number, the new function sayPrefixCount(cnt) is | |
| used. For example, if a room contains a silver coin, a bronze coin, | |
| and five gold coins (all with the isListed property set to true), | |
| listcont(room) will display this: | |
| a silver coin, a bronze coin, and five gold coins | |
| - A new built-in function has been added to make it possible to | |
| implement the functionality of the new listcont method. The new | |
| function, firstsc(obj), returns the first immediate superclass of | |
| the given object (or nil if the object has no superclass). This | |
| function, along with the isEquivalent property, can be used to | |
| determine if two objects should be considered indistinguishable. | |
| - A new special word has been added: ANY, which is equivalent to | |
| EITHER. These words are at the end of the original specialWords | |
| list; for compatibility with past versions, a specialWords list | |
| that omits this position is still legal, and indicates that the | |
| default ('any' = 'either') should be used for this slot. | |
| This special word slot is used by the parser during disambiguation. | |
| Whenever the parser asks the player to choose an object from a list | |
| of ambiguous objects, it will accept ANY: | |
| >take coin | |
| Which coin do you mean, the silver coin, the bronze coin, or | |
| the gold coin? | |
| >any | |
| silver coin: Taken. | |
| When ANY is used in these cases, the parser will simply pick one | |
| of the objects arbitrarily. Note that it displays the chosen | |
| object in the same manner as it would if multiple objects were | |
| being used. | |
| In addition, the parser will accept noun phrases that start with | |
| ANY to indicate that any object matching the given noun phrase is | |
| acceptable; the parser will choose one of the objects arbitrarily | |
| in these cases. For example: | |
| >take any coin | |
| silver coin: Taken. | |
| >look at any of the coins | |
| bronze coin: It's a valuable 1964 Tadsmid, worth over | |
| .0004 cents on today's scrap bronze market. | |
| The player can also specify the number of items to take. When | |
| a number of items is specified, it must be applied to a plural | |
| noun phrase, and it means the same thing as "any," except that | |
| the parser (arbitrarily) chooses the given number of items rather | |
| than just one. For example: | |
| >look at 3 coins | |
| >look at any 3 coins | |
| >look at 3 of the coins | |
| >look at any 3 of the coins | |
| As a special case, a count of "1" can be used with a singular | |
| noun phrase. It means the same thing as "any." | |
| >look at 1 coin | |
| - A new convenience feature, similar to doSynonym and ioSynonym but | |
| somewhat easier to use, has been added. You can now specify that | |
| a method in one object should instead be sent to another object. | |
| An example: | |
| desk: fixeditem | |
| noun = 'desk' | |
| sdesc = "desk" | |
| location = office | |
| doOpen -> deskDrawer | |
| doClose -> deskDrawer | |
| ; | |
| This specifies that doOpen, verDoOpen, doClose, and verDoClose | |
| calls should be sent to the deskDrawer object when received by | |
| the desk. Note that this should only be used for standard verb | |
| handler methods, because it redirects both the method indicated | |
| and its verXoVerb equivalent. | |
| - The parser calls a new method, multisdesc, when displaying the | |
| name of an object that's part of a list of objects. Previously, | |
| the parser simply used sdesc in these cases. The default adv.t | |
| definition of thing.multisdesc simply calls the object's sdesc. | |
| This new method is intended to allow you greater control over | |
| the display in situations like this: | |
| >get all | |
| book: Taken. | |
| rug: That's much too heavy to carry. | |
| The object names listed before the colons are now displayed | |
| with multisdesc. | |
| For compatibility with old games, if an object being listed does | |
| not define or inherit a multisdesc property, its sdesc is used | |
| instead. This ensures that games compiled with previous versions | |
| of adv.t will continue working properly. | |
| - A new user-defined function that the parser calls has been added. | |
| This new function is called preparseCmd(), and is similar to | |
| preparse(). Whereas preparse() is called once for an entire | |
| command line, and is called with the original, unfiltered text | |
| of the player's command line, preparseCmd() is called separately | |
| for each command on a command line if more then one command is | |
| entered. Furthermore, preparseCmd() is called after the command | |
| has been "tokenized" (broken into individual words). Whereas the | |
| argument to preparse() is a string with the entire command line, | |
| the argument to preparseCmd() is a list, each entry of which is | |
| a (single-quoted) string giving an individual word. Using the new | |
| function, you can exert much greater control over how a command | |
| is parsed, including rewriting a command entirely. | |
| preparseCmd() is called immediately before Me.roomCheck() is called | |
| for the command. This call is made prior to any disambiguation or | |
| object defaulting. | |
| If preparseCmd() returns nil, the command is abandoned (but no | |
| error message is displayed), and no fuses or daemons are run. If | |
| the function returns true, the command proceeds as normal. Any | |
| remaining commands on the same line are executed regardless of the | |
| return value from preparseCmd(). (If you need to cancel the entire | |
| rest of the command line, one approach would be to set a property | |
| in the global object to indicate to preparseCmd() that all commands | |
| are to be ignored; preparseCmd() would always check this property | |
| before doing anything else, and return nil if it were set. You | |
| could clear this property in preparse() so that commands always | |
| start off enabled.) | |
| In addition, preparseCmd() can return a list of (single-quoted) | |
| strings, in which case the parser starts over parsing the list | |
| instead of the original command. The list is limited to a maximum | |
| of 128 characters, with one additional character of overhead per | |
| word -- in other words, you can't make the new command longer | |
| than the original command, which is limited to the same maximum | |
| when entered by the player in the first place. However, you can | |
| otherwise rewrite the command entirely. If you want to include | |
| any special words in the new command, use the conventions described | |
| below (for example, if you want to include 'and' in the new command, | |
| use ',' instead). After the new command inserted by preparseCmd() | |
| has been processed, the parser will resume processing any remaining | |
| commands on the player's original command line. | |
| The new command list returned by preparseCmd() can contain multiple | |
| commands. Simply separate the commands with commas (',') in your | |
| list. | |
| If preparseCmd() returns a list, preparseCmd() will be invoked on | |
| the new command. However, preparseCmd() is not allowed to return | |
| another new command in these cases -- if it does, the parser will | |
| assume that preparseCmd() is looping, and will generate an error. | |
| Several new parseError codes have been added to respond to error | |
| conditions that can arise from preparseCmd(): | |
| 32 Internal game error: preparseCmd returned an invalid list | |
| 33 Internal game error: preparseCmd command too long | |
| 34 Internal gmae error: preparseCmd loop | |
| The sample implementation of preparseCmd() below simply lists all | |
| of the words in the current command and displays a newline, then | |
| allows the command to proceed as usual. | |
| #pragma C+ | |
| preparseCmd: function(cmd) | |
| { | |
| local i, tot; | |
| for (i = 1, tot = length(cmd) ; i <= tot ; ++i) | |
| "<<cmd[i]>> "; | |
| "\n"; | |
| return true; | |
| } | |
| Note that the parser performs conversions of the special words. | |
| These conversions will show up in the list as follows: | |
| "and" becomes "," | |
| "all" becomes "A" | |
| "but" becomes "X" | |
| "it" becomes "I" | |
| "them" becomes "T" | |
| "him" becomes "M" | |
| "her" becomes "R" | |
| "any" becomes "Y" | |
| Here are some examples using the preparseCmd() function above. | |
| (The actual response of the commands has been removed -- only the | |
| text displayed by preparseCmd() is shown.) | |
| >look at all | |
| look at A | |
| >examine him; take everything except the box and the book and go north | |
| examine M | |
| take A X the box , the book | |
| go north | |
| preparseCmd() is called even for commands that the parser doesn't | |
| understand. This allows you to rewrite commands that TADS wouldn't | |
| normally understand and put them into a format that's acceptable to | |
| the parser. For example, the preparseCmd() example below will | |
| take sentences of the form "tell <actor> to <command>", and convert | |
| them to the normal TADS syntax, "<actor>, <command>". | |
| #pragma C+ | |
| preparseCmd: function(cmd) | |
| { | |
| local i, tot, to_loc, actor, the_rest; | |
| tot = length(cmd); | |
| /* check to see if it starts with "tell" */ | |
| if (tot > 3 && cmd[1] == 'tell') | |
| { | |
| /* see if there's a word "to" */ | |
| for (i = 1, tot = length(cmd) ; i <= tot ; ++i) | |
| { | |
| if (cmd[i] == 'to') | |
| to_loc = i; | |
| } | |
| /* if there's a "to", convert the command */ | |
| if (to_loc != nil && to_loc > 2) | |
| { | |
| /* find the parts before and after the 'to' */ | |
| for (i = 2, actor = [] ; i < to_loc ; ++i) | |
| actor += cmd[i]; | |
| for (the_rest = [], i = to_loc + 1 ; i <= tot ; ++i) | |
| the_rest += cmd[i]; | |
| /* convert it to "actor, command" */ | |
| return actor + ',' + the_rest; | |
| } | |
| } | |
| /* otherwise, process the command as normal */ | |
| return true; | |
| } | |
| When the parser doesn't know how to handle a sentence, it calls | |
| preparseCmd with the entire rest of the command (which will include | |
| everything before the next THEN or period), and processing is the | |
| same as in any other case. If preparseCmd() returns nil in this | |
| case, the command is cancelled as usual without any further | |
| processing by the parser. If preparseCmd() returns true when the | |
| sentence is not recognized by TADS, the parser will display the | |
| usual message (parseError code 18, "I don't understand that | |
| sentence"). If preparseCmd() returns a list of strings, the | |
| command is replaced with the list, and the parser starts over | |
| processing the new command. | |
| - Several new parseError codes have been added. | |
| In order to produce better messages when actors are involved, | |
| the system now attempts to figure out whether an object in a | |
| command refers to an actor, and if so, to determine whether the | |
| actor should be called "him" or "her". Previously, this | |
| one-size-fits-all message was generated: | |
| What do you want to <verb> it <prep>? | |
| for example: | |
| >hit bill | |
| What do you want to hit it with? | |
| Although it's not always possible to determine which object | |
| should be used in these cases (because disambiguation will not | |
| be possible until the indirect object is known), the system will | |
| make its best guess. To do so, it looks at all of the objects | |
| that might be involved, based on the vocabulary. If the player | |
| appears to mean multiple objects, " them " will be used -- this | |
| is message number 144 for parseError: | |
| >throw ball and bat | |
| What do you want to throw them at? <- message 144 | |
| ------ | |
| If only one object appears to be intended, the parser will try | |
| to figure out whether the object is male, female, or neuter, | |
| using the isHim and isHer properties (these properties are not | |
| new; they have been around since before TADS 2.0). If all of | |
| the objects that match a single noun phrase have isHim and | |
| not isHer, " him " will be used -- this is parseError message | |
| number 145: | |
| >hit bill | |
| What do you want to hit him with? <- message 145 | |
| ----- | |
| If they all have isHer and not isHim, " her " will be used | |
| (message number 146): | |
| >hit jill | |
| What do you want to hit her with? <- message 146 | |
| ----- | |
| If both are set, the system equivocates with " them ", message | |
| number 147 (note that this is the same default text as message | |
| 144, but it's distinguished as a separate message number in | |
| case a game author wants a more suitable word in this case, | |
| especially in a non-English language): | |
| >hit hermaphrodite | |
| What do you want to hit them with? <- message 147 | |
| ------ | |
| If not all of the objects involved have isHim and/or isHer | |
| set, the system uses "it" (message 141) as in previous versions. | |
| Another change to the processing for this message involves | |
| actors. If an actor is specified in the command, the system | |
| now builds a replacement for message 140. First, message | |
| 148 is displayed, which has the default text "What do you want ". | |
| Then, the actor's thedesc is invoked to display the actor's | |
| name. Finally, message 149 (default text " to ") is displayed, | |
| and the rest of the message is built as before. | |
| >guard, throw ball | |
| What do you want the guard to throw it at? | |
| ----------------- ---- | |
| message 148 149 | |
| - The functionality of the parseAskobj function has been extended | |
| so that you can generate the same sort of message that the parser | |
| now generates when a missing object is needed for a command directed | |
| to an actor. To provide the new functionality, the parser now calls | |
| a function called parseAskobjActor. This function is exactly the | |
| same as parseAskobj, except that it takes the actor as the first | |
| parameter, the verb as the second parameter, and the preposition | |
| as an optional third parameter (which is only present when asking | |
| for an indirect object). | |
| If your game defines parseAskobjActor, the system ignores parseAskobj | |
| and calls the new function instead. If parseAskobjActor is not defined, | |
| but parseAskobj is defined, the system calls parseAskobj as it did in | |
| past versions. If neither function is defined, the system generates | |
| the message itself as described above. You should use parseAskobjActor | |
| rather than parseAskobj for new games, since it gives you more | |
| information. | |
| Here's an example of parseAskobjActor, which generates roughly the | |
| same message as the system would (but it doesn't do any of the checking | |
| of isHim and isHer to determine which pronoun to use -- this function | |
| simply uses "it"). | |
| #pragma C+ | |
| parseAskobjActor: function(a, v, ...) | |
| { | |
| if (argcount == 3) | |
| { | |
| "What do you want "; | |
| if (a <> Me) a.thedesc; | |
| " to <<v.sdesc>> it <<getarg(3).sdesc>>?"; | |
| } | |
| else | |
| { | |
| "What do you want "; | |
| if (a <> Me) a.thedesc; | |
| " to <<v.sdesc>>?"; | |
| } | |
| } | |
| - The parser has been changed slightly to allow a command to specify | |
| an actor in any command within a single line containing multiple | |
| commands. Previously, if an actor was to be specified, the actor | |
| had to be specified at the very beginning of the command. This | |
| restriction has been removed, which allows commands like this: | |
| >joe, north. bob, south. bill, east, take book, west. | |
| Note that, as in past versions, once an actor is specified, the | |
| actor remains in effect for subsequent commands. Since you could | |
| only specify one actor for an entire command line in past versions, | |
| this meant that the actor was used for every command on the line; | |
| with this new version, an actor remains in effect until another | |
| actor is specified. So, in the command above, "north" is directed | |
| to Joe, "south" is directed to Bob, and "east, take book, west" | |
| is directed to Bill. | |
| - The maximum number of notifiers has been increased to 200. The | |
| maximum number of daemons and fuses has been increased to 100 each. | |
| - The maximum number of ambiguous words matching a particular | |
| vocabulary word has been increased to 200. This should relieve | |
| problems that some people have reported with the error message | |
| "The word 'foo' refers to too many objects". | |
| - The compiler now supports most of the remaining C operators: | |
| a % b - returns the remainder of dividing a by b | |
| a %= b - sets a to a % b | |
| a != b - equivalent to a <> b | |
| !a - equivalent to (not a) | |
| a & b - bitwise AND | |
| a &= b - sets a to the bitwise AND of a and b | |
| a | b - bitwise OR | |
| a |= b - sets a to 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 the bitwise XOR of a and b | |
| ~a - bitwise negation of a | |
| a << b - a shifted left by b bits | |
| a <<= b - shifts a left by b bits | |
| a >> b - a shifted right by b bits | |
| a >>= b - shifts a right by b bits | |
| Note a slight complication involving the >> operator: you can't | |
| use this operator from within an embedded string expression, because | |
| it would be confused by the parser for the end of the expression. | |
| It doesn't help to use parentheses, since the embedded string | |
| processing is essentially a textual substitution mechanism which | |
| happens without knowledge of the expression context (and is thus | |
| unaware of parenthesization). For example, this would be illegal: | |
| 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 | |
| Another slight complication arises from the use of the & operator | |
| in lists. Since TADS allows list elements to appear without any | |
| separating punctuation (except whitespace), you can have a list | |
| that looks like this: | |
| mylist = [&sdesc &adesc &thedesc] | |
| This construct is still legal, and is still interpreted with the | |
| "&" operators as unary operators, not bitwise AND operators. | |
| However, the parser now warns when such a definition is used; see | |
| the description of the new warning TADS-357 below for details. | |
| - The compiler will generate a new warning when it detects a unary | |
| operator within a list that could also be interpreted as a binary | |
| operator. For example, in this list, | |
| list2 = [5 -2 -6 -7] | |
| the "-" operators could be interpreted either as unary negation | |
| operators, which would result in a list with four elements (the | |
| numbers 5, -2, -6, and -7), or as binary subtraction operators, | |
| which would result in a list with only one element (the number | |
| -10). This same problem arises with the operators "+" and "&", | |
| since these also have a unary and binary interpretation which | |
| depends on context. | |
| In these cases, the compiler interprets the operators as unary | |
| operators, and issues a warning to let you know that the usage | |
| was ambiguous. The warning is TADS-357: | |
| TADS-357: warning: operator '-' intepreted as unary in list | |
| Note that this is a change from the previous version for the '+' | |
| and '-' operators. If your game depends on building lists from | |
| calculated numeric constants, you will need to change your code; | |
| we don't expect that any games actually depend on the old behavior. | |
| If you really do want the operators in these cases interpreted | |
| as binary operators, use parentheses: | |
| list2 = [(5 -2 -6 -7)] | |
| The parentheses tell the compiler that the expression is to be | |
| interpreted as a single list element. | |
| If you want the unary interpretation, and you want to suppress | |
| the warning, use commas between the list elements: | |
| list2 = [5, -2, -6, -7] | |
| This doesn't change the interpretation, but it does suppress the | |
| warning, because it removes the ambiguity: when the commas are | |
| present, there is no way the '-' operators could be interpreted | |
| as binary operators. | |
| You can suppress the TADS-357 warning if you wish using a new -v | |
| suboption. Specify -v-abin in your compiler options to turn off | |
| the warning ("abin" is short for "ambiguous binary operator", which | |
| is the meaning of the warning). You may want to suppress this | |
| warning when compiling code written prior to this version of TADS, | |
| since the warning is generated in case you wanted the new meaning | |
| of the & operator. | |
| - A new compiler command-line option has been added: -C, a toggle | |
| option, which turns on and off C-language operator compilation. | |
| By default, C operator mode is off (-C-), which makes the compiler | |
| use the normal TADS operators. Specifying -C+ turns on C operator | |
| mode; specifying -C- disables C operator mode. | |
| When C-language operator mode is in effect, two operators are | |
| affected: the assignment operator becomes '=', and the equality | |
| comparison operator becomes '=='. With normal TADS operator mode | |
| in effect, assignment is ':=' and equality is '='. If you are | |
| a C programmer, and you're unhappy with the slight variation in | |
| operator notation between TADS and C, you can use -C+ to make TADS | |
| behave more like a real language. Thanks to the #pragma C options | |
| (see below), C operator mode has no effect on your choice of | |
| header files -- you can use the same old adv.t and other header | |
| files unchanged, and still use C-style operators in your code. | |
| - A new preprocessor command has been added: #pragma. This special | |
| directive can be used to specify certain compiler options from | |
| within your source code. Currently, the only #pragma option available | |
| is the C operator mode option. Use #pragma C+ to turn on C operator | |
| mode, and #pragma C- to turn it off. | |
| The #pragma C+ and #pragma C- settings are local to a particular | |
| file. If a file is included by another file, the #pragma C settings | |
| specified in the included file will be in effect only until the end | |
| of the included file; the including file's #pragma C settings that | |
| were in effect before including the other file will be restored at | |
| the end of the included file. adv.t and std.t now start with a | |
| #pragma C- command -- this allows adv.t and std.t to be included | |
| from a file with C-style operators (and thus a #pragma C+ or | |
| command-line C operator mode setting). Since the enclosing file's | |
| #pragma C option will be restored after the inclusion, files with | |
| different operator modes can be freely intermixed with #include, as | |
| long as each included file specifies its desired mode with a | |
| #pragma C directive. | |
| - The precedence of the comparison operators has been changed to | |
| be the same as that used by C. Previously, all of the comparison | |
| operators were at the same precedence; starting with this version, | |
| == and <> (and thus !=) are at the same level of precedence, and | |
| associate left to right as before, but <, >, <=, and >= are one | |
| level higher in precedence. The following type of expression will | |
| be affected: | |
| a > 1 <> b > 1 | |
| Previously, this grouped as: | |
| (((a > 1) <> b) > 1 // obsolete | |
| This now groups as: | |
| (a > 1) <> (b > 1) // current behavior | |
| This shouldn't affect any existing code, since the old interpretation | |
| should always have resulted in an error (because a truth value, true | |
| or nil, can not be compared in magnitude to a number or other type). | |
| - Limited conditional compilation and preprocessor text substitution | |
| (#define) support has been added to the compiler. The following | |
| preprocessor directives are now available: | |
| #define symbol value | |
| #undef symbol | |
| #ifdef symbol | |
| #ifndef symbol | |
| #else | |
| #endif | |
| #define is used to assign a value to a preprocessor symbol. The | |
| "value" is simply text that will be substituted verbatim for the | |
| symbol whenever it occurs in your file (other than within quoted | |
| strings). For example: | |
| #define TEST say('hello from TEST!') | |
| myfunc: function | |
| { | |
| TEST; | |
| } | |
| The symbol TEST is replaced with its definition, so the function | |
| myfunc() displays "hello from TEST!" when called. | |
| Preprocessor symbols defined with #define are in a separate | |
| namespace from all other symbols in your program. Unlike a | |
| standard C preprocessor, no arguments are allowed in #define | |
| macros. | |
| #undef deletes a previously #define'd symbol. You can #undef | |
| the special symbols defined automatically by the compiler if | |
| you wish (see below). | |
| #ifdef tests to see if a preprocessor symbol is defined. If it | |
| is, the lines following the #ifdef line, and up to the corresponding | |
| #else or #endif, are included; otherwise, they are ignored. | |
| #ifndef is the opposite of #ifdef: #ifndef tests to see if the | |
| symbol is NOT defined. If the symbol is undefined, the lines | |
| following the #ifndef line up to the corresponding #else or | |
| #endif are included; otherwise, they are ignored. | |
| #else indicates that the lines between the #else and #endif are | |
| to be included if an only if the corresponding #ifdef (or #ifndef) | |
| failed. #else is optional. At most one #else is allowed per | |
| conditional. | |
| #endif terminates a conditional block. Exactly one #endif must appear | |
| for each conditional (#ifdef or #ifndef). | |
| You can use #ifdef to compile certain parts of your code | |
| conditionally. For example, if you want to include a verb only | |
| for your debugging version of a game, but you want to remove it | |
| from the final version, you could do something like this: | |
| #ifdef __DEBUG | |
| magicVerb: deepverb | |
| verb = 'xyzzy' | |
| action(actor) = | |
| { | |
| } | |
| ; | |
| #endif | |
| Note that __DEBUG is especially handy for this sort of thing, | |
| because the compiler automatically defines this symbol when | |
| debugging (-ds) is turned no (see below). | |
| - The compiler automatically defines several preprocessor symbols. | |
| These symbols can be used or tested within your code as needed. | |
| __TADS_VERSION_MAJOR is defined to a number indicating the major | |
| version number of the compiler (in the present system, 2). | |
| __TADS_VERSION_MINOR is defined to a number indicating the minor | |
| version number (in the present system, 2). | |
| __TADS_SYSTEM is defined to a string (single-quoted) identifying | |
| the operating system the compiler is running on. In addition, the | |
| same identifier contained in the string is defined as a preprocessor | |
| symbol itself (its value is always 1; it is intended that it will | |
| be tested with #ifdef, and not otherwise used). For example, if | |
| __TADS_SYSTEM is 'MSDOS', the symbol MSDOS will be defined to 1. | |
| If __TADS_SYSTEM is 'Macintosh', the symbol Macintosh will be | |
| defined to 1. | |
| __DEBUG is defined to 1 if debugging is turned on for this | |
| compilation (with the -ds compiler option). Otherwise, this symbol | |
| is not automatically defined. You can test the existence of __DEBUG | |
| with #ifdef to conditionally include code only when you are compiling | |
| for debugging. This might be useful if you want to include certain | |
| commands only in the debugging version of your game, and want to | |
| remove them when you actually deliver the game to players. | |
| If this set of symbols were entered manually with #define statements, | |
| the definitions might look like this: | |
| #define __TADS_VERSION_MAJOR 2 | |
| #define __TADS_VERSION_MINOR 2 | |
| #define __TADS_SYSTEM 'MSDOS' | |
| #define MSDOS | |
| __DATE__ is defined as a single-quoted string giving the system date | |
| when the compilation began, in the format "Jan 01 1980". | |
| __TIME__ is defined as a single-quoted string giving the system time | |
| when the compilation began, in a 24-hour format, "13:40:50". | |
| __FILE__ is defined as a single-quoted string giving the file being | |
| scanned at the point where the __FILE__ macro is encountered. Each | |
| time you use __FILE__, it will have the correct value for that point | |
| in your source code. | |
| __LINE__ is defined as a number giving the line number at the point | |
| where the __LINE__ macro is encountered. Each time you use __LINE__, | |
| it will have the correct value for that point in your source code. | |
| - The compiler has two new options that give you further control | |
| over preprocessor symbols. The -D option allows you to define | |
| a preprocessor symbol from the command line. For example, to | |
| define TEST to 5 from the command line, you could do this: | |
| tc -i/tads/include -DTEST=5 mygame.t | |
| Note that if you omit the equals sign, the default definition | |
| of the symbol will be 1: | |
| tc -i/tads/include -DTEST mygame.t | |
| This defines TEST to 1. | |
| The -U option undefines a predefined symbol. You can use this to | |
| undefine one of the symbols automatically defined by the compiler. | |
| You can also use it to undefine a symbol defined in a precompiled | |
| header, if you're loading one; -U is applied after the precompiled | |
| header is loaded, so it will undefine symbols loaded from the file. | |
| For example, to compile for debugging, but leave __DEBUG undefined, | |
| you could do this: | |
| tc -ds -U__DEBUG mygame.t | |
| The -U option is applied after all -D options, so you can also use | |
| it to undefine a symbol placed earlier on the command line. This | |
| may be useful if you are using a configuation file (CONFIG.TC) | |
| that contains -D options for symbols you sometimes want to undefine. | |
| - The compiler has a new preprocessor directive, #error, which allows | |
| you to generate your own error during compilation. If a #error | |
| directive is encountered, any text after the #error is displayed | |
| as a compiler error; an occurrence of #error is counted as an actual | |
| compilation error, so compilation will fail if #error is encountered. | |
| For example: | |
| #ifndef TEST | |
| # error TEST is not defined! | |
| #endif | |
| If the preprocessor symbol TEST is not defined at the point when | |
| this sequence is encountered, the compiler will display an error: | |
| mygame.t(181): error TADS-124: TEST is not defined! | |
| - The runtime has a new debugging feature that may help you track | |
| down problems with word definitions. You can make the player | |
| command parser generate a number of status messages as it analyzes | |
| a player's command; these messages provide information on how the | |
| parser is interpreting the words in the command. | |
| To activate this new debug mode, use the debugTrace function with | |
| these arguments: | |
| debugTrace(1, true); | |
| To turn the debug mode off, call with nil instead of true. This | |
| debugTrace function is always available, even when running under | |
| the normal runtime; the function returns no value when called with | |
| these arguments. | |
| - The built-in function incturn() has been extended to allow you | |
| to run a series of turns all at once. You can now specify a numeric | |
| argument to incturn(); the argument gives the number of turns that | |
| should pass. An argument of 1 causes incturn() to behave as usual. | |
| When an argument higher than 1 is given to incturn(), the function | |
| runs all of the fuses that are set to burn down within the number | |
| of turns specified, but not after that number of turns. Note that | |
| the normal incturn() has never actually executed any fuses, but | |
| simply burns down all fuses by one more 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 more turn. | |
| - A new built-in function, skipturn(), has been added. This new | |
| function takes a numeric argument specifying the number of turns | |
| to skip; it must be at least 1. skipturn(n) is similar to incturn(n), | |
| except that it does not run any of the fuses that burn down during | |
| the 'n' turns -- instead, it simply removes them without running | |
| them. | |
| - A new built-in function allows you to force capitalization off -- | |
| this function, nocaps(), is the opposite of caps(). If you call | |
| caps() then call nocaps(), the next character is lower-case; if | |
| you call nocaps() then caps(), the next character is capitalized. | |
| Along with nocaps(), the special sequence \v has been added. Using | |
| this sequence in a displayed string is equivalent to calling | |
| nocaps(); this sequence is analogous to \^, which is equivalent | |
| to calling caps(). | |
| - A new built-in function has been added to clear the screen: | |
| clearscreen(), which takes no arguments and has no return value, | |
| simply clears the screen. This routine may do nothing on some | |
| platforms; for example, in -plain mode on DOS, clearscreen() has | |
| no effect. | |
| - The parser is now capable of disambiguating direct objects before | |
| indirect objects. By default, everything works as it always has -- | |
| in a two-object command, the indirect object is disambiguated first, | |
| and then the direct object is disambiguated in the presence of the | |
| known indirect object. | |
| It is now possible, however, to specify that the reverse should be | |
| done. To do this, you use a new special flags syntax when defining | |
| your verb: | |
| tellVerb: deepverb | |
| verb = 'tell' | |
| desc = "tell" | |
| ioAction(aboutPrep) = [disambigDobjFirst] 'TellAbout' | |
| ; | |
| The new special flags are placed in square brackets between the | |
| equals sign and the property template for the verb definition. | |
| The flags currently accepted are: | |
| disambigDobjFirst | |
| disambigIobjFirst | |
| Note that disambigIobjFirst is provided for completeness only; it | |
| is never needed, because it is the default setting. | |
| When the disambigDobjFirst flag is specified, it means that the | |
| command should have its direct object disambiguated before its | |
| indirect object. | |
| When the disambiguation order is reversed, the normal argument | |
| lists for verDoTellAbout and verIoTellAbout are interchanged. | |
| For our example, the prototypes for the verifier methods become: | |
| verDoTellAbout(actor) | |
| verIoTellAbout(actor, dobj) | |
| Normally, verIoVerb would not receive the direct object as an | |
| argument, because the direct object would not be known at the time | |
| of the verIoVerb call; and verDoVerb would receive the indirect | |
| object as an argument, because it would be known by the time the | |
| direct object was being tested. When the disambiguation order is | |
| reversed, however, so are the prototypes to these functions. | |
| The actual action method, ioTellAbout(actor, dobj), remains | |
| unchanged. All other methods also remain the same. | |
| When the direct object is disambiguated first, the player is not | |
| allowed to use multiple direct objects (or multiple indirect objects) | |
| in the command. If the player tries to do so, the new parseError | |
| message number 28 is displayed: | |
| >tell bob and bill about gun | |
| You can't use multiple objects with this command. | |
| - Note that a .GAM file format change was required to support the | |
| extra information needed for the disambigDobjFirst flag. The TADS | |
| file format is now format "C". The compiler is still capable of | |
| producing formats A or B, in case you need to generate .GAM files | |
| that can be played with versions prior to 2.2; however, if you | |
| use a format prior to C, you will not be allowed to use the | |
| disambigDobjFirst flag (or any other similar flags that may be | |
| added in the future). | |
| - Two new methods have been added to disambiguate actors. Previously, | |
| the system validated and disambiguated an actor by pretending that | |
| you were attempting to take the actor -- takeVerb.validDo and the | |
| actor's verDoTake were used to validate and disambiguate the actor, | |
| when all you wanted to do was speak to him. This did not always | |
| produce satisfactory results, and in particular did not allow for | |
| such situations as talking over a radio to an actor in another room. | |
| To provide better control over actor validation and disambiguation, | |
| the system for testing actors has been enhanced. | |
| First, to validate an actor, the system now uses the method | |
| validActor in the actor object itself. This method takes no | |
| arguments; it returns true if the object is valid as an actor in | |
| a command, nil otherwise. This method is called before the verb | |
| or any of the objects involved in the sentence are known. Its | |
| function is not to determine whether the actor wants to receive | |
| the command, or even if the object can be used as an actor | |
| (actorAction is the place to do both of these tests), but rather | |
| simply to determine if the object can be addressed by the player | |
| at all. This method should return true if the actor is accessible | |
| by voice command (or whatever other means you want to provide for | |
| giving commands) to the player. The default thing.validActor in | |
| adv.t returns true if the object is reachable by Me, which provides | |
| roughly the same behavior as the old takeVerb-based mechanism. | |
| Second, to disambiguate an actor, the new preferredActor method | |
| has been added. This method is called if an actor is ambiguous. | |
| As with validActor, it doesn't take any arguments, and it returns | |
| true if the object is "preferred" as an actor, nil otherwise. If | |
| exactly one ambiguous object's preferredActor method returns true, | |
| the parser will use that object as the actor without further | |
| questioning the player; otherwise, the system will ask the player | |
| to disambiguate the noun as normal. In adv.t, movableActor defines | |
| preferredActor = true. | |
| An example of how these might be used: | |
| Sleeping Compartment | |
| You are in a sleeping compartment on a moving train. | |
| A pair of bunks is along each wall. | |
| There is a copper wire here. | |
| The train conductor is standing in the doorway, asking for | |
| your ticket. | |
| >look at conductor | |
| Which conductor do you mean, the copper wire, or the train | |
| conductor? | |
| >copper | |
| It's a piece of wire, about a foot long. | |
| >conductor, where is ivan? | |
| "Your ticket, please," is all the conductor has to say. | |
| In this sequence, when the command is addressed to "conductor", the | |
| parser matches both the copper wire and the train conductor. It | |
| checks validActor in each of them -- both return true, since both | |
| are accessible to the player. (As described above, even though the | |
| copper wire couldn't possibly be an actor, it is valid as an actor | |
| at this point -- it's not until its actorAction that we will decide | |
| that there's no point in talking to it.) | |
| So, the parser has an ambiguous actor. The parser tries to | |
| disambiguate the actor by testing preferredActor in each object. | |
| The copper wire's preferredActor returns nil; the train conductor's, | |
| however, returns true, because the conductor is a movableActor | |
| object. The parser now has only one object, and thus doesn't | |
| need to ask the player for further information. | |
| For compatibility with games compiled with previous versions of | |
| adv.t, the parser will continue to use the old mechanism (involving | |
| the takeVerb) if validActor is not defined in your game. Each time | |
| an actor is used, the parser checks to see if the first object in | |
| the list of possible actor objects has a validActor method defined; | |
| if not, the parser uses the old mechanism. If you use the new adv.t, | |
| all objects will at least inherit a validActor method (from thing), | |
| so testing for the presence of this method in any object lets the | |
| parser determine if the new mechanism can be used with the game. | |
| - A new parseError message has been added: number 27, whose default | |
| text is "You can't repeat that command." This message is displayed | |
| when the player types AGAIN, but the parser can't repeat the command; | |
| this is the case if one of the objects involved in the command is | |
| no longer accessible. | |
| - A new parseError message has been added, related to validActor | |
| (see above). This new message, number 31, is used when none of | |
| the objects matching the vocabulary for an actor in a player's | |
| command can be used as an actor. The default text of this message | |
| is "You can't talk to that." For example, if there is no object | |
| matching the vocabulary "guard" whose validActor method returns | |
| true, but an object named "guard" is visible to the player, the | |
| message is used: | |
| Security Room | |
| You are inside a small cubicle. A thick, laser-proof | |
| (as you now know from your ill-fated attempt) glass door | |
| (closed) is to the north. Through the door you can see | |
| a guard standing watch. | |
| >guard, open door | |
| You can't talk to that. | |
| This message is used for actors in place of the standard cantReach | |
| processing done for direct and indirect objects under these conditions. | |
| - A new parseError code, number 29, has been added. The default | |
| message is "I think you left something out after 'any of'", and | |
| is used when the player uses "any of" in a sentence, but doesn't | |
| follow it with anything. | |
| - Using the same word as both a plural and a noun works much better | |
| now. The parser will first attempt to use such a word as a plural; | |
| if no objects match the plural usage, the parser will try to use | |
| the word as a noun instead. | |
| - "of" can now be used as a preposition. In previous versions of | |
| the run-time, "of" was exclusively a special word that was embedded | |
| in noun phrases (such as "pile of paper"). The runtime will still | |
| allow "of" to be used in noun phrases as before, but it also will | |
| treat "of" as an ordinary word when, based on the context, it does | |
| not appear to be part of a noun phrase. | |
| The parser will still attempt to treat "of" as part of a noun | |
| phrase whenever it matches an object. For example, if you have | |
| an object with nouns matching "pile of paper", then the following | |
| interpretations will apply: | |
| accuse bob of murder -> dobj = bob, iobj = murder, prep = of | |
| accuse pile of paper -> dobj = pile of paper | |
| Note that ofPrep, defining "of" as a preposition, has been added | |
| to adv.t. | |
| - All of the system verbs that use "abort" have been modified | |
| slightly in adv.t to make it easier to augment their behavior | |
| with the 'modify' statement. All of the processing other than | |
| the "abort" has been moved out of the doVerb (or action) method | |
| in each case, and put into a new method. For example, saveVerb's | |
| action routine now looks like this: | |
| action( actor ) = | |
| { | |
| self.saveGame(actor); | |
| abort; | |
| } | |
| The new method saveVerb.saveGame(actor) now performs all of the | |
| processing that the action(actor) method previously performed. | |
| The benefit of this change is that you can now modify the | |
| saveGame(actor) method, and inherit the original behavior, | |
| without having to worry about an "abort" interfering with the | |
| order of operations. For example: | |
| modify restoreVerb | |
| restoreGame(actor) = | |
| { | |
| // restore the game as usual - check for success | |
| if (inherited.restoreGame(actor)) | |
| { | |
| // re-randomize the puzzle | |
| "The carnival hawker flashes a mischevious | |
| 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); | |
| } | |
| } | |
| ; | |
| - The format mask fmtMe has been added. You can now use %me% in | |
| messages to refer to the actor. For basicMe, fmtMe is set to | |
| the message "me"; for other actors, it is set to the actor's | |
| thedesc. In adv.t, thing.ldesc has been changed to use %me%: | |
| "It looks like an ordinary <<sdesc>> to %me%." This makes the | |
| default sentence somewhat more adaptable if you ask another | |
| actor to describe something: | |
| >guard, look at the card | |
| It looks like an ordinary card to the guard. | |
| - AGAIN, WAIT, and SLEEP are now darkVerb's in adv.t. | |
| - A new verb, breakVerb, has been added to adv.t. The vocabulary | |
| words for this verb are 'break', 'destroy', and 'ruin', and it | |
| provides a single-object command "break <direct-object>". The | |
| thing class has been adjusted so that verDoBreak(actor) validates | |
| breaking any object, but doBreak(actor) simply displays "You'll have | |
| to tell me how to do that." If you want to make a breakable object, | |
| simply override doBreak(actor) so that it breaks the object (you may | |
| also want to override verDoBreak(actor) so that it doesn't allow a | |
| broken object to be broken again). | |
| - "there" has been added a synonym for "it" in the specialWords list | |
| in adv.t. This allows sentences like this: | |
| >get box. put ball in there. | |
| - lightsource now has a doTurnnon method in adv.t. This method will | |
| show the room's description if the room becomes lit as a result | |
| of turning on the lightsource. Note that lightsource, by default, | |
| has no verDoTurnon, so you can't turn a generic light source on | |
| and off. However, if you add switchItem to the superclass list of | |
| a lightsource object, you will have a light source that you can | |
| turn on and off, and which will have this new behavior. Note that | |
| you should put lightsource in the superclass list prior to | |
| switchItem, so that lightsource.doTurnon overrides switchItem.doTurnon: | |
| flashlight: lightsource, switchItem | |
| sdesc = "flashlight" | |
| noun = 'flashlight' 'light' | |
| adjective = 'flash' | |
| location = tunnel | |
| ; | |
| - verIoGiveTo and ioGiveTo have been added to movableActor in adv.t. | |
| verIoGiveTo rejects the command if the actor is the same as the | |
| indirect object, otherwise the command is accepted. ioGiveTo | |
| always rejects the offer. In addition, ioGiveTo has been added | |
| to basicMe; the method always accepts anything from another actor, | |
| because this method will only be called in response to commands | |
| such as: | |
| >guard, give me the key | |
| - A small problem with thing.isVisible in adv.t has been fixed. If | |
| the vantage is inside the object, and the object's contents are | |
| visible, isVisible returns true. This is is needed in certain | |
| situations involving nested rooms. | |
| - moveableActor.travelTo(room) in adv.t has been corrected so | |
| that it does nothing when room = nil, which is the case when the | |
| actor can't travel in the desired direction. Previously, the | |
| "noexit" message would be displayed, but then the actor would | |
| be moved into a nil location. | |
| - moveableActor now has a roomCheck method in adv.t. The method | |
| is the same as basicMe's roomCheck method; its omission in | |
| previous versions was an oversight. | |
| - The showcontcont function in adv.t has been corrected so that | |
| the isqsurface flag is checked correctly. | |
| - A new class, seethruItem, has been added to adv.t. This class is | |
| intended for objects such as windows or magnifying glasses which | |
| the player can look through. Each object of this class should | |
| define an appropriate thrudesc method, which displays what the | |
| player sees when looking through this object. | |
| Note that a seethruItem is not the same as a transparentItem. | |
| The class transparentItem is intended for objects, such as glass | |
| bottles, whose contents are visible, whereas seethruItem is intended | |
| for objects that are not containers but which the player can look | |
| through. | |
| - The compiler now sets an error code on exit. If you are using | |
| a MAKE utility or other program-building tool, you can use the | |
| exit code. On success, the compiler uses exit code 0; if any | |
| errors occurred, a non-zero exit code is used. | |
| - The compiler no longer creates a .GAM file if errors occurred | |
| during compilation. If a .GAM file of the same name exists | |
| prior to a compilation, and errors occur, the original .GAM | |
| file is unaffected (it is not deleted or overwritten). Although | |
| the compiler previously produced a .GAM file even when an error | |
| occurred, this .GAM file was not generally usable; to avoid | |
| confusion, the compiler no longer produces a .GAM file at all | |
| when an error occurs. | |
| - The compiler performs more checking for invalid -m parameters. | |
| When -m parameters are entered that exceed the compiler's internal | |
| limits, it will report an error and abort the compilation. The | |
| affected options are -mp, -ml, and -ms. The runtime similarly | |
| checks its parameters more carefully now. | |
| - The DOS runtime no longer displays anything on the status line at | |
| startup. It initializes both the left portion (which normally is | |
| used to display the location) and the right portion (which is | |
| normally used to display the score and turn count) to empty | |
| strings. To ensure that a score is properly displayed at the | |
| start of the first turn, we added a call to scoreStatus(0, 0) in | |
| the "init" function defined in std.t. | |
| - The DOS runtime now has a plain ASCII mode. In this mode, the | |
| runtime uses only DOS character input and output functions, | |
| performs no highlighting or cursor positioning, and does not | |
| display a status line. Command editing and screen scrollback | |
| are also disabled (although normal DOS keyboard editing can be | |
| used, and any loaded command editor, such as DOSKEY, will be | |
| active). This mode is intended for people with a special need | |
| to use direct DOS character I/O, such as persons using a voice | |
| synthesizer. To invoke this mode, specify -plain in the runtime | |
| command options: | |
| tr -plain mygame | |
| Note that .EXE files produced by MAKETRX can be used in this | |
| manner as well; simply specify -plain with the game program: | |
| deep -plain | |
| - The DOS runtime now uses the control-left-arrow and | |
| control-right-arrow to move the cursor left and right by one | |
| word in the command editor. This behavior is consistent with | |
| most other DOS command editors. | |
| - $$ABEND can now be used after a question from the parser, such | |
| as during disambiguation and when OOPS is permitted. | |
| - The runtime handles hyphenation better. Multiple hyphens will | |
| no longer be split across lines -- so if you use two hyphens | |
| together for a dash, both hyphens will always be grouped on one | |
| line. Furthermore, the runtime formatter will never put a dash | |
| at the very start of a line; if a line must be split at a dash, | |
| the formatter will put the dash at the end of the line, then | |
| break the line, and will back up and split at the previous word | |
| if necessary. | |
| - The runtime will now properly convert "\" sequences in askfile() | |
| prompts properly. If you use a \n, \t, \', \", or \\ sequence | |
| in a prompt to askfile(), the sequence will be displayed as the | |
| appropriate character. (For the DOS runtime, this only matters | |
| in -plain mode, because the DOS file selector in the normal full- | |
| screen mode doesn't use the prompt string.) | |
| - The DOS runtime's file selector dialog incorrectly interpreted | |
| the Alt keys for disks in the last version -- Alt-B selected the A | |
| drive, Alt-C selected the B drive, and so forth. This has been | |
| corrected. | |
| - The MAKETRX command now takes an additional argument that lets | |
| you specify the run-time command options that should be used when | |
| the game is executed. MAKETRX still accepts the original command | |
| formats; using one of the old-style command formats will not bind | |
| any command options into the resulting .EXE file. | |
| To specify command options for your game executable, you must | |
| first create a file. Use the same format as CONFIG.TR -- simply | |
| enter your options into the file as you would on the TR command | |
| line; separate options by newlines or spaces. For example, to | |
| specify a minimal cache size and a swap file of SWAP.DAT, you | |
| could make your CONFIG.TR file look like this: | |
| -m0 -tf swap.dat | |
| Once you've created a file with your command options, specify | |
| that file to MAKETRX by using it as the first parameter on the | |
| command line, prefixed with an "at" sign (@): | |
| maketrx @config.tr mygame | |
| The @config option can be used with any of the original command | |
| formats for MAKETRX. Once the config file is bound into your | |
| executable, its options will be used every time a player runs | |
| your game's .EXE file. Note that you may want to avoid specifying | |
| anything specific to your system, such as drives or directories, | |
| since that may prevent the game from working properly on someone | |
| else's system. | |
| - The new sentence parsing for the form VERB PREP IOBJ DOBJ | |
| (introduced in 2.1.1) causes a subtle problem: if you create | |
| an object with an adjective that is also a preposition, the | |
| system attempts to interpret sentences with a single-word verb | |
| that refers to the object as VERB PREP IOBJ. The result is | |
| this: | |
| >push off button | |
| What do you want to push? | |
| This is because the parser is interpreting the sentence as | |
| "push something off button", and needs to know the "something". | |
| This problem has been corrected. Now, the parser will check this | |
| type of case to see if the preposition can also be used as an | |
| adjective, and if so, checks to see if a noun (possibly preceded | |
| by one or more adjectives) follows; if this test is met, the word | |
| will be interpreted as an adjective, as it should be. | |
| - The parser now correctly distinguishes between cases involving | |
| a word that is defined as both a preposition and an adjective. | |
| The parser previously did not accept sentences such as this: | |
| >enter south wall | |
| when 'south' was defined as an adjective. Because 'south' was | |
| also defined as a preposition (in adv.t), the parser attempted | |
| to interpret this sentence as though 'enter south' were a verb | |
| (in the same manner as 'pick up' or 'put down'). The parser | |
| now checks to make sure that 'enter south' is a valid combination | |
| verb; if it's not, and 'south' is also defined as another part of | |
| speech, the parser assumes that 'enter' is the verb, and treats | |
| 'south' as an adjective. | |
| - The parser previously asked to disambiguate a direct object twice | |
| if askio(prep) was used. For example: | |
| >unlock door | |
| Which door do you mean, the large door, or the small door? | |
| >large | |
| What do you want to unlock it with? | |
| >key | |
| Which key do you mean, the silver key, or the gold key? | |
| >gold | |
| Which door do you mean, the large door, or the small door? | |
| >large | |
| The door unlocks with a satisfying click. | |
| This has been corrected -- the parser now only asks once about | |
| the door. | |
| - The parser did not previously accept a word during disambiguation | |
| if the word was defined as both a noun and an adjective. For | |
| example, if you've defined objects "violet paper", "violet banana", | |
| and "paper towel", and the parser asked you this: | |
| >x violet | |
| Which violet do you mean, the violet paper, or the violet banana? | |
| then you couldn't respond with "paper". This has been corrected. | |
| - The inputkey() function now clears the "more" line counter. So, | |
| when inputkey() is called, a "more" prompt will not show up until | |
| another screenful of text has been displayed. | |
| - "local" statements that occur out of context (i.e., as other than | |
| the first statements after an open brace) now generate better | |
| diagnostics. | |
| - Dividing by zero is now flagged as a run-time error. | |
| - A problem involving the embedded string << >> notation has been | |
| corrected. Under certain circumstances, if one expression used | |
| in an embedded string invoked another string with an embedded | |
| expression, an error occurred (usually "invalid type for built-in | |
| function"). This should no longer occur. | |
| - The system did not properly handle lists containing function | |
| pointers. This has been corrected. | |
| - A problem involving pre-compiled headers and 'modify' has been | |
| corrected. Several people have encountered problems that generally | |
| were manifested as "assert" failures in mcm.c (the cache manager) | |
| when using 'modify' and pre-compiled headers; these should no | |
| longer occur. | |
| - An internal cache corruption occurred under certain obscure | |
| circumstances involving 'modify'. This has been corrected. | |
| - A note on roomCheck: if you return nil from roomCheck, fuses | |
| and daemons are NOT run; in this sense, returning nil from roomCheck | |
| is equivalent to using abort in other methods. (This is not a | |
| change -- it's always worked this way -- but this behavior is | |
| not mentioned in the TADS Author's Manual.) | |
| - A note on using << >> embedded strings: If you put a newline | |
| immediately before the << of an embedded string, any spaces between | |
| the last non-space character preceding the << and the << will be | |
| lost. If you want a space before the <<, do not put it on a new | |
| line. Instead, you can put it at the end of the line, and put | |
| the embedded expression itself on the next line. For example: | |
| embeddedString = "embedded string" | |
| sdesc = "This is a message with an | |
| <<self.embeddedString>>!" // wrong | |
| This will display as follows: | |
| This is a message with anembedded string! | |
| If you intend a space to precede the embedded string, you should | |
| write this as follows: | |
| sdesc = "This is a message with an << | |
| self.embeddedString>>!"; // right | |
| (This is not a change, but simply a note for game developers who | |
| have encountered this problem.) | |
| 2.1.2 11/22/93 enhancements, bug fixes | |
| - You can now detect when the player uses multiple direct objects | |
| with a verb, and reject such commands. Whenever the player uses | |
| multiple direct objects with a command (or uses "all", even if it | |
| results in a single object being used), the parser calls the | |
| verb object's rejectMultiDobj(prep) method. If you don't wish to | |
| take any special action for multiple direct objects used with a | |
| particular verb, simply return nil from this method (or don't | |
| define the method at all for the verb). If you want to prevent | |
| multiple direct objects from being used, however, you should display | |
| an appropriate message, and return true. The parser will skip the | |
| command entirely. Note that the parser doesn't display any additional | |
| message when rejectMultiDobj(prep) returns true; the method should | |
| display whatever message is desired. The "prep" parameter is the | |
| preposition object used with the command; it will be nil if no | |
| indirect object is present. An example: | |
| modify inspectVerb | |
| rejectMultiDobj(prep) = | |
| { | |
| "You can only look at one thing at a time."; | |
| return true; | |
| } | |
| The verb's rejectMultiDobj(prep) method is called immediately | |
| before the actor's actorAction method. Note that the parser will | |
| continue processing any remaining commands on the line, and will | |
| then run daemons and fuses as normal, even if rejectMultiDobj(prep) | |
| returns true; if you want to stop the current turn altogether, use | |
| abort. | |
| - The player command parser now gives you greater control over object | |
| validation error reporting. In previous versions, if an object was | |
| visible but did not pass the validIo/validDo test, the parser called | |
| the object's cantReach method to report the error (see the note below | |
| about an additional change to object.cantReach processing). | |
| Now, however, the parser will call verb.cantReach instead, if the | |
| command's deepverb object defines (or inherits) a cantReach method. | |
| If the verb does not have a cantReach method at all, the parser | |
| will use the old behavior instead. The new cantReach method should | |
| be defined as follows: | |
| myVerb: deepverb | |
| verb = 'whatever' | |
| cantReach(actor, dolist, iolist, prep) = | |
| { | |
| // your code here | |
| } | |
| ; | |
| This method doesn't return a value; it simply displays the appropriate | |
| message explaining why the object can't be used with the command. | |
| verb.cantReach is used only when the objects are visible (that is, | |
| object.isVisible(actor) returned true for each object in the list). | |
| Only one of dolist or iolist will be non-nil. If the direct object | |
| of the command refers to one or more objects that are visible but | |
| can't be used (according to validDo), dolist will be a list of all | |
| such objects, and iolist will be nil. Otherwise, iolist will be a | |
| list of such objects used for the indirect object, and dolist will | |
| be nil. | |
| adv.t has not been changed to use verb.cantReach. This change has | |
| been made to provide finer control for game authors implementing | |
| their own verbs and object validation procedures. | |
| - The player command parser had an odd quirk when ambiguous nouns | |
| were used with transparent items. If the player used a command | |
| containing a noun that referred to multiple objects that were | |
| visible but were not valid for the verb (for example: "take trophy" | |
| in a room containing a closed glass trophy case containing a bowling | |
| trophy and a tennis trophy), the parser asked the normal disambiguation | |
| question. This was not really necessary, because the parser already | |
| knew that the objects were invalid. This has been changed; the parser | |
| now simply uses the cantReach message for *each* object that is | |
| visible and matches the vocabulary, using the usual multiple-word | |
| format: | |
| bowling trophy: You'll have to open the glass case first. | |
| tennis trophy: You'll have to open the glass case first. | |
| This new behavior should have no effect on your game code. Note | |
| that it is entirely irrelevant if you use the new verb.cantReach | |
| feature described above. | |
| - The compiler sports a new case sensitivity option. By default, | |
| the compiler is case-sensitive, as it has been in past versions. | |
| However, the new toggle option allows you to change this. Specify | |
| -case- to turn off case sensitivity (the default is -case+). Note | |
| that this is a toggle option, so simply using -case will reverse | |
| the current case sensitivity (which is useful if you use a CONFIG.TC | |
| file that sets a non-default case option). When case sensitivity | |
| is turned off, the compiler will treat upper- and lower-case letters | |
| in symbols (names of objects, properties, functions, and local | |
| variables) as equivalent. Hence, foodItem = fooditem = FoodItem, | |
| and so on. | |
| If you create a pre-compiled header with -w, any compilation which | |
| reads that binary header with -l will use the same case sensitivity | |
| as was in effect when the header was pre-compiled. The -case option | |
| is ignored when a pre-compiled header is loaded. Likewise, the | |
| debugger uses the same case sensitivity that was in effect when the | |
| game being debugged was compiled. | |
| - The debugger's command set is no longer case-sensitive (hence, | |
| BP = Bp = bP = bp). | |
| - adv.t has a new darkVerb class. This is a type of deepVerb that | |
| can be used in the dark. The darkroom class has been changed to | |
| accept any verb of class darkVerb in its roomAction and roomCheck | |
| methods. The travel verbs and system verbs have all been made | |
| darkVerb objects, so darkroom only needs to check the single | |
| verb type. | |
| In a related change, turnOnVerb and dropVerb have been changed to | |
| be darkVerb objects, allowing the player to turn on an object or | |
| drop it in the dark. The verDoTurnon method in switchItem has | |
| been changed so that it checks to see if the player is in a dark | |
| room; if so, the item can only be turned on if the player is already | |
| carrying the object. This allows the player to turn on a light | |
| source that's already being carried, but doesn't allow the player | |
| to pick up a light source in a dark room. | |
| - The compiler issues a new warning message (TADS-452) if you | |
| use the same verb with two deepverb objects. The parser can only | |
| choose a single deepverb object for any verb typed by the player, | |
| so you should never define the same 'verb' vocabulary word in more | |
| than one deepverb object. In past versions, the compiler did not | |
| flag this as a warning. | |
| - Several improvements have been made for numbers in player | |
| commands. First, multiple numbers are now allowed in a single | |
| commands; for example, the player can now say "press 1, 2, 3, | |
| 4 on keypad," and the numbers are set in numObj.value, one by | |
| one. Second, the sdesc, adesc, and thedesc properties of | |
| basicNumObj have been improved to show the number's value. | |
| - Similar improvements to those for numbers have been made for | |
| strings. Multiple strings are now allowed in a single command, | |
| and the basicStrObj properties sdesc, adesc, and thedesc have | |
| been improved to show the string's value. | |
| - specialWords has been enhanced. First, in past versions, if a | |
| game contained multiple specialWords statements, the word lists | |
| were additive -- all specialWords lists were used in the game. | |
| This has been changed so that each specialWords statement replaces | |
| any previous list in effect. However, you now can explicitly add | |
| to the specialWords list, without removing any of the previously | |
| defined words, by using "modify specialWords". When you use | |
| 'modify', you can use nil in any word slot if you do not wish | |
| to add any words for that slot. Finally, you can use "replace | |
| specialWords" to make the replacement explicit; this is the default | |
| if neither 'modify' nor 'replace' is specified, but the compiler | |
| will now issue a warning (which is harmless) if you use specialWords | |
| without 'replace' or 'modify' and a previous specialWords list is | |
| already in effect. | |
| - The words "one" and "ones" (or their equivalent for your game, | |
| if you've changed them with specialWords) are no longer considered | |
| reserved words within normal commands. This allows you to use | |
| objects such as a "one dollar bill"; previous versions rejected | |
| player commands containing "one" or "ones". These words are now | |
| considered special only during the parsing of a response to a | |
| disambiguation question, when they can be used in place of a | |
| noun ("the red one" can be used in answer to "Which book do you | |
| mean..."). | |
| - The hider class has been changed so that 'it' or 'them' (as | |
| appropriate) are set to the object or objects found when | |
| searching the hider. | |
| - The verDoPutIn and verDoPutOn messages in thing and surface | |
| (respectively) have been improved for the somewhat obscure case | |
| of attempted circular containment - that is, putting an object | |
| into a container, when the container is already in the first object | |
| (either directly or by virtue of being inside another object which | |
| is inside the first object, or inside an object which is inside an | |
| object which is inside the first object, and so on). The new | |
| method thing.circularMessage(iobj) is called in these cases to | |
| display an appropriate message; the default implementation of this | |
| method displays the complete list of containers of the direct | |
| object out to the indirect object. For example, if you have a | |
| crate which contains a box, and you try to "put crate in box", | |
| the message is "You can't put the crate in the box, because | |
| the box is already in the crate." | |
| - The default doTake method has been changed to include the weight | |
| of any contents of the item being taken, in addition to the item | |
| itself, to determine if the actor's inventory is too heavy. | |
| The old doTake method only included the weight of the object | |
| being taken, not counting its contents. | |
| - The Actor class has been changed to add a travelTo method. You | |
| can now move any actor (Me included) with travelTo(destination). | |
| The default Actor.travelTo method moves the actor; it announces | |
| the departure of the actor if the actor was in the same location as | |
| Me before leaving (and the location is lit); and it announces the | |
| arrival of the actor if the actor is moving into the same location | |
| as the player (and the location is lit). The departure message | |
| is generated with a call to self.sayLeaving, and the arrival | |
| message is generated with self.sayArriving. The default versions | |
| of these methods simply display "Thedesc leaves the area" and | |
| "Thedesc enters the area", respectively; you can override these | |
| methods if a more specific message is desired. | |
| - When modifying a class object with 'modify', the modified object | |
| was not a class unless the 'class' keyword was included with the | |
| 'modify' statement ("modify class foo" rather than "modify foo"). | |
| This has been corrected; a modified class is still a class. | |
| - outhide(true) now returns a status indicator, which is a value that | |
| can be used in a subsequent call to outhide() to return output | |
| hiding to the state it was in before the outhide(true). This | |
| allows you to nest text hiding. When you use the nested form | |
| (which you do simply by using the return value of outhide(true) 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 ONLY BETWEEN THE NESTED CALLS. For example: | |
| old_stat1 := outhide(true); | |
| "This is some hidden text."; | |
| old_stat2 := outhide(true); | |
| // write no 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 = nil. However, new_stat1 | |
| = 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. | |
| The general form of a nested hidden output section looks like this: | |
| { | |
| local original_hide_stat; | |
| local nested_stat; | |
| original_hide_stat := outhide(true); | |
| // do whatever you want to do while output is hidden | |
| nested_stat := outhide(original_hide_stat); | |
| } | |
| Now nested_stat will indicate whether any output occurred during | |
| the nested outhide() - that is, between the outhide(true) and | |
| the outhide(original_hide_stat). In addition, output hiding will | |
| be returned to the same state it was in prior to the original | |
| outhide(true). | |
| - The random number generator has been improved. Many people have | |
| complained about the many undesirable properties of the old | |
| generator, especially when small upper limits were used. The | |
| interface to the new random number generator is the same as | |
| before - call rand(upper_limit), which will return a uniformly | |
| distributed random number from 1 to upper_limit, inclusive. | |
| Note that the old random number generator will still be used if | |
| you don't call randomize(). This allows test scripts (which require | |
| a fixed sequence of random numbers in order to be repeatable) that | |
| were written with older versions to continue to operate unchanged. | |
| If you want numbers from the improved generator, be sure to call | |
| randomize(). | |
| - When 'modify' was used on an object, the compiler sometimes did | |
| not correctly apply the original object's vocabulary and location | |
| to the new object. This has been corrected. | |
| - restore() and undo() have been changed so that they always cancel | |
| all pending commands on the command line. In the past, if the | |
| player typed several commands, and something happened (such as | |
| the player character dying) during one of the commands that led to | |
| an undo() or restore(), the remaining commands were still | |
| executed. This has been fixed. | |
| - If you explicitly set an object's location to nil in its object | |
| definition, and the object inherited a location from a superclass, | |
| the system incorrectly placed the object in the contents list of | |
| the object named in the location inherited from the object's | |
| superclass. This has been corrected. | |
| - "abort" can now be used within a daemon or fuse, and the | |
| expected behavior will occur. In the past, "abort" within | |
| a fuse (or daemon) merely exited from the current fuse, | |
| but the remaining fuses and daemons were still executed. | |
| Now, "abort" will cause the entire turn to end; no more | |
| fuses or daemons will be executed on the current turn. | |
| 2.1.1 09/09/93 enhancements | |
| - You can now access objwords(1) while deciding whether to use a | |
| default direct object in doDefault. This is useful mostly if | |
| you want to prevent players from being able to use "all" with | |
| certain verbs, but still want to generate a default direct object | |
| for the verbs. To do this, you can detect when objwords(1) = ['A'] | |
| ('A' is the parser's internal code for "all", which saves you the | |
| trouble of checking for "everything" and shorter abbreviations as | |
| well as "all"): | |
| doDefault(actor, prep, iobj) = | |
| { | |
| if (objwords(1) = ['A']) | |
| { | |
| global.allMessage := 'You can\'t use "all" with this verb.'; | |
| return []; | |
| } | |
| /* your normal default object code goes here */ | |
| } | |
| If you wish, you can also suppress the default message that the | |
| parser will generate ("I don't see what you're referring to"). | |
| To do this, you'll have to write your own parseError() function | |
| and detect when an "all" violation has occurred (cleverly using the | |
| global.allMessage, which we set above for this purpose): | |
| parseError: function(str, num) | |
| { | |
| // if there's an allMessage waiting, use it instead of the default | |
| if (global.allMessage <> nil) | |
| { | |
| local r; | |
| r := global.allMessage; | |
| global.allMessage := nil; | |
| return r; | |
| } | |
| else | |
| return nil; | |
| } | |
| - The compiler's error message format has been changed slightly | |
| to work better with editors and workbench programs that scan error | |
| logs to go to lines with errors. The format is now: | |
| file(line): error TADS-xxxx: message | |
| For example: | |
| deep.t(1151): error TADS-300: expected colon | |
| - The parser now accepts sentences of the form VERB PREP IOBJ DOBJ, | |
| where the PREP is *not* part of the verb. For example, GIVE TO THE | |
| MAN THE BALL. This change has two benefits. First, while this | |
| type of sentence is not common in English, some other languages | |
| allow this type of phrasing, so the parser is now somewhat more | |
| adaptable to non-English languages. Second, this allows for | |
| object defaulting and command completion when specifying just the | |
| indirect object, which was not possible before. For example, if | |
| the player types ASK FOR A BEER, the parser will be able to attempt | |
| to provide a default (if one is available), or at least ask for the | |
| direct object. Previous versions would simply say "I don't understand | |
| that sentence." Note that the parser still attempts to combine the | |
| verb and preposition into a single phrase; the new action happens | |
| only when the verb and preposition don't go together (that is, they | |
| haven't been defined together as a "verb =" property of a deepverb). | |
| For example, suppose that a line like this appears in a deepverb: | |
| verb = 'pick up' | |
| In this case, PICK UP THE BOX will use THE BOX as the direct object, | |
| just as in previous versions. Only when the verb-preposition combination | |
| is not specifically defined in a verb will the new phrasing be used. | |
| - When no preposition is specified between the direct and indirect objects, | |
| the parser will now evaluate a new property, nilPrep, in the deepverb | |
| object. This property should return the preposition object that should | |
| be used as the preposition between the objects. Previous versions of | |
| TADS always looked for an object that defined the word 'to' as a | |
| "preposition =" property. While 'to' is almost always the correctly | |
| the correct preposition to substitute in English, it's obviously the | |
| wrong word in other languages; furthermore, the correct word in other | |
| languages is sometimes a function of verb. If no nilPrep property is | |
| defined for the deepverb, the parser will still use the object whose | |
| "preposition =" property matches the word 'to'. | |
| - The class transparentItem in adv.t has been modified so it works better | |
| when you define an object that inherits from both transparentItem and | |
| container or openable. First, an ldesc has been added so that the | |
| contents of a transparentItem are listed by default with the ldesc. | |
| Second, the "look in" command now works on a transparentItem. In | |
| addition, the openable class has been changed so that the "look in" | |
| command can be used when an openable is also a transparentItem, even | |
| when the openable is closed (because you should be able to see the | |
| contents of a transparentItem regardless of whether it's open or closed). | |
| Thanks to Ron Hale-Evans for pointing out this problem and finding the | |
| solution. | |
| Please consult TADSV200.DOS for information on earlier releases. | |
Xet Storage Details
- Size:
- 110 kB
- Xet hash:
- 4c9e623e201fb58032af9c02d0e883900bfab30230977d4a922d5af5b6057b7a
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.