Recent Changes to TADS

Index to Versions

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:

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


Version 2.5.16

Released on August 30, 2012


Version 2.5.15

Released on December 21, 2011


Version 2.5.14

Released on May 5, 2009


Version 2.5.13

Released on April 28, 2009


Version 2.5.12

Released on September 28, 2008


Version 2.5.11

Released on August 9, 2008


Version 2.5.10

Released on August 17, 2006


Version 2.5.9

Released on September 12, 2004


Version 2.5.8

Released on June 12, 2004


Version 2.5.7

Released on September 22, 2002


Version 2.5.6

Released on June 1, 2002


Version 2.5.5

Released on September 27, 2000


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:

Bug Fixes




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

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:

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




Version 2.5.1

Released on September 21, 1999

Index of Changes

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

Bugs Fixed




Version 2.5.0

Released on July 10, 1999

Index of Changes

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:

Debugger bug fixes

The following debugger problems have 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.