TADS Revision History

Recent Changes to TADS

#### Index to Versions

- Version 2.5.17
- Version 2.5.16
- Version 2.5.15
- Version 2.5.14
- Version 2.5.13
- Version 2.5.12
- Version 2.5.11
- Version 2.5.10
- Version 2.5.9
- Version 2.5.8
- Version 2.5.7
- Version 2.5.6
- Version 2.5.5
- Version 2.5.4
- Version 2.5.3
- Version 2.5.2
- Version 2.5.1
- Version 2.5.0

#### Organization of this Page

This page is a list of recent changes to TADS, arranged chronologically: changes are grouped by release, with the most recent release listed first. Each release incorporates all new features and corrections of each prior release unless otherwise stated.

#### Generic and Platform-Specific Changes

Since version 2.2.6, the TADS change log has been divided into two separate files: one for "generic" changes that apply to TADS on all types of computers and operating systems, and one for changes that apply only to specific platforms. This file contains the generic release notes. Platform-specific release notes are in a separate file for each platform:

> Changes specific to MS-DOS and Windows

#### Multimedia vs. Text-Only Interpreters

These release notes sometimes refer to the "character-mode" or "text-only" TADS Interpreter. This is meant to distinguish the traditional TADS Interpreter, which can only display text, from the multimedia interpreters such as HTML TADS and HyperTADS, which can display graphics as well as text. The traditional TADS Interpreter has a graphical user interface on some systems, such as the Macintosh, so it's not really a character-mode application on those systems; nonetheless, we still refer to it here as the character-mode Interpreter simply to make it clear that we're not talking about one of the multimedia versions.

#### Older Revisions

To keep the size of this page under control, changes are listed here only for the most recent several versions of TADS. Older release notes are in separate files:

- Generic changes from version 2.2.3 through 2.4.0 are in TADSV240.TXT.
- DOS-specific changes from version 2.2.3 through 2.4.0 are in TADSV240.DOS.
- For DOS, all revisions from 2.0 through 2.1.0 are available in the file TADSV200.DOS; those from 2.1.1 through 2.2.2 are in TADSV222.DOS.
- Similar files are available for other platforms; please refer to your platform-specific release notes for details.

Because the older revision history files are quite large and are static (they are, after all, historical), they're not included in the normal TADS distributions, but you can download them from the Interactive Fiction Archive via the internet at ftp://ftp.ifarchive.org/if-archive/programming/tads2/manuals/ (note that ftp.ifarchive.org is the new home, effective August 2001, of the former ftp.gmd.de archive).

### Version 2.5.17

Released May 16, 2013

- In the past, HTML <PRE> blocks were problematic if you wanted to use <PRE> for precise indentation. The complication was that the output formatter in the VM pre-filtered the text sent to the HTML parser to consolidate each run of whitespace into a single space. If you wanted to use spaces in a <PRE> block for alignment purposes, you had to use quoted spaces (`"\ "` sequences) to make sure that all of the spaces were sent through to the HTML parser.

  Now, the VM formatter layer watches for <PRE> tags. Within a <PRE> block, the VM formatter passes spaces and newlines through to the HTML parser without any filtering. This change addresses bug #0000178 (which was filed as a TADS 3 bug, but effectively applied to TADS 2 as well, because the output formatting and HTML handling are very similar in TADS 2 and TADS 3).

  Note that there's another slight complication if you're writing multi-line preformatted blocks with spacing for indentation. The compiler normally removes the spaces that follow a line break within a string. To preserve spaces after a line break within a string, put an explicit `\n` sequence at the start of each line within a multi-line string.

- On 64-bit Linux platforms, a bug in the compiler caused spurious errors ("invalid property value") when an object definition contained a property value set to a list containing constant values (e.g., `p = [1]`). This has been corrected. (bugdb.tads.org #0000106)

### Version 2.5.16

Released on August 30, 2012

- A couple of bugs appeared in version 2.5.15 due to internal changes in the file safety mechanism. In particular, stand-alone executable games on Windows could sometimes crash on start-up, and compiling a game that accessed files within its preinit function sometimes failed due to a spurious file error. The second problem affected games using the gameinfo.t module to create a GameInfo file within preinit. These are now fixed.

### Version 2.5.15

Released on December 21, 2011

- In past versions of the text-only interpreters, if an <ABOUTBOX> section contained an <IMG> tag with an ALT attribute, the ALT text was displayed. It shouldn't have been, because the text-only systems aren't supposed to display anything that's within an <ABOUTBOX> section. This has been corrected. (bugdb.tads.org #0000063)
- The text-only interpreters now parse and translate HTML entity markups ("&" sequences) within <TITLE> tags when setting the window title. (In past versions, the text-only interpreters simply displayed the literal text of the & sequences. The HTML interpreters didn't have this problem, so this change doesn't affect them.) (bugdb.tads.org #0000062)
- Some older games (specifically, compiled with a version before TADS 2.3.0, from 1999) use an old version of the file dialog functions that wasn't able to specify the file type. For modern GUI operating systems, the file type is fairly important, because the file selection dialog usually uses this to filter the file list, and to set default file types or extensions for new files. The interpreter now tries to infer when the interpreter is asking for the name of a transcript file, by looking at the prompt string. If it sees the word "script" or "transcript" the prompt string, the interpreter now assumes that it's asking for a transcript file, and sets the appropriate default file type in the dialog.
- The interpreter core engine is now more careful at game termination to release allocated memory. This generally won't make any difference to players, as the operating system usually frees this memory anyway at program exit, but it should improve stability in interpreters that can run multiple games in series.
- In the past, it was possible for a game to crash the interpreter by returning a fairly long (around 200-character) message from parseError or parseErrorParam. This has been corrected. (bugdb.tads.org #0000100)
- Some internal changes in the code address problems related to memory management on 64-bit processors. (bugdb.tads.org #0000084)

### Version 2.5.14

Released on May 5, 2009

- Objects created with 'new' during the preinit phase are now marked as ordinary objects in the .gam file, as though they'd been defined statically in the source code. The same thing applies to the vocabulary words they use. This change means that objects created dynamically during preinit are effectively permanent: they persist through RESTART, RESTORE, UNDO, and the like, and they can't be deleted with the 'delete' operator. (bugdb.tads.org #0000055)

### Version 2.5.13

Released on April 28, 2009

- The new systemInfo() codes __SYSINFO_AUDIO_FADE and __SYSINFO_AUDIO_CROSSFADE let you determine if the interpreter supports audio fades and audio cross-fades, respectively. (Audio fades are accessed through the HTML TADS <SOUND> tag.) These codes return nil or 0 if fades/cross-fades aren't supported at all, or an integer giving a combination of bit-flags indicating which audio formats support fades/cross-fades. The bit flags are __SYSINFO_AUDIOFADE_WAV, __SYSINFO_AUDIOFADE_MPEG, __SYSINFO_AUDIOFADE_OGG, and __SYSINFO_AUDIOFADE_MIDI (for WAV, MP3, Ogg Vorbis, and MIDI files, respectively). For example, if systemInfo(__SYSINFO_AUDIO_FADE) returns (__SYSINFO_AUDIOFADE_WAV | __SYSINFO_AUDIOFADE_OGG), it means that WAV and Ogg Vorbis files support fades, but other audio formats don't.
- A bug in the compiler sometimes made the debugger show the wrong source location when single-stepping within a single-line expression property. (That is, a property with an executable expression, but no curly braces, with the whole definition on a single line of text.) This bug was mostly cosmetic, but in rare cases triggered a separate bug in the debugger that could crash the debugger. When a single-line expression property was immediately followed by a single-line method (with braces), and execution stopped in the method due to single-stepping or due to a run-time error, the debugger was unable to show the execution location, and sometimes even crashed. Both of these bugs are now fixed.

### Version 2.5.12

Released on September 28, 2008

- The cvtnum() built-in function returned the wrong result for negative values on occasion. The problem was dependent on chance arrangements of the run-time string/list heap, so it occurred essentially at random. This has been corrected - cvtnum() now correctly stops at the end of the string. (Workaround for older versions: append a non-digit character to the cvtnum() argument - e.g., `cvtnum(str + ';')`. The problem was that cvtnum() didn't properly detect the end of the input string, so it sometimes scanned into random bytes in memory following the string if those bytes happened to look like ASCII digits. However, the function did always correctly stop upon reaching any non-digit character, so explicitly appending a non-digit was enough to make the function stop scanning, ensuring that it wouldn't stray past the end of the string. This workaround isn't necessary with 2.5.12 and later, since the underlying problem in cvtnum() is now fixed, but you can use the workaround if you want to ensure compatibility with older interpreter versions that some players might still be using.)
- In the past, run-time error messages (e.g., "[TADS-1010: object value required]") were omitted from the transcript. This was particularly inconvenient for beta testing, since any errors encountered by testers were invisible in their session logs. These messages are now included in the transcript. (bugdb.tads.org #0000032)

### Version 2.5.11

Released on August 9, 2008

- The parser now leaves the antecedent of "it" intact when a command involves a number or string. That is, "it" will simply refer to the noun phrase from the second-most-recent command when the most recent command involves a number or string as its noun phrase: "x box; note 5; x it" will now treat the "it" in the last command as referring to the box. In the past, the parser simply forgot any antecedent in this type of situation. That was intentional, to avoid any confusion over whether "it" should refer to the number or string, but in practice players don't seem to find this confusing at all and expect "it" to continue referring to the last real object. (bugdb.tads.org #0000014)
- The parser showed a somewhat confusing parser error message for player commands of the form "verb x OF y" in some cases. If x and y were valid nouns or adjectives, but the combination "x OF y" didn't match any object's vocabulary (e.g., there's a BOX and some PAPER, but there's no such object as BOX OF PAPER), the parser treated this command as though it were a two-object verb, such as PUT IN or LOCK WITH - that is, the OF was treated as a verb-phrase preposition, and x and y were treated as (respectively) the direct and indirect objects. This often resulted in the parser error "I don't recognize that sentence," since OF is a rare preposition in verb phrases. For example, EXAMINE BOX OF PAPER would be treated as an EXAMINE OF command, which most games don't defined as a valid verb phrase. In such cases, the parser now checks to see if it could have formed a syntactically valid noun phrase from the whole "x OF y" phrase, and if so, shows the more sensible error "I don't see any x of y here." (bugdb.tads.org #0000010)
- If the game executed parserReplaceCommand() from within certain of the "parser hook" functions, the interpreter displayed the run-time error "TADS-1024: 'abort' statement executed". This happened because of the way parserReplaceCommand() works internally, but it obviously shouldn't result in an error message. This is now fixed. (bugdb.tads.org #0000011)
- The compiler's -p option (to make the compiler pause before terminating, to give you a chance to read the output) didn't work in some cases where an error occurred during compilation. This has been fixed. (bugdb.tads.org #0000015)

### Version 2.5.10

Released on August 17, 2006

- The INVENTORY TALL command now respects "quiet" containers and surfaces (that is, the contents of these objects will not be listed in the inventory). In the past, the default INVENTORY WIDE mode respected the "quiet" flags, but TALL mode didn't. INVENTORY TALL also now properly takes into account the "contentsVisible" settings of the objects listed. These changes ensure that WIDE and TALL modes yield the same information.
- In adv.t, the default message for TELL thing ABOUT topic now properly takes into account the number (singular or plural) of the thing, so the message will now change to "...are interested" when the thing has a plural name. In the past, the text always said "is interested," which was ungrammatical when the thing had a plural name.
- A bug in the interpreter caused unpredictable behavior, sometimes crashing the interpreter, when starting certain games, including recent release Finding Martin. The problem was triggered by a very large number of objects (more than about 150) in a single location, so it didn't affect most games, and it appeared somewhat sporadically even for games that did trigger it. The bug has now been fixed.
- An interpreter bug caused delword() to occasionally and unpredictably delete extra words beyond what it was actually asked to delete. This has been corrected.
- In the past, if the player's answer to a disambiguation question had certain kinds of syntax errors, the parser responded with a suitable error message, but incorrectly showed that message twice in a row. This was due to an interpreter bug, which has now been corrected.
- In adv.t, the messages for "the key doesn't fit" in doorway.doLockWith and doorway.doUnlockWith were phrased incorrectly in some cases (in particular, the subject and verb didn't always agree). This has been corrected.

### Version 2.5.9

Released on September 12, 2004

- The "file safety" levels have changed slightly to provide better security against malicious game programs. First, level 2, which formerly provided global read access but no write access, now instead provides read/write access to the current working directory only (the game isn't allowed any access to files outside of the working directory). Second, the default safety level is now level 2. In the past, level 1 (read from any file/write to current directory only) was the default, which left open the possibility that a game could access data outside of the working directory. A malicious game could conceivably have exploited this by, for example, secretly copying sensitive data to the game directory for later use by a coordinating network worm, or by encoding the data in a hyperlink (displayed in an HTML-enabled game) that takes the player to a spyware site when clicked. The new default setting should effectively eliminate these risks by barring access to any files outside of the working directory.
- A bug in the compiler sometimes caused crashes when the "preinit" function called certain built-in functions, particularly for file manipulation. The problem was somewhat random, and tended to show up on some machines and not others, because it depended on memory conditions left over from other programs. The problem showed up for several people when they included the "gameinfo.t" module in their games, because that module writes a text file during preinit. The bug has now been fixed.
- An interpreter bug was capable of corrupting list values during game execution under very rare conditions. The bug only showed up when assigning a value to an indexed element of a list (either directly or via the "+=" or "-=" operators) at just the right moment to trigger garbage collection, and only then when the list happened to be situated in memory in just the right way. The bug was more likely to occur with small "heap" memory settings (the "-mh" option); with the large default heap of most current interpreters, the bug almost never showed up. The bug was also quite unlikely to occur except in games that perform unusually heavy list processing. The bug manifested itself in unpredictable ways, ranging from spurious run-time errors to interpreter crashes. This has now been fixed. Note that most games that were likely to trigger the bug in the past will now instead cause a "heap overflow" error; this error is valid, and indicates that the game exhausted the available memory for processing list and string values. If a heap overflow does occur while running a game, try increasing the heap size using the "-mh" command-line option when you run the game.
- An inconsistency in the command parser has been fixed. In the past, the parser didn't properly take into account whether responses to missing object prompts were singular or plural. This caused some subtle differences between typing a full command and piecing a command together with missing object prompts. In particular, the parser didn't announce the object names for a plural response, and didn't check rejectMultiDobj. This has been fixed, so the parser's behavior is now consistent for missing object responses.
- In the past, the parser incorrectly treated THEM as plural even when it referred to only one object, so it checked with rejectMultiDobj in these cases. This is undesirable in some cases, especially when the single object being referred to is an object whose name has plural usage. The parser now treats THEM as singular when it refers to just one object.

### Version 2.5.8

Released on June 12, 2004

- In adv.t, THROW AT now uses a new method, throwHitDest, to determine where the thrown object lands. This method is called for the actor's current location; by default, it simply returns 'self', so a thrown object simply lands in the actor's current location. This is the same behavior as in the past. However, the special 'theFloor' object overrides this to return the current room. This corrects a problem that occurred when the player sat on the floor and then threw something: because the object formerly ended up in the actor's location, the object landed in the 'theFloor' object, at which point the object became inaccessible. This change ensures that a thrown object will never be moved into 'theFloor', but will always land in the actor's actual room location. As a side benefit, the new method also makes it possible to customize the location where objects land when thrown from within a nested room.
- When running in HTML mode, the default parser error message 1 ("I don't understand the punctuation %c") now includes an "\H-" sequence before the message to ensure that the unrecognized character is displayed literally if it's an HTML markup-significant character (such as '<' or '&'). It also shows an "\H+" sequence after the message to restore HTML mode. In the past, if the player entered a markup-significant character while the interpreter was in HTML mode, the HTML parser could become momentarily confused, leading to strange output.
- The new parser hook parseDefaultExt() extends the parseDefault() function. The new function takes two additional parameters, giving the actor and deepverb object involved in the command; the new parameter list is parseDefaultExt(actor, verb, obj, prp). If the game defines parseDefaultExt(), then the interpreter ignores parseDefault() and calls parseDefaultExt() instead. If the game does not define parseDefaultExt(), but does define parseDefault(), the interpreter uses parseDefault() as it used to, which ensures that existing games will continue to run unchanged. You can use the new extended version of the function if you need the actor and/or verb information in displaying the default object message; this is useful in some non-English languages where a default object name's noun phrase must be inflected according to its usage in the command.
- The new parser hook preparseExt() allows the game to inspect and optionally change text the player types in response to parser questions: disambiguation queries, requests for missing direct or indirect objects, and OOPS typo-correction opportunities. The new function's parameter list is preparseExt(actor, verb, str, typ), where 'actor' is the actor performing the command, 'verb' is the deepverb object, 'str' is a string containing the exact text the player typed in, and 'typ' is an integer giving the type of query the player is responding to. The meaning of 'typ' is the same as the that of the type codes passed to commandPrompt() and commandAfterRead(). The function can return a new string, which the parser will use to replace the text the user typed; true, to proceed with the original text the user typed; or nil, to treat the user's text as a brand new command, bypassing the special interpretation as a response to the parser's question. The game doesn't have to define the preparseExt() function; it's purely optional, so existing games will not be affected.
- When the interpreter looks for external resource bundles (*.rsN files), it now looks for the files using lower-case and upper-case suffixes (in other words, it looks for both *.rs0 and *.RS0, and likewise for digits 1-9). Lower-case suffixes are tried first, then upper-case. This change makes the naming system more flexible on systems with case-sensitive file systems, such as Unix; for systems such as Windows and Macintosh that have case-insensitive file systems, this change will have no effect.
- A bug in the parser made it impossible to use numeric adjectives in certain phrasings. In particular, if a given word was defined in a game as both a noun and an adjective, but the word was defined only as a noun for a given object that takes a numeric adjective, then the phrasing noun number didn't match the object. For example, if the word "switch" was defined as a noun for a given numbered switch, but "switch" was defined as an adjective for some other object, then "switch 5" didn't match the switch even if "5 switch" did. This has been corrected.
- The compiler now checks for missing symbols before running the 'preinit' function, and skips running preinit if any symbols are undefined. In the past, the compiler called preinit even if some symbols were undefined; if preinit itself called any undefined functions or invoked methods of undefined objects, this sometimes led to misleading error messages. The compiler will now simply point out the missing symbols, and skip running preinit entirely.
- The compiler now allows filenames up to 255 characters to appear in the source file names in debugging records. In the past, the limit was 127 characters. Important note: due to a bug, older interpreters cannot properly handle .gam files compiled with debugging information with path names over 127 characters. Attempting to load .gam files with debugging information containing long source file names will cause unpredictable results. Since some users might still have older interpreter versions, we strongly advise against releasing games compiled with debugging information. That is, you should never release a game that you compiled with the "-ds" or "-ds2" options.
- In the past, if the game code called a function or method, using the return value of another function or method as an argument, and the other function or method didn't actually return any value for the argument, the results were unpredictable; in some cases, this even caused the interpreter to crash. The interpreter is now more careful about checking for these cases; when they occur, the interpreter now automatically adds 'nil' values for the missing arguments. This should ensure that errant code won't crash the interpreter, without breaking any existing code with "asymptomatic" instances of this error.
- The default "file safety level" for the command-line interpreters is now set to 1, which allows reading files from anywhere on the disk, but allows writing files only to the current working directory. This protects against malicious game authors attempting, for example, to modify your operating system's files. You can manually override this to use a more permissive or more protective level using the "-s" option.
- External functions are now obsolete and can no longer be used on any platform. One of the nice things about running games with a system like TADS is that you don't have to worry as much about viruses and other malicious code when playing downloaded games, since games run in the interpreter environment rather than as native code. The external function feature made it possible for games to call out to native code, though, so a malicious author could have used external functions to do damage to a user's system. Removing this feature eliminates this security loophole. External functions were only supported on a few platforms to start with, and they never caught on with authors, probably because they were difficult to use and had serious drawbacks (such as complete unportability). Since no one has ever had much use for this feature, and because of the potential security vulnerability, we've decided that there's no reason to keep it around.
- In the past, the compiler did not accept 'self' as an element in a list, even in contexts where 'self' was otherwise valid. The compiler now does accept 'self' as a list element in method contexts.
- A bug in the interpreter sometimes made the result of the subtract-and-assign operator ("-=") incorrect under certain circumstances: specifically, the problem occurred when the left-hand side was a local variable, the local variable contained a list value, and the list value did not contain the value on the right-hand side of the operator. In some such cases, a run-time error occurred; in some others, the wrong value was assigned to the local variable. This has been corrected; when the value on the right side is not in the list, the list value in the local is simply left unchanged.
- Due to a bug, the compiler sometimes crashed if you used a "break" and "continue" statement in an invalid context (i.e., outside of a "while", "for", "do", or, in the case of "break", a "switch" statement). This has been corrected; the compiler now simply flags these as errors, but shouldn't crash.

### Version 2.5.7

Released on September 22, 2002

- The new system information code __SYSINFO_INTERP_CLASS returns information on the "class" of interpreter currently running. This returns one of the following codes:
  - __SYSINFO_IC_TEXT: a text-only, character-mode interpreter, such as an MS-DOS or Unix interpreter. These interpreters use a single, fixed-pitch font, and support the text-only HTML subset.
  - __SYSINFO_IC_TEXTGUI: a text-only interpreter on a graphical platform, such as MacTADS or WinTADS. These interpreters behave essentially identically to character-mode text-only interpreters, so from the game program's perspective there is no practical difference between this and character-mode interpreters.
  - __SYSINFO_IC_HTML: a full HTML-enabled interpreter on a graphical platform, such as HTML TADS on Windows or HyperTADS on Macintosh. These interpreters can display text and graphics, use multiple fonts, and interpret the full HTML TADS markup language.
- The travel system in adv.t has been enhanced slightly to allow rooms to differentiate their travel behavior for travel by the player character vs. travel by non-player characters. The new properties actorNorth, actorSouth, actorEast, actorWest, actorNE, actorNW, actorSE, actorSW, actorUp, actorDown, actorIn, and actorOut are now called when an NPC attempts travel; these properties correspond to the traditional properties north, south, east, west, and so on, and the only difference is that the new properties are called when NPC's travel. The traditional properties are still called for all player character travel. By default, each of the new actorXxx properties simply returns the value of the corresponding traditional property: so actorNorth by default simply returns the value of (self.north). This means that if you don't want to differentiate NPC travel from PC travel, you need do nothing at all; but if you want a particular travel direction from a particular room to work differently for an NPC than it does for the player character, you simply need to override actorNorth (or whichever direction you want to customize) in that location. Thanks to Tommy Nordgren for suggesting this enhancement.

### Version 2.5.6

Released on June 1, 2002

- The new system information code __SYSINFO_TEXT_COLORS tests for text color support. Currently, only the HTML interpreters support text colors; the text-only interpreters have no text color support. The return value indicates the level of support available:
  - 0/nil - no text color support
  - 1 - parameterized color names only (TEXT, STATUSTEXT, etc.)
  - 2 - the eight ANSI colors (white, black, red, blue, green, yellow, magenta, cyan) are supported for the text foreground color, but the background color cannot be set
  - 3 - the eight ANSI colors are supported for text foreground and background colors
  - 4 - full RGB colors are supported, foreground and background (the HTML interpreters generally return this code)
- The new system information code __SYSINFO_TEXT_HILITE tests for text highlighting support. This returns 1 if the interpreter is capable of rendering highlighted text (i.e., text within "\( \)" sequences, or text in HTML <B> sequences) with a distinctive appearance, 0 or nil if not. Most interpreters are capable of showing highlighting, except when running in "plain" mode or when using a terminal or display device that has no ability to show different text colors or styles.
- The new system information code __SYSINFO_OGG tests for Ogg Vorbis audio format support. systemInfo(__SYSINFO_OGG) returns 1 if the Ogg Vorbis format is supported by the interpreter, 0 if not.
- The new system information codes __SYSINFO_MNG, __SYSINFO_MNG_TRANS, and __SYSINFO_MNG_ALPHA test for basic MNG (animated image) suport, MNG transparency support, and MNG alpha-channel support, respectively. These are analogous in meaning to the PNG information codes.
- The interpreter now includes the name of the external resource (.rsn) file in error messages that result from errors reading these files. In the past, the interpreter did not indicate which specific file was causing the problem, which made it difficult to track down where the problem was. (The non-specific error message worked fine in the days before external resource files, when everything was always in the single .gam file, but the new, more specific diagnostic information is important now that a game's resources can be put in several separate files.)
- In adv.t, `basicMe.moveInto(room)` now calls a new method, `room.meIsMovingInto(self)`, whenever the `basicMe` object being moved is the current player character (i.e., `self` is the object that `parserGetMe()` returns). A default implementation of `meIsMovingInto(meActor)` has been added to `room`; this default implementation does nothing. Finally, an overriding implementation of `meIsMovingInto(meActor)` has been added to `theFloor`; this implementation sets `theFloor.sitloc` to the player character actor's previous location. The purpose of all of this new mechanism is to ensure that `theFloor.sitloc` is properly set to the player character's prior location whenever the player character is explicitly moved into `theFloor`. In the past, `theFloor.sitloc` was set whenever the player character moved to `theFloor` via a "sit on the floor" command from the player, but wasn't set when the object was moved to `theFloor` through other means. The `theFloor` object uses the `sitloc` property to remember the location that originally contained the actor before sitting on the floor; this is needed because `theFloor` is a floating item and thus doesn't have an actual location of its own.
- In adv.t, the various `hider` subclasses (`underHider`, `searchHider`, `behindHider`) now check to see if they were never hiding anything to start with, and issue an appropriate message if so. In the past, if an object was defined using one of the `hider` subclasses, but no other object was ever hidden inside the `hider`, looking in/under/behind the object generated an erroneous message, of the form "You find , which you take." Now, when the list of hidden objects is initially empty, the generated message is simply "You find nothing under (the object)."
- The `parserDictLookup()` function can now be called from within `init()`. In the past, this function caused a run-time error if called while `init()` was running; this has been corrected.
- Fixed a problem in parserGetObj(PO_ACTOR) that caused the incorrect actor to be reported between the time `preinit` was called and the time `preCommand` was called. During this window, if a command was the first command on a new command line, and the command line didn't start with a target actor ("bob, ..."), then parserGetObj(PO_ACTOR) incorrectly returned the actor from the previous command. This no longer occurs; the function now correctly returns the current "me" object in such cases.
- Fixed a problem in the output formatter that caused problems with displaying a series of quoted spaces ('\ ' sequences) in some situations, especially on platforms with proportionally-spaced display fonts such as the Mac. This has been corrected.
- The compiler no longer treats any characters in the "extended" character set (i.e., characters with character codes outside the ASCII range 0 to 127) as whitespace. In the past, the compiler on some platforms treated certain extended characters as spaces, which sometimes caused problems for authors working in non-English languages. The compiler has no knowledge of the attributes of any localized character sets (the compiler's character set translation mechanism does not provide attribute information, only set-to-set mapping information), so it was incorrect of the compiler to assume anything about which extended characters represented whitespace characters. This problem manifested itself most apparently when a string containing extended characters spanned several lines of source; if the compiler mistook any of the leading characters on continuation lines for whitespace, it would remove them, resulting in incorrect text displayed at run-time. This has been corrected; the compiler now treats all extended characters as non-whitespace characters.
- Fixed a problem in the `execCommand()` built-in function that caused various problems ranging from data value corruptions to interpreter crashes. The problem appeared when `execCommand` was used to execute a command that terminated with an `exit` or `abort`, and then only when certain combinations of operations on local variables followed. The problem has been corrected.
- Fixed a compiler bug that caused an infinite loop when parsing certain nested `switch` statements. A `switch` containing string constant `case` labels nested within another `switch` with string constant `case` labels sporadically caused this problem. This has been corrected.
- Fixed a problem in the compiler that caused the `contents` lists of objects to be initialized improperly in certain cases. In particular, if an object's `contents` list was used during execution of the `preinit` function, any object that inherited a `location` value equal to the given object was omitted from the given object's `contents` list, when clearly the object should have been included in the list. This only affected `preinit`, and then only when a `contents` that should have contained an object with an inherited location was referenced from within the `preinit` function. This has been corrected; inherited `location` values are now properly handled when `preinit` is executing.
- The compiler and interpreter must sometimes create temporary files for internal memory management purposes. On certain systems (in particular, DOS, Windows, and Unix), TADS determines a suitable directory location to store any needed temporary files by looking in the environment for variables called TEMPDIR, TMPDIR, TEMP, and TMP (in that order), and using the value of the first such variable it finds as the temporary directory path. In past versions, if the path found in this manner was invalid, the interpreter or compiler would usually terminate with an error message that wasn't very helpful in tracking down the problem. In most cases, the error message was "unable to open swap file." This has been changed; now, if the temporary path obtained from the environment variables is invalid, TADS ignores the path and simply creates any necessary temporary files in the current working directory.
- A number of messages in adv.t now use format strings ("%you%" and the like) where they didn't before but should have, to facilitate games written in styles other than second-person. Thanks go to Bennett Standeven for catching these.
- Fixed a problem that caused interpreter crashes under rare circumstances for interpreters that could play more than one game in a single session (such as the HTML TADS interpreter). If the interpreter loaded a game that used the "output filter function" feature, and then that game was terminated and another game was loaded into the same interpreter session, and the new game didn't use its own output filter function, results were unpredictable, and interpreter crashes sometimes occurred. This has been corrected. (This problem did not affect most platforms, because most interpreters terminate when the game being executed terminates.)
- Fixed an interpreter problem that caused the "`new`" operator to generate a run-time error in some unusual situations. In particular, if the constructor modified a property of the object containing the method that invoked the `new` operator (directly in its own code, or in any code it invoked), then the error "TADS-5: attempting to reallocate a locked object" appeared sporadically. This has been corrected.

### Version 2.5.5

Released on September 27, 2000

- In adv.t, a comment in listcontgen() has been fixed to reflect that it's actually part of the "tall" listing rather than the "wide" listing.
- In adv.t, in moveableActor.travelTo, if the actor's previous location was nil, a run-time error occurred. This has been corrected.
- In adv.t, in thing.thatdesc, the plural form has been changed to display "those" rather than "them"; this provides a better parallel to the singular display ("that").

### Version 2.5.4

Released on June 17, 2000

#### New systemInfo() flags for PNG transparency

Two new systemInfo() flags have been added to test for support of certain features of the PNG image format:

- __SYSINFO_PNG_TRANS: tests for PNG transparency support. Some newer versions of the HTML interpreter support PNG transparency, which allows parts of a PNG image to be designated as transparent so that the background shows through. systemInfo(__SYSINFO_PNG_TRANS) returns true if transparency is supported when displaying PNG images, nil if not. This will never return true under interpreters that don't support PNG images at all. Note that this flag indicates only simple transparency support, which allows pixels to be designated as fully opaque or fully transparent, and does not imply support of full alpha blending; some interpreters (such as the Windows HTML interpreter 2.5.4) support only simple transparency but not alpha blending.
- __SYSINFO_PNG_ALPHA: tests for PNG "alpha blending" support, which allows individual pixels of a PNG image to be designated as partially transparent. Currently, none of the interpreters support alpha blending in PNG images; this flag has been added for possible future use.

#### Bug Fixes

- A bug in the compiler caused a crash under certain unusual conditions when the program defined a symbol as an object, and the symbol was already defined as a property or function. Most of the time, the compiler reported an error ("redefining symbol as object"), but in some cases the compiler crashed. This has been corrected.

### Version 2.5.3

Released on April 16, 2000

This version contains no changes to text-only versions of TADS; the only changes are in HTML versions.

### Version 2.5.2

Released on March 27, 2000

#### Index of Changes

- Parser recognizes isThem
- New scoreFormat() Function
- New systemInfo() Capability Codes
- Bugs Fixed

#### Parser recognizes isThem

The parser now recognizes the `isThem` property for the purposes of deciding on the pronoun to display when prompting for an indirect object. In the past, the parser only recognized the `isHim` and `isHer` properties, and used the pronoun "it" if neither of these properties were consistently defined for the objects matching the direct object. The parser now uses the pronoun "them" if the matching objects all have `isThem` set to `true`.

For example:

       >throw pants
       What do you want to throw them at?

#### New `scoreFormat()` Function

The way that adv.t displays the status line has been changed slightly to simplify coding of special formats. A new function, `scoreFormat()`, is now responsible for formatting the string to display in the right half of the status line. `scoreFormat()` takes the current score and the current turn counter as arguments, and returns a string to display in the right half of the status line. The default implementation in adv.t simply returns a string consisting of the score, a slash ("/"), and the turn count, which provides the same format that adv.t has traditionally used for the status line.

The existing function `scoreStatus()` now calls `scoreFormat()` to generate the display. In addition, the code in `room.statusLine` that generates the HTML version of the status line now calls `scoreFormat()` as well. This makes `scoreFormat()` the single point in adv.t where the right half of the status line is formatted.

The advantage of this new mechanism is that you can customize the display of the right half of the status line for both text-only and HTML-enabled games simply by replacing the `scoreFormat()` function.

Of course, HTML-enabled games are not limited to using the traditional status line display; if you want a completely custom status line display when running under an HTML-enabled interpreter, you can replace adv.t's `room.statusLine` and generate your own <BANNER> display. The new `scoreFormat()` mechanism, however, is convenient when you simply want to customize the right half of the status line, but still use the traditional single-line status format.

#### New `systemInfo()` Capability Codes

The `systemInfo()` function accepts several new capability codes that let you discover whether the system is capable of following URL-style <A HREF> hypertext links in HTML-formatted text. The new codes are:

- `__SYSINFO_LINKS_HTTP` - checks if the system can follow HTTP links to display web pages. HTTP URL's start with "`http:`".
- `__SYSINFO_LINKS_FTP` - determines if the system can follow FTP links, which start with "`ftp:`".
- `__SYSINFO_LINKS_NEWS` - determines if the system can follow news links to Usenet newsgroups, which start with "`news:`".
- `__SYSINFO_LINKS_MAILTO` - determines if the system can follow mail links to send email; these links start with "`mailto:`".
- `__SYSINFO_LINKS_TELNET` - checks if the system can follow telnet links, which start with "`telnet:`".

For example, suppose you want to display a link to a web page you've created for your game, so that players can check for updates and hints on your web site. You can use `__SYSINFO_LINKS_HTTP` to check the user's TADS interpreter for HTTP link capability; if the interpreter can follow HTTP links, you can show your link with a friendly display, and fall back on spelling out the URL when the interpreter can't directly follow the link.

       "You can check for updates to this game at ";
       if (systemInfo(__SYSINFO_LINKS_HTTP))
           "<A HREF='http://www.mysite.com/mygame.htm'>my web site</A>";
       else
           "my web site (http://www.mysite.com/mygame.htm)"
       ". This site also has hints and some background
       information about the game. ";

#### Bugs Fixed

- For a verb defined with the `[disambigDobjFirst]` flag, the parser called the indirect object's `verIoVerb` method incorrectly in cases where the player omitted an indirect object, causing the parser to try to find a default indirect object. For example, suppose you were to make the following definitions:

        waveVerb: deepverb
             verb='wave'
             sdesc="wave"
             prepDefault = atPrep
             ioAction(atPrep) = [disambigDobjFirst] 'WaveAt'
        ;
        modify thing
             verDoWaveAt(actor) = {}
             verIoWaveAt(actor, dobj) = {}
             ioWaveAt(actor, dobj) = { "Nothing special happens. "; }
        ;

  In the past, if you typed a "wave" command with no indirect object, as in "wave flag," the parser incorrectly invoked the `verIoWaveAt` method with only one argument, causing a run-time error. This no longer occurs; the parser consistently calls the `verIoVerb` method in these cases with the correct number of arguments.

- In past versions, the `parserTokenize()` built-in function did not work properly when the input text contained a quoted string; in particular, the text of the quoted string was not included in the list. This has been corrected; the tokenized list now correctly includes the quoted string, enclosed in double quote marks (following the same rules as `preparseCmd()`). The same problem affected the token list passed to `parseUnknownVerb()`; this has been corrected as well.
- If the `preparse()` or `preparseCmd()` functions executed an `exit`, `exitobj`, or `abort`, the interpreter displayed an error message (such as "'abort' statement executed"). This was mostly harmless; these types of statements are generally not needed within `preparse()` and `preparseCmd()`, because these functions use return codes rather than `exit` and the like to control further processing. However, certain built-in functions perform `abort` operations implicitly; in particular, the `parserReplaceCommand()` function uses `abort` to start processing the new command immediately. To enable the use of such built-ins in `preparse()` and `preparseCmd()`, the interpreter no longer displays an error message when `exit`, `exitobj`, or `abort` are used in these user functions.
- In the past, the `parserResolveObjects()` built-in function did not correctly handle unknown words; in particular, the function did not display any message if the noun phrase contained an unknown word, even when the "silent" parameter was nil. This has been corrected. Now, when "silent" is nil, the function handles unknown words by displaying the normal error message ("I don't know the word..."), then prompting the player for a new command. If the player responds with "oops" (or "o") followed by another word, the function returns the new result code `PRSERR_OOPS_UPDATE`, with the corrected version of the noun phrase string as the second element. Refer to the Parser Manual for details.
- A problematic interaction between `parseUnknownVerb()`, and `parserReplaceCommand()` has been corrected. In the past, if `parserReplaceCommand()` was used within the `parseUnknownVerb()` function, and the parser called `parseUnknownVerb()` because of an unknown word in the command, the parser incorrectly deferred processing the replacement command until after checking to see if the player tried to use OOPS to correct the unknown word. This is undesirable, because in this case the game has already provided the replacement command, so there is no reason to give the player a chance to use OOPS. This has been corrected.
- Due to a bug, the parser did not correctly set the `PRSFLG_ENDADJ` flag for some objects when calling the `disambigDobj` and `disambigIobj` methods. The parser omitted the flag when a noun phrase ended in an adjective, and the vocabulary word involved was only defined as an adjective (in other words, the word was never defined as a noun or plural for any object in the entire game). This has been corrected. (Thanks to Amir Karger for helping to track down this problem.)
- A parser bug caused a strange response message in certain cases. If the player answered a disambiguation question with a special word (such as "it" or "her"), the parser displayed a `preparse`-style special word code in the response, as in "I don't see any R book here." This has been corrected; the parser now displays an improved message ("I don't see any such book here").
- A bug in the compiler caused a crash under certain obscure circumstances. If a symbol was defined as a property or an object, and then later in the source code a function definition appeared using the same symbol name, the compiler sometimes crashed. This has been corrected.
- A compiler bug caused the incorrect method to be called at run-time in a certain situation. If code in an object used `pass` or `inherited` to inherit code in a base class, and the base class was an object that had been modified with the `modify` keyword, and the method being inherited had been replaced with the `replace` keyword within the modifying class, the system did not correctly call the replaced version of the method. This has been corrected.
- In past versions, the compiler accepted a `case` label in a `switch` statement with `self` as the value. Since `self` is not a constant, this type of `case` label has never been legal, and at run-time the code after the "`case self:`" label was never executed; however, the compiler should not have accepted the construct in the first place. The compiler now displays an error when it encounters a "`case self:`" label in a `switch` statement.
- In the `basicMe` class in adv.t, the `ioGiveTo` method generated an incorrect message. This has been corrected.
- The inventory listing code in adv.t (in the `iVerb` object) produced an incorrect display ("You are carrying.") when the player character was carrying only items marked as unlistable (`isListed = nil`). This has been corrected.
- The `thing` class in adv.t now has default verification methods for the verbs "attach x to y," "detach x," and "detach x from y." These default verification methods each simply display the message "There's no obvious way to do that."
- In the past, the text-only interpreter did not correctly suppress certain markup sequences (in particular, BR, TAB, and HR) that appeared within <TITLE> and <ABOUTBOX> tags. All text, including these markups, is now properly suppressed within TITLE and ABOUTBOX sequences.

### Version 2.5.1

Released on September 21, 1999

#### Index of Changes

- New Parser Hook: parseAskobjIndirect()
- New Parser Hook: prefixdesc
- New built-in function: resourceExists()
- New defined() Codes
- New parserGetObj() Codes
- Filename-to-URL Conversions in tadsrsc
- cantReach with Multiple Objects
- Changes to inputkey()
- Changes to adv.t
- Bugs Fixed

#### New Parser Hook: `parseAskobjIndirect`

A new parser hook, named `parseAskobjIndirect()`, has been added to provide more control over the message that the parser uses to ask the player to supply an indirect object when none is provided in the command but the verb requires one. This new function supplements the existing `parseAskobj()` and `parseAskobjActor()` functions.

Refer to the TADS Parser Manual (version 2.5.1) for details on this new parser hook.

#### New Parser Hook: `prefixdesc`

The parser now calls a new method, called `prefixdesc`, to display the object name prefix that comes before the execution results for each object in a command with multiple direct objects. This new method effectively replaces the existing `multisdesc` mechanism. Refer to the TADS Parser Manual (version 2.5.1) for details on this new parser hook.

#### New built-in function: `resourceExists()`

A new built-in function, `resourceExists()`, allows you to determine whether a named resource (such as a JPEG image or an MP3 audio file) can be loaded. This function takes as its single argument a string giving the name of a resource to find; the function returns true if the resource can be loaded, nil if not. The function returns nil if the interpreter is simply not capable of loading resources at all (the text-only interpreters thus always return nil for this function), or if the resource can't be found. The function returns true only if the interpreter is capable of loading resources at all, and the resource is available.

If you're writing a multi-media game for the HTML interpreters, but you also want your game to work on text-only systems, you can use this function for finer control over the presentation on the text systems; you might, for example, want to provide additional text to make up for missing graphics or sounds. You can also use this function if you're planning to distribute your game in different subset versions in order to provide players with different options for download and install sizes; if you make some of your graphics optional (by bundling some into a separate ".RSn" resource file, for example), you can use `resourceExists()` to detect at run-time which resources the player has chosen to install, and customize the presentation accordingly.

Here's an example that checks to see if a JPEG image is available for display:

        if (!resourceExists('images/title.jpg'))
        {
           // the title graphic isn't available - display a text version
           ...
        }

#### New `defined()` Codes

The `defined()` function, which tests an object to determine whether it defines or inherits a given property, provides a new "flags" argument that lets you get more specific information about the property definition. The new third argument is optional; if provided, it can be one of the following values, defined in adv.t:

`DEFINED_ANY` | This is the default, and has the same effect as omitting the flag argument. The function returns true if the object defines or inherits the property, nil if not.
`DEFINED_DIRECTLY` | The function returns true only if the object directly defines the property. If the object doesn't define the property at all, or merely inherits the definition from a superclass, the function returns nil.
`DEFINED_INHERITS` | The function returns true only if the object inherits the property. If the object doesn't define the property, or defines the property directly rather than inheriting it from a superclass, the function returns nil.
`DEFINED_GET_CLASS` | The function returns the class where the property is defined. If the object directly defines the property, the function returns the object itself. If the object inherits the property from a superclass, the function returns the superclass from which the property is inherited. If the object doesn't define or inherit the property, the function returns nil.

For example, to determine if the object `redBook` directly defines `verDoTake`, you could use this code:

       if (defined(redBook, &verDoTake, DEFINED_DIRECTLY))
          "verDoTake is overridden directly in redBook. ";

#### New `parserGetObj()` Codes

The `parserGetObj()` function now accepts several new code values to get additional objects. These new code values are defined in adv.t:

`PO_IT` | Get the object that the word "it" refers to in player commands. This is nil if there is not "it" object.
`PO_HIM` | Get the object that the word "him" refers to in player commands.
`PO_HER` | Get the object that the word "her" refers to in player commands.
`PO_THEM` | Get the list of objects that the word "them" refers to in player commands. This is an empty list if "them" doesn't refer to anything at the moment.

These new codes are used in the same manner as the original ones. For example, to get the object that the word "him" currently refers to, you'd write this:

       local himObj;

       himObj := parserGetObj(PO_HIM);

#### Filename-to-URL Conversions in `tadsrsc`

The resource bundling tool, `tadsrsc`, in the past did not convert filenames entered with explicit paths to URL notation. This problem only affected individual filenames entered with explicit paths (i.e., files in subdirectories); the tool correctly converted paths to URL notation when adding entire directories to a resource file, and also worked when adding individual files that were in the current directory and thus didn't need an explicit path.

Instead of storing resource names in URL notation, the resource tool stored the actual filenames in local file system notation. This didn't work with HTML TADS, because the HTML resource loader requires image and sound resources to be named using URL notation.

This problem has been corrected; the resource tool now converts all paths used in resource filenames to URL notation.

#### `cantReach` with Multiple Objects

When the player refers to an object that is visible but is not reachable (an item within a closed transparent container, for example), the parser calls the object's `cantReach` to display a message explaining why the object is not accessible. This has not been changed, but the multiple-object prefix mechanism has been.

In the past, when the command had multiple unreachable objects, the parser called the object's `sdesc` method, then displayed message 200 (":"). This has been changed. Now, the parser uses the same mechanism that it does for any other multiple-object listing prefix: for each object, before calling `cantReach`, the parser calls the object's `multisdesc` method then displays message 120 (":").

This minor change is for consistency; in particular, the change allows the game to override multiple-object prefix displays in a uniform manner for all types of these displays.

#### Changes to `inputkey()`

The `inputkey()` built-in function now returns certain keys more consistently and portably. The codes that `inputkey()` returns vary by platform according to what keys are available on the keyboard. In addition, in the past, the function mapped certain keystrokes to high-level functional codes in different ways on different platforms; this corresponded to the differing ways that platforms used certain keyboard keys.

The return codes from `inputkey()` are now somewhat more consistent. The Escape key now returns the string `'[esc]'` on most platforms that provide such a key, and the "control" keys now return `'[ctrl-X]'` for almost all of the control keys on most platforms. In the past, the escape key didn't return anything on most platforms, and control keys frequently were mapped to other functions (for example, on Unix, the Ctrl-E key returned `'[end]'`, since this key is used on Unix TADS interpreters to move the cursor to the end of the line when entering a command).

A few new command keys have been added. The full set of command keys is shown below. The exact assignment of these keys varies by platform, as it has in past versions, and any given platform might support only a subset of these keys.

`[up]` | Up arrow
`[down]` | Down arrow
`[right]` | Right arrow
`[left]` | Left arrow
`[end]` | End of line
`[home]` | Home
`[del-eol]` | Delete to end of line
`[del-line]` | Delete line
`[del]` | Del (delete character)
`[page up]` | Page up
`[page down]` | Page down
`[top]` | Top of file
`[bottom]` | Bottom of file
`[fN]` | Function key N (N is replaced by a number, 1 through 10)
`[tab]` | Tab
`[word-left]` | Word left
`[word-right]` | Word right
`[del-word]` | Delete word
`[bksp]` | Backspace
`[esc]` | Escape
`[ctrl-X]` | Control-X (X is replaced by a lower-case letter: Control-C is `[ctrl-c]`)
`[alt-X]` | Alt-X or Meta-X (X is replaced by a lower-case letter: Alt-F is `[alt-f]`)

#### Changes to adv.t

- Added verifier methods to class `thing` to accomodate the verbs `screwVerb` and `unscrewVerb`. All of these verifier methods simply stop the command with a default failure message (variations on "You see now way to do that"). The new methods are:
  - `verDoScrew`
  - `verDoScrewWith`
  - `verIoScrewWith`
  - `verDoUnscrew`
  - `verDoUnscrewWith`
  - `verIoUnscrewWith`
- The `fopen()` built-in function's `'r+'` mode did not correctly create a new file when the file being opened did not previously exist, as it is documented to do. This has been corrected; if no file exists, the `'r+'` mode creates a new file, otherwise it opens the existing file, keeping its contents intact.
- The `moveInto` method in the `basicMe` object in adv.t now works properly when the object is not the current player character. `moveInto` now uses the default handling inherited from `Actor` when the object is not the current player character; this is important for games that switch among different player character objects.
- The `ioGiveTo` method in `basicMe` in adv.t now uses format strings (such as "%You%"), so that it adapts properly when the object is not the current player character.

#### Bugs Fixed

- Format strings (such as "%You%") can now be used from within the `endCommand()` function. In the past, the actor setting that the output formatter uses to translate format strings was forgotten before the parser called `endCommand()`, even though the parser still had the necessary information internally. The output formatter now keeps track of this information until after `endCommand()` returns, which allows you to use format strings from within `endCommand()`.
- Fixed a parser bug: Executing `exit` within `preCommand()` did not have the documented effect of skipping all of the remaining objects in the command, but simply skipped the first object in the command. `exit` in `preCommand()` now correctly skips the entire object list for the command.
- Fixed a parser bug: An object defined with the same leading substring six characters or longer in multiple adjectives sometimes appeared more than once in a disambiguation query ("Which book do you mean, the yellow book, or the yellow book?"). This same type of problem was fixed in most cases in version 2.4.0, but one type of the problem remained; this has now been fixed.
- Another bug, related to the bug above, was fixed. In some cases, when the player responded to a disambiguation query with multiple objects, the parser would ask the disambiguation question again with every object in the entire game, whether accessible to the player or not, in the list of possible objects. This has been corrected.
- Another parser bug was fixed: If a word referred to more than about 200 objects, the parser displayed a double error message (specifically, "The word 'foo' refers to too many objects.I don't see any foo here."). This has been corrected; only the first message ("too many objects") is displayed now.
- Fixed a bug in adv.t: the `listcontgen()` function, when the "recursive" flag was set, incorrectly tried to evaluate `itemcnt()` with an object rather than a list, causing a run-time error. This has been corrected.
- The parser's handling of situations where an "again" command is impossible due to object deletion has been improved. In the past, if an object involved in the previous command was deleted, and the player typed "again," the parser would respond with parser message 26 ("There's no command to repeat"). The parser now responds with the more suitable message 27 ("You can't repeat that command").
- The parser now properly restores the text of the player's original command words for the direct and indirect object when the player repeats a command with "again." In the past, the parser did not retain the text of the previous command's words, so `objwords()` returned empty lists when the game attempted to retrieve the object words during execution of a repeated command. This inconsistency has been corrected.
- The `parseNounList()` function returned an incorrect word index value in the first element of its return list in two cases: when the noun phrase did not match any objects; and when there was no noun phrase present. In both of these cases, the returned index value had a value one less than the correct value. This has been corrected.
- Fixed a problem that occasionally caused a crash when the player entered a command containing an ampersand ("&") when the game was running in HTML mode.
- Fixed a parser bug that occasionally caused a crash when the player responded to a disambiguation prompt ("which book do you mean...") with "him" or "her."

### Version 2.5.0

Released on July 10, 1999

#### Index of Changes

- preCommand()
- postAction() and endTurn()
- inputdialog()
- inputline()
- New restore() return codes
- Extended askfile() interface
- switchPlayer()
- Inventory Wide and Tall
- nestlistcont()
- listcontgen()
- basicMe.travelTo changes
- movableActor.travelTo changes
- New compiler debug record format (-ds2)
- Parser bug fixes
- Debugger bug fixes
- Compiler bug fixes

#### New pre-execution processing: `preCommand`

At the start of the execution phase, before calling `verbAction()` for the first direct object in the command, the parser invokes the new function `preCommand()`:

      preCommand(actor, verb, dobj_list, prep, iobj);

The "actor", "verb", "prep", and "iobj" parameters are objects giving the actor, verb, preposition, and indirect object in the command, respectively. The "dobj_list" parameter is a list of the direct objects in the command, in the same order as they appear in the command.

This function can use `exit` to skip to fuses and daemons, or it can use `abort` to cancel the command entirely, in which case the parser will skip directly to the `endCommand` function (see below). If this function simply returns, the parser continues processing the command as normal.

#### New end-of-turn processing: `postAction` and `endCommand`

In past versions of TADS, it wasn't possible to add special event processing at the end of a turn. The closest approximation was to put processing in a fuse or daemon, but this approach has several drawbacks: no information is available in a fuse or daemon on the last command executed, and it is not possible to specify the order of execution of fuses and daemons.

Two new parser hooks provide for structured event handling at the end of a turn. The first hook, the `postAction` function, lets you write code that the parser calls immediately after the "action" method, and before any fuses or daemons. The second hook, the `endCommand` function, lets you write code that the parser calls at the end of a turn, just after running all of the fuses and daemons for the turn.

The parser calls `postAction` once for each object in a command with multiple direct objects, just after it calls the "action" method for the object. If the command is terminated early with `exit`, `exitobj`, or `abort`, the parser invoked `postAction` immediately after the `exit`, `exitobj`, or `abort` statement executes. The parser calls `postAction` with the following arguments:

        postAction(actor, verb, dobj, prep, iobj, status);

The first five parameters specify the current command; any of the objects except "actor" and "verb" can be nil. The "status" parameter has the same meaning as the return codes from the `execCommand` built-in function; it can be one of the following values, defined in adv.t:

`EC_SUCCESS` | The command executed successfully, which indicates that everything up through and including the command's "action" method (`verb.action`, `dobj.doAction`, or `iobj.ioAction`, as appropriate).
`EC_EXIT` | An `exit` statement was executed.
`EC_EXITOBJ` | An `exitobj` statement was executed.
`EC_ABORT` | An `exit` statement was executed.

The `postAction` function returns no value.

The parser invokes the `endCommand` after all of the fuses and daemons have finished running at the end of a turn. This function is called once per command, not per object; in a command with multiple direct objects, this function is called only once, just as fuses and daemons are called only once for the entire command.

The parser calls `endCommand` as follows:

        endCommand(actor, verb, dobj_list, prep, iobj, status);

The "status" parameter has the same meaning as the status code parameter to `postAction`. The other parameters have the same values as they did in the call to `preCommand` that the parser makes at the start of the execution phase for the command.

`endCommand` is always invoked at the end of a turn. If an `abort` statement is executed in the course of a turn, the parser skips directly to `endCommand`, because `abort` skips the daemons and fuses. This means that `endCommand` is executed at the end of a turn even when fuses and daemons are skipped.

The `endCommand` function returns no value.

#### New built-in function `inputdialog()`

A new built-in function, `inputdialog()`, lets you ask the player a multiple-choice question. On graphical systems, inputdialog() displays a system dialog box, and lets the user respond by clicking a button. On text systems, this function displays a textual prompt and lets the user respond with the keyboard.

`inputdialog` takes the following parameters:

        inputdialog(icon, prompt, response_list, default_idx, cancel_idx)

The "icon" parameter indicates which icon, if any, to display. The icon will only be displayed on graphical systems; text-only systems ignore this parameter. These icon constants are defined in adv.t:

`INDLG_ICON_NONE` | Do not use any icon.
`INDLG_ICON_WARNING` | Show a "warning" icon. This indicates a potential or minor problem. (On Windows, this displays an exclamation-point icon.)
`INDLG_ICON_INFO` | Show an "information" icon. This indicates to the user that the dialog is being displayed to inform them of the status of an operation. (On Windows, this displays an icon with a small letter "i" in a circle.)
`INDLG_ICON_QUESTION` | Show a "question" icon. This indicates that additional information from the user is required. (On Windows, this displays a question-mark icon.)
`INDLG_ICON_ERROR` | Show an "error" icon. This indicates that a problem has occurred. (On Windows, this displays a stop-sign icon.)

The "prompt" parameter is the message string to display. For graphical systems, this message is displayed in a dialog box; for text systems, it's simply displayed on the terminal.

The "response_list" is a list of strings giving the valid responses. Each entry in the list is a string giving one possible response. On graphical systems, one button is displayed in the dialog for each response string; the response string is the button's label. On text systems, the responses are displayed to the player after the prompt string.

Each string in the response list can optionally include an ampersand character ("&") before the character that serves as a keyboard short-cut for the response. The ampersand is not displayed in the button label or response list displayed to the player. For example, the response list string '&Yes' makes the "Y" key a short-cut for the button, which is labeled "Yes" in the dialog. On some systems the short-cut key will be indicated visually in the dialog; on Windows, for example, the "Y" in the "Yes" button would be underlined to indicate that the letter "Y" is the short-cut for the button. If no ampersand appears in a response list item, the item has no short-cut.

On text-only systems, the keyboard short-cut will be indicated visually by enclosing the short-cut letter in parentheses when dispalying the list of possible responses to the player. If a response item has no short-cut key, the player must enter a sufficiently long leading substring of the response item so that the response is unambiguous with the other valid responses.

Each element of the list can be a number, rather than a string. If an element is a number, it specifies that the button should use a pre-defined standard label. You should use standard labels when possible, because these labels will follow local system conventions and will be localized to the player's language settings; these labels are read from external resources on platforms with appropriate operating system support, so they can be localized easily. To select a standard label, use one of the following values, defined in adv.t:

`INDLG_LBL_OK` | "OK", or local system or language equivalent
`INDLG_LBL_CANCEL` | "Cancel"
`INDLG_LBL_YES` | "Yes"
`INDLG_LBL_NO` | "No"

The strings shown above do not necessarily reflect the actual button text that the player will see, because the actual label will vary by platform and by language. Whatever label is displayed, though, will convey to the user the same meaning.

You can also select an entire standard set of buttons, rather than specifying each button individually. If the response_list parameter is a number, rather than a list, it indicates that a standard set of buttons is to be used, selected from a pre-defined list. The advantage of using one of these pre-defined button sets when possible is that the buttons will automatically follow local system conventions and be localized to the player's language settings, on platforms with appropriate operating system support. To select a pre-defined button set, use one of the following values, defined in adv.t, for the response_list parameter:

`INDLG_OK` | The dialog will display an "OK" button, properly localized.
`INDLG_OKCANCEL` | The dialog will display an "OK" button and a "Cancel" button, properly localized.
`INDLG_YESNO` | The dialog will display an "Yes" button and a "No" button, properly localized.
`INDLG_YESNOCANCEL` | The dialog will display a "Yes" button, a "No" button, and a "Cancel" button, properly localized.

The "default_idx" parameter gives the index in the response list of the default response. If the user presses the "Return" key, or performs any other action appropriate to the system user interface that by local convention accepts the default response to a dialog, this response will be used. The first list entry is at index 1. Pass nil in this parameter if there is no default response, in which case TADS will require the user to select a specific button. (Note that, on some systems, passing nil for this parameter will not make a noticeable difference; on Windows, for example, one of the buttons will always have keyboard focus, so pressing the Return key will always select one of the buttons.)

The "cancel_idx" parameter gives the index in the response list of the cancellation response. Most GUI systems have a standard way of cancelling a dialog; the Escape key has this effect on Windows, for example, as does the Command-Period key combination on the Macintosh. If the user performs the appropriate system-specific action to cancel the dialog, this response is used. The first list entry is at index 1. Pass nil in this parameter if there is no cancel response, in which case TADS will not allow the player to cancel the dialog.

The dialog returns the index of the response that the player selects: 1 for the first response in the response list, 2 for the second entry in the list, and so on. For the standard response lists (`INDLG_YESNO` and so on), the response are in the order described for the constant name: `INDLG_YESNO` has a "Yes" button at index 1 and a "No" button at index 2, for example.

Here's an example of using this function.

        ret := inputdialog('What would you like to do next?',
                           ['&Restore', 'Re&start', '&Quit'],
                           nil, 3);
        switch(ret)
        {
        case 1:
          /* restore a game... */
          break;

        case 2:
          /* restart the game */
          restart();
          break;

        case 3:
          /* quit */
          quit();
          break;
        }

On a graphical system, this would display a dialog with the message text "What would you like to do next?", and three buttons: one with the label "Restore", one with the label "Restart", and one with the label "Quit". If the user presses the "R" key, the "Restore" button would be selected; if the user presses "S", the "Restart" button would be selected; if the user presses "Q", or cancels the dialog (by pressing the Escape key on a Windows machine, for example), the "Quit" button would be selected.

On a text-only system, TADS would display this text on the terminal, on a new line (TADS would output a "\n" sequence to start a new line):

        What would you like to do next? (R)estore/Re(s)tart/(Q)uit >

TADS would then wait for the player to enter a line of text (as with the `input()` built-in function). If the player enters one letter, TADS would check the letter against each response's short-cut, and return the one that matches. If the player enters more than one letter, TADS would check the string against the leading substring of each possible response; if the string matches one of the responses unambiguously, TADS would return that response. If the player enters something invalid or ambiguous, TADS would redisplay the prompt and await another response.

`inputdialog()` has certain limits. The prompt string can be no longer than 256 characters. There can be no more than ten responses, and the total length of the text in all of the responses must not exceed 256 characters. In addition, to ensure portability, you should choose a reasonably short label for each button; some systems use buttons of a fixed size, so a long label name might not fit in the available space on some systems. Whenever possible, use a single word for each button label.

#### New `inputline()` function in adv.t

A new function, `inputline()`, is defined in adv.t. This function is a simple cover for the built-in function `input()`, with the additional feature that `inputline()` switches to the "TADS-Input" font when the game is running in HTML mode. This means that the text that the player enters during an `inputline()` has the same appearance as the text entered on a normal command line.

Note that the `input()` function does not switch to the "TADS-Input" font itself, because if it did, you'd have no way to override this behavior. Rather than forcing input text to be displayed in the "TADS-Input" font, the `input()` function leaves it up to the game author to determine how input text should look. In most cases, you should use `inputline()` rather than calling `input()` directly, to ensure that the player's input has the normal command line appearance. In some cases, however, you might wish to use a specific appearance for input text, rather than using the default setting; in these cases, you should set your own font choice, then call the `input()` function directly, since it won't change the appearance from what you choose.

#### New `restore()` return code

The `restore()` built-in function's return code has been changed. In the past, this function returned nil to indicate success, and true to indicate failure. The return value is now a number; different values are returned for different error conditions, which makes it possible to provide better information to the player about the specific problem that caused the operation to fail.

The new return values, defined in adv.t, are:

`RESTORE_SUCCESS` | Success
`RESTORE_FILE_NOT_FOUND` | The file to be restored does not exist (or could not be opened for some other reason).
`RESTORE_NOT_SAVE_FILE` | The file is not a valid saved game.
`RESTORE_BAD_FMT_VSN` | The file was saved by an incompatible version of the TADS Interpreter
`RESTORE_BAD_GAME_VSN` | The file was saved by a different game, or by a different version of the same game.
`RESTORE_READ_ERROR` | An error occurred reading the file. This could indicate that the file was corrupted, or that the physical medium containing the file is damaged.
`RESTORE_NO_PARAM_FILE` | No parameter file has been specified. This is returned only when `restore(nil)` is called to attempt to load the file specified by a start-up parameter; it indicates that there is in fact no parameter file to load.

For compatibility, `RESTORE_SUCCESS` is defined as 0, and all of the other values are non-zero. In most cases, this should allow existing code (that assumes the nil/true return value) to continue working without changes, since `if (restore(fname))` will continue to have the same effect with this change. Only code that explicitly compared the return value to nil or true will need to be changed.

The code in adv.t that calls `restore()` has been updated to reflect this change, and uses the additional information to display a more precise message when an error occurs.

#### Extended `askfile()` Interface

The `askfile()` built-in function's interface has been extended.

A new, optional fourth argument lets you specify additional flags to the `askfile` function. The possible flag values, defined in adv.t, are:

`ASKFILE_EXT_RESULT` | Return extended result codes (described below). If this flag is provided, the function returns extended results; if this flag is not specified, the function returns the traditional results.

In order to specify the new flag value argument, you must specify the prompt type and file type arguments as well; if you omitted the prompt or file type argument, the `askfile` function would not be able to tell that you meant the last argument as the flags value.

If you omit the flags argument, `askfile` uses a default value of zero, which makes the function behave the same as in past versions. Because older code never specifies a flags value, the function will always behave compatibly with past versions when called from older code.

Traditionally, `askfile` returned a string on success, or nil for any type of failure; this didn't permit the caller to determine exactly what kind of failure occurred, and in particular did not allow the caller to distinguish between an actual error and the player cancelling the file selector dialog. When `ASKFILE_EXT_RESULT` is specified, the function will return additional information that allows the caller to distinguish these cases.

When the `ASKFILE_EXT_RESULT` flag is specified, `askfile` returns a list that contains two elements. The first element is a number which indicates the status of the file selection; the second element is a string if a file was successfully chosen, or nil if not. The possible values for the first element of the returned list, defined in adv.t, are:

`ASKFILE_SUCCESS` | A file was successfully chosen. The second element of the list contains a string giving the chosen filename.
`ASKFILE_FAILURE` | An error occurred prompting for a filename. This usually indicates that the file selector dialog could not be shown for some reason (insufficient memory, for example).
`ASKFILE_CANCEL` | The user canceled the file selector dialog. On the Macintosh, for example, this means that the user clicked the "Cancel" button. This indicates that the user does not wish to proceed with whatever operation is in progress, so the operation should be aborted. Since the user explicitly chose to cancel the operation, the program should not indicate that an error occurred, but simply that the operation will be terminated in accordance with the user's request.

All code in adv.t that calls `askfile` has been updated to use the new extended results information, so that it can provide more appropriate responses when the user cancels a file selector dialog.

Here's an example, from the "restore" command's implementation in adv.t, of using the new extended results.

        local savefile;

        savefile := askfile('File to restore game from',
                            ASKFILE_PROMPT_OPEN, FILE_TYPE_SAVE,
                            ASKFILE_EXT_RESULT);
        switch(savefile[1])
        {
        case ASKFILE_SUCCESS:
            return mainRestore(savefile[2]);

        case ASKFILE_CANCEL:
            "Canceled. ";
            return nil;

        case ASKFILE_FAILURE:
        default:
            "Failed. ";
            return nil;
        }

#### New `switchPlayer()` function in adv.t

The standard library file adv.t provides a new function, `switchPlayer`, that you can use to switch the player character to a new actor. This function uses the built-in function `parserSetMe()` to change the parser's internal player character record, and in addition performs some book-keeping work that's necessary when switching to a new player.

Call `switchPlayer()` with one argument: the actor object that is to become the new player character.

First, `switchPlayer()` adds the outgoing player character object to its room's contents list, and removes the new player character object from its room's contents list. By convention in adv.t, the active player character is never in its location's contents list, but other actor objects are. Since the outgoing object is switching from being the active player object to an ordinary actor, it must be added to its room's contents list; likewise, since the new object is changing from an ordinary actor to the player character, it must be removed from its location's contents list.

Second, `switchPlayer()` removes the vocabulary words "me" and "myself" from the outgoing player character object, and adds these vocabulary words to the incoming player character. This way, these words always refer to the current player character.

If your game is based on adv.t, you should use `switchPlayer()` to change to a new player character object, rather than calling `parserSetMe()` directly, to ensure that the adv.t conventions for the active player character object are maintained through the change.

Thanks to Scott Starkey for his help defining this function.

#### New adv.t verbs: inventory wide, inventory tall

The standard library file adv.t defines two new verbs: "inventory wide" and "inventory tall." These verbs let the player control the inventory display format. "Inventory wide" displays the player's inventory in the traditional paragraph-style format. "Inventory tall" displays the inventory in a single-column format, showing one object per line, and showing the contents of each object indented one tab stop from its container.

Once the player enters an "inventory tall" or "inventory wide" command, subsequent "inventory" commands (without a format specified) will default to the format of the previous command. The traditional "wide" format is the initial setting, but you can change this in your game by setting `iVerb.useInventoryTall` to `true` in your `init` function.

#### New `nestlistcont()` function in adv.t

The standard library file adv.t has a new function, `nestlistcont()`, that displays a listing of an object's contents in a nested single-column format. This function can be used to display "tall" inventory listing, rather than the paragraph-style "wide" listings that adv.t normally displays.

`nestlistcont()` takes two arguments: the object whose contents are to be listed, and a number giving the initial indenting. The function indents the contents of the given object by the given number of tabs. For each object contained in the given object that has a contents list of its own, the function displays that object's contents indented one additional tab level, and so on for their contents.

`nestlistcont()` displays an object's contents only if the object's contents are visible, using the normal visibility rules. If the object's contents are not visible, this function has no effect. Furthermore, the function displays the contents of objects it displays only for those objects whose contents are themselves visible.

Thanks to Kevin Forchione for providing this addition to adv.t.

#### New `listcontgen()` function in adv.t

To support the `nestlistcont()` function, adv.t includes another new listing function, `listcontgen()`. This function is a general-purpose lister that provides the functionality of the traditional `listcont()` function as well as the new `nestlistcont()` function, which are essentially the same except for the display format.

`listcontgen()` takes three parameters:

      listcontgen(obj, flags, depth);

The "obj" parameter is the object whose contents are to be listed, or simply a list of objects to display. "flags" specifies a set of flag values, defined in adv.t:

`LCG_TALL` | Show a "tall" listing, with one item listed per line. If this flag is specified, each line will be indented by the number of tab stops given by the "indent" parameter. If `LCG_TALL` isn't specified, the function shows a "wide" paragraph-style listing, with the items separated by commas.
`LCG_CHECKVIS` | Checks the visibility of the contents of "obj" before listing the contents. If "obj" is a list, this flag is ignored. If this flag isn't specified, the function lists the contents of "obj" without checking to see if they're visible.
`LCG_RECURSE` | Show a recursive listing. If this flag is specified, the function lists the contents of each item it displays. The recursive call uses the same "flags" value and increments the "indent" depth by one. If this flag isn't specified, the function doesn't show the contents of the objects it lists.

Note that `listcontgen()` allows you to produce an object listing for lists other than contents of objects. If you pass a list in the "obj" parameter, the function lists the items in the list using the same formatting that it would for a contents list. This allows you to display a contents-style listing for some collection of objects other than a contents list.

#### `basicMe.travelTo` changes

In adv.t, the `basicMe.travelTo` method now checks to see if "self" is actually the active player character object; if not, the method inherits the `travelTo` processing for a regular actor, rather than using the special behavior for the current player character. This change facilitates using `basicMe` as a base class for defining player character objects in a game with more than one player character.

#### `movableActor.travelTo` changes

In adv.t, the `movableActor.travelTo` method uses an improved algorithm for determining when to show the "leaving" and "arriving" messages for the actor. The new algorithm considers the actor's visibility to the player, before and after the move, in determining whether to show the messages; the old algorithm considered only the immediate container of the actor and of the player, which produced the wrong results when the actor was moving between locations that are both visible to the player, such as a nested room. In addition, the new algorithm handles obstacles (such as doors) properly.

Thanks to Kevin Forchione for providing this improved implementation.

#### New compiler debug record format

A new compiler option, `-ds2`, tells the compiler to generate a new style of debugging information designed to work with the Windows HTML TADS Debugger (part of TADS Workbench). If you're running your game with the Windows debugger version 2.5.0 or later, you must compile with the `-ds2` option. If you're using an older version of the Windows debugger, or you're using the debugger on another platform (DOS, Unix, Mac), you must continue to use the original `-ds` option as before.

#### Parser bug fixes

The following player command parser bugs have been fixed:

- Fixed a parser bug that caused an infinite loop (which caused the game to lock up) if a game defined the same vocabulary word as both an adjective and a plural.
- Fixed a parser bug involving `disambigDobj` and `disambigIobj`. When these methods returned a list of objects that was still ambiguous, the parser's default question ("Which noun-phrase do you mean...") didn't format the list of possibilities correctly; in particular, extra commas and "or"'s sometimes appeared at the end of the list. This has been corrected.

#### Debugger bug fixes

The following debugger problems have been corrected:

- Fixed a debugger problem that caused single-step execution to skip past a line containing a disabled breakpoint.
- Fixed a problem in the debugger that caused a sporadic crash when displaying a stack traceback where a function in the stack trace had a string argument value, and the string was over about 40 characters long. This bug affected all versions of the debugger, but was most noticeable on the Windows HTML version, because the Windows debugger builds a stack traceback every time it takes control (on hitting a breakpoint, for example, or after single-stepping a line of code).
- In certain cases, the debugger showed the current source line (and breakpoints) off by a couple of lines from where it should have been. This happened when the source file had one or more lines that were exactly 97 characters long, and had DOS-style line termination (CR-LF newline sequences). (This was actually a bug in the compiler and not the debugger, but it showed up most visibly using the debugger.) This has been corrected.

#### Compiler bug fix

This version corrects an obscure compiler bug that caused `preinit` to execute incorrectly under certain circumstances. This bug occurred when `preinit` called a method in an object, and the method modified a list-valued property of the object, and the property was defined earlier in the object than the method (i.e., the property's definition in the source file preceded that of the method). In such cases, the compiler's behavior was unpredictable; sometimes it ran correctly, sometimes it stored incorrect values in the list, and sometimes the compiler crashed. This problem should no longer occur.