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.
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).
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.
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.
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").
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.
Released on April 16, 2000
This version contains no changes to text-only versions of TADS; the
only changes are in HTML versions.
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:
- __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.
Released on September 21, 1999
Index of Changes
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.
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.
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
...
}
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. ";
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);
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.
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.
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])
|
|
- 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.
- 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."
Released on July 10, 1999
Index of Changes
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.
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.
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.
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.
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.
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;
}
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.