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 August 30, 2012
- A couple of bugs appeared in version 2.5.15 due to internal
changes in the file safety mechanism. In particular, stand-alone
executable games on Windows could sometimes crash on start-up, and
compiling a game that accessed files within its preinit function
sometimes failed due to a spurious file error. The second problem
affected games using the gameinfo.t module to create a GameInfo file
within preinit. These are now fixed.
Released on December 21, 2011
- In past versions of the text-only interpreters, if an
<ABOUTBOX> section contained an <IMG> tag with an ALT
attribute, the ALT text was displayed. It shouldn't have been,
because the text-only systems aren't supposed to display
anything that's within an <ABOUTBOX> section. This has
been corrected.
(bugdb.tads.org #0000063)
- The text-only interpreters now parse and translate HTML
entity markups ("&" sequences) within <TITLE> tags when
setting the window title. (In past versions, the text-only
interpreters simply displayed the literal text of the &
sequences. The HTML interpreters didn't have this problem,
so this change doesn't affect them.)
(bugdb.tads.org #0000062)
- Some older games (specifically, compiled with a version before
TADS 2.3.0, from 1999) use an old version of the file dialog functions
that wasn't able to specify the file type. For modern GUI operating
systems, the file type is fairly important, because the file selection
dialog usually uses this to filter the file list, and to set default
file types or extensions for new files. The interpreter now tries to
infer when the interpreter is asking for the name of a transcript
file, by looking at the prompt string. If it sees the word "script"
or "transcript" the prompt string, the interpreter now assumes that
it's asking for a transcript file, and sets the appropriate default
file type in the dialog.
- The interpreter core engine is now more careful at game
termination to release allocated memory. This generally won't make
any difference to players, as the operating system usually frees this
memory anyway at program exit, but it should improve stability in
interpreters that can run multiple games in series.
- In the past, it was possible for a game to crash the interpreter
by returning a fairly long (around 200-character) message from
parseError or parseErrorParam. This has been corrected. (bugdb.tads.org
#0000100)
- Some internal changes in the code address problems related to
memory management on 64-bit processors.
(bugdb.tads.org #0000084)
Released on May 5, 2009
- Objects created with 'new' during the preinit phase are now marked
as ordinary objects in the .gam file, as though they'd been defined
statically in the source code. The same thing applies to the
vocabulary words they use. This change means that objects created
dynamically during preinit are effectively permanent: they persist
through RESTART, RESTORE, UNDO, and the like, and they can't be
deleted with the 'delete' operator.
(bugdb.tads.org #0000055)
Released on April 28, 2009
- The new systemInfo() codes __SYSINFO_AUDIO_FADE and
__SYSINFO_AUDIO_CROSSFADE let you determine if the interpreter
supports audio fades and audio cross-fades, respectively. (Audio
fades are accessed through the HTML TADS <SOUND> tag.) These
codes return nil or 0 if fades/cross-fades aren't supported at all, or
an integer giving a combination of bit-flags indicating which audio
formats support fades/cross-fades. The bit flags are
__SYSINFO_AUDIOFADE_WAV, __SYSINFO_AUDIOFADE_MPEG,
__SYSINFO_AUDIOFADE_OGG, and __SYSINFO_AUDIOFADE_MIDI (for WAV, MP3,
Ogg Vorbis, and MIDI files, respectively). For example, if
systemInfo(__SYSINFO_AUDIO_FADE) returns (__SYSINFO_AUDIOFADE_WAV |
__SYSINFO_AUDIOFADE_OGG), it means that WAV and Ogg Vorbis files
support fades, but other audio formats don't.
- A bug in the compiler sometimes made the debugger show the wrong
source location when single-stepping within a single-line expression
property. (That is, a property with an executable expression, but no
curly braces, with the whole definition on a single line of text.)
This bug was mostly cosmetic, but in rare cases triggered a separate
bug in the debugger that could crash the debugger. When a single-line
expression property was immediately followed by a single-line method
(with braces), and execution stopped in the method due to
single-stepping or due to a run-time error, the debugger was unable to
show the execution location, and sometimes even crashed. Both of
these bugs are now fixed.
Released on September 28, 2008
- The cvtnum() built-in function returned the wrong result for
negative values on occasion. The problem was dependent on chance
arrangements of the run-time string/list heap, so it occurred
essentially at random. This has been corrected - cvtnum() now
correctly stops at the end of the string. (Workaround for
older versions: append a non-digit character to the cvtnum()
argument - e.g., cvtnum(str + ';'). The problem was that
cvtnum() didn't properly detect the end of the input string, so it
sometimes scanned into random bytes in memory following the string
if those bytes happened to look like ASCII digits. However,
the function did always correctly stop upon reaching any non-digit
character, so explicitly appending a non-digit was enough to make the
function stop scanning, ensuring that it wouldn't stray past the end
of the string. This workaround isn't necessary with 2.5.12 and later,
since the underlying problem in cvtnum() is now fixed, but you can use
the workaround if you want to ensure compatibility with older
interpreter versions that some players might still be using.)
- In the past, run-time error messages (e.g., "[TADS-1010: object
value required]") were omitted from the transcript. This was
particularly inconvenient for beta testing, since any errors
encountered by testers were invisible in their session logs. These
messages are now included in the transcript.
(bugdb.tads.org #0000032)
Released on August 9, 2008
- The parser now leaves the antecedent of "it" intact when a command
involves a number or string. That is, "it" will simply refer to the
noun phrase from the second-most-recent command when the most recent
command involves a number or string as its noun phrase: "x box; note
5; x it" will now treat the "it" in the last command as referring to
the box. In the past, the parser simply forgot any antecedent in this
type of situation. That was intentional, to avoid any confusion over
whether "it" should refer to the number or string, but in practice
players don't seem to find this confusing at all and expect "it" to
continue referring to the last real object.
(bugdb.tads.org #0000014)
- The parser showed a somewhat confusing parser error message for
player commands of the form "verb x OF y" in some cases. If x and y
were valid nouns or adjectives, but the combination "x OF y" didn't
match any object's vocabulary (e.g., there's a BOX and some PAPER, but
there's no such object as BOX OF PAPER), the parser treated this
command as though it were a two-object verb, such as PUT IN or
LOCK WITH - that is, the OF was treated as a verb-phrase preposition,
and x and y were treated as (respectively) the direct and indirect
objects. This often resulted in the parser error "I don't recognize
that sentence," since OF is a rare preposition in verb phrases. For
example, EXAMINE BOX OF PAPER would be treated as an EXAMINE OF
command, which most games don't defined as a valid verb phrase. In
such cases, the parser now checks to see if it could have
formed a syntactically valid noun phrase from the whole "x OF y"
phrase, and if so, shows the more sensible error "I don't see any x of
y here."
(bugdb.tads.org #0000010)
- If the game executed parserReplaceCommand() from within certain of
the "parser hook" functions, the interpreter displayed the run-time
error "TADS-1024: 'abort' statement executed". This happened because
of the way parserReplaceCommand() works internally, but it obviously
shouldn't result in an error message. This is now fixed.
(bugdb.tads.org #0000011)
- The compiler's -p option (to make the compiler pause before
terminating, to give you a chance to read the output) didn't work
in some cases where an error occurred during compilation. This
has been fixed.
(bugdb.tads.org #0000015)
Released on August 17, 2006
- The INVENTORY TALL command now respects "quiet" containers and
surfaces (that is, the contents of these objects will not be listed in
the inventory). In the past, the default INVENTORY WIDE mode
respected the "quiet" flags, but TALL mode didn't. INVENTORY TALL
also now properly takes into account the "contentsVisible" settings of
the objects listed. These changes ensure that WIDE and TALL modes
yield the same information.
- In adv.t, the default message for TELL thing ABOUT
topic now properly takes into account the number (singular or
plural) of the thing, so the message will now change to "...are
interested" when the thing has a plural name. In the past, the text
always said "is interested," which was ungrammatical when the thing
had a plural name.
- A bug in the interpreter caused unpredictable behavior, sometimes
crashing the interpreter, when starting certain games, including
recent release Finding Martin. The problem was triggered by
a very large number of objects (more than about 150) in a single
location, so it didn't affect most games, and it appeared somewhat
sporadically even for games that did trigger it. The bug has now
been fixed.
- An interpreter bug caused delword() to occasionally and
unpredictably delete extra words beyond what it was actually
asked to delete. This has been corrected.
- In the past, if the player's answer to a disambiguation question
had certain kinds of syntax errors, the parser responded with a
suitable error message, but incorrectly showed that message twice in a
row. This was due to an interpreter bug, which has now been corrected.
- In adv.t, the messages for "the key doesn't fit" in
doorway.doLockWith and doorway.doUnlockWith were phrased incorrectly
in some cases (in particular, the subject and verb didn't always
agree). This has been corrected.
Released on September 12, 2004
- The "file safety" levels have changed slightly to provide better
security against malicious game programs. First, level 2, which
formerly provided global read access but no write access, now instead
provides read/write access to the current working directory only (the
game isn't allowed any access to files outside of the working
directory). Second, the default safety level is now level 2. In the
past, level 1 (read from any file/write to current directory only) was
the default, which left open the possibility that a game could access
data outside of the working directory. A malicious game could
conceivably have exploited this by, for example, secretly copying
sensitive data to the game directory for later use by a coordinating
network worm, or by encoding the data in a hyperlink (displayed in an
HTML-enabled game) that takes the player to a spyware site when
clicked. The new default setting should effectively eliminate these
risks by barring access to any files outside of the working directory.
- A bug in the compiler sometimes caused crashes when the "preinit"
function called certain built-in functions, particularly for file
manipulation. The problem was somewhat random, and tended to show up
on some machines and not others, because it depended on memory
conditions left over from other programs. The problem showed up for
several people when they included the "gameinfo.t" module in their
games, because that module writes a text file during preinit. The bug
has now been fixed.
- An interpreter bug was capable of corrupting list values during
game execution under very rare conditions. The bug only showed up
when assigning a value to an indexed element of a list (either
directly or via the "+=" or "-=" operators) at just the right moment
to trigger garbage collection, and only then when the list happened to
be situated in memory in just the right way. The bug was more likely
to occur with small "heap" memory settings (the "-mh" option); with
the large default heap of most current interpreters, the bug almost
never showed up. The bug was also quite unlikely to occur except in
games that perform unusually heavy list processing. The bug
manifested itself in unpredictable ways, ranging from spurious
run-time errors to interpreter crashes. This has now been fixed.
Note that most games that were likely to trigger the bug in the past
will now instead cause a "heap overflow" error; this error is valid,
and indicates that the game exhausted the available memory for
processing list and string values. If a heap overflow does occur
while running a game, try increasing the heap size using the "-mh"
command-line option when you run the game.
- An inconsistency in the command parser has been fixed. In the
past, the parser didn't properly take into account whether responses
to missing object prompts were singular or plural. This caused some
subtle differences between typing a full command and piecing a command
together with missing object prompts. In particular, the parser
didn't announce the object names for a plural response, and didn't
check rejectMultiDobj. This has been fixed, so the parser's behavior
is now consistent for missing object responses.
- In the past, the parser incorrectly treated THEM as plural even
when it referred to only one object, so it checked with
rejectMultiDobj in these cases. This is undesirable in some cases,
especially when the single object being referred to is an object whose
name has plural usage. The parser now treats THEM as singular when it
refers to just one object.
Released on June 12, 2004
- In adv.t, THROW AT now uses a new method, throwHitDest, to
determine where the thrown object lands. This method is called for
the actor's current location; by default, it simply returns 'self', so
a thrown object simply lands in the actor's current location. This is
the same behavior as in the past. However, the special 'theFloor'
object overrides this to return the current room. This corrects a
problem that occurred when the player sat on the floor and then threw
something: because the object formerly ended up in the actor's
location, the object landed in the 'theFloor' object, at which point
the object became inaccessible. This change ensures that a thrown
object will never be moved into 'theFloor', but will always land in
the actor's actual room location. As a side benefit, the new method
also makes it possible to customize the location where objects land
when thrown from within a nested room.
- When running in HTML mode, the default parser error message 1 ("I
don't understand the punctuation %c") now includes an "\H-" sequence
before the message to ensure that the unrecognized character is
displayed literally if it's an HTML markup-significant character
(such as '<' or '&'). It also shows an "\H+" sequence after
the message to restore HTML mode. In the past, if the player entered
a markup-significant character while the interpreter was in HTML
mode, the HTML parser could become momentarily confused, leading to
strange output.
- The new parser hook parseDefaultExt() extends the parseDefault()
function. The new function takes two additional parameters, giving
the actor and deepverb object involved in the command; the new
parameter list is parseDefaultExt(actor, verb, obj, prp). If the game
defines parseDefaultExt(), then the interpreter ignores parseDefault()
and calls parseDefaultExt() instead. If the game does not define
parseDefaultExt(), but does define parseDefault(), the interpreter
uses parseDefault() as it used to, which ensures that existing games
will continue to run unchanged. You can use the new extended version
of the function if you need the actor and/or verb information in
displaying the default object message; this is useful in some
non-English languages where a default object name's noun phrase must
be inflected according to its usage in the command.
- The new parser hook preparseExt() allows the game to inspect and
optionally change text the player types in response to parser
questions: disambiguation queries, requests for missing direct or
indirect objects, and OOPS typo-correction opportunities. The new
function's parameter list is preparseExt(actor, verb, str, typ), where
'actor' is the actor performing the command, 'verb' is the deepverb
object, 'str' is a string containing the exact text the player typed
in, and 'typ' is an integer giving the type of query the player is
responding to. The meaning of 'typ' is the same as the that of the
type codes passed to commandPrompt() and commandAfterRead(). The
function can return a new string, which the parser will use to replace
the text the user typed; true, to proceed with the original text the
user typed; or nil, to treat the user's text as a brand new command,
bypassing the special interpretation as a response to the parser's
question. The game doesn't have to define the preparseExt() function;
it's purely optional, so existing games will not be affected.
- When the interpreter looks for external resource bundles (*.rsN files),
it now looks for the files using lower-case and upper-case suffixes
(in other words, it looks for both *.rs0 and *.RS0, and likewise for digits
1-9). Lower-case suffixes are tried first, then upper-case. This change
makes the naming system more flexible on systems with case-sensitive
file systems, such as Unix; for systems such as Windows and Macintosh that
have case-insensitive file systems, this change will have no effect.
- A bug in the parser made it impossible to use numeric adjectives
in certain phrasings. In particular, if a given word was defined in
a game as both a noun and an adjective, but the word was defined only as
a noun for a given object that takes a numeric adjective, then the
phrasing noun number didn't match the object. For example,
if the word "switch" was defined as a noun for a given numbered switch,
but "switch" was defined as an adjective for some other object, then
"switch 5" didn't match the switch even if "5 switch" did. This has
been corrected.
- The compiler now checks for missing symbols before running the
'preinit' function, and skips running preinit if any symbols are
undefined. In the past, the compiler called preinit even if some
symbols were undefined; if preinit itself called any undefined
functions or invoked methods of undefined objects, this sometimes
led to misleading error messages. The compiler will now simply
point out the missing symbols, and skip running preinit entirely.
- The compiler now allows filenames up to 255 characters to appear
in the source file names in debugging records. In the past, the limit
was 127 characters. Important note: due to a bug, older
interpreters cannot properly handle .gam files compiled with debugging
information with path names over 127 characters. Attempting to load
.gam files with debugging information containing long source file
names will cause unpredictable results. Since some users might still
have older interpreter versions, we strongly advise against
releasing games compiled with debugging information. That is, you
should never release a game that you compiled with the "-ds" or "-ds2"
options.
- In the past, if the game code called a function or method, using
the return value of another function or method as an argument, and the
other function or method didn't actually return any value for the
argument, the results were unpredictable; in some cases, this even
caused the interpreter to crash. The interpreter is now more careful
about checking for these cases; when they occur, the interpreter now
automatically adds 'nil' values for the missing arguments. This
should ensure that errant code won't crash the interpreter, without
breaking any existing code with "asymptomatic" instances of this
error.
- The default "file safety level" for the command-line interpreters
is now set to 1, which allows reading files from anywhere on the disk,
but allows writing files only to the current working directory. This
protects against malicious game authors attempting, for example, to
modify your operating system's files. You can manually override this
to use a more permissive or more protective level using the "-s" option.
- External functions are now obsolete and can no longer be used on
any platform. One of the nice things about running games with a
system like TADS is that you don't have to worry as much about
viruses and other malicious code when playing downloaded games, since
games run in the interpreter environment rather than as native code.
The external function feature made it possible for games to call out
to native code, though, so a malicious author could have used
external functions to do damage to a user's system. Removing this
feature eliminates this security loophole. External functions were
only supported on a few platforms to start with, and they never
caught on with authors, probably because they were difficult to use
and had serious drawbacks (such as complete unportability). Since no
one has ever had much use for this feature, and because of the
potential security vulnerability, we've decided that there's no
reason to keep it around.
- In the past, the compiler did not accept 'self' as an element in a
list, even in contexts where 'self' was otherwise valid. The compiler
now does accept 'self' as a list element in method contexts.
- A bug in the interpreter sometimes made the result of the
subtract-and-assign operator ("-=") incorrect under certain
circumstances: specifically, the problem occurred when the left-hand
side was a local variable, the local variable contained a list value,
and the list value did not contain the value on the right-hand
side of the operator. In some such cases, a run-time error occurred;
in some others, the wrong value was assigned to the local variable.
This has been corrected; when the value on the right side is not in
the list, the list value in the local is simply left unchanged.
- Due to a bug, the compiler sometimes crashed if you used a "break"
and "continue" statement in an invalid context (i.e., outside of a
"while", "for", "do", or, in the case of "break", a "switch"
statement). This has been corrected; the compiler now simply flags
these as errors, but shouldn't crash.
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.