Newsgroups: rec.arts.int-fiction
Path: gmd.de!xlink.net!news.belwue.de!zib-berlin.de!netmbx.de!Germany.EU.net!EU.net!howland.reston.ans.net!sol.ctr.columbia.edu!newsxfer.itd.umich.edu!nntp.cs.ubc.ca!newsserver.sfu.ca!sfu.ca!neilg
From: neilg@fraser.sfu.ca (Neil K. Guy)
Subject: A TADS Tip Sheet
Message-ID: <neilg.763944351@sfu.ca>
Keywords: TADS tip sheet
Sender: news@sfu.ca (seymour news)
Organization: Simon Fraser University, Burnaby, B.C., Canada
Date: Thu, 17 Mar 1994 22:45:51 GMT
Lines: 508


 +-----------------------------+

  A TADS Tip Sheet, version 0.1

 +-----------------------------+


TADS is a very powerful and flexible programming system. However
with that power comes a good deal of complexity. Here are some tips
that I've come up with over the past two years of TADS authoring.
And I've decided that there may well be beginning TADS authors out
there who could use some of these tips. Feel free to mail me with any
suggestions for new tips or, indeed, corrections. I can be reached at
neilg@sfu.ca.

 - Neil K.


RESTRICTIONS:
You may distribute, modify and copy this file as much as you like,
but it cannot be bought or sold. Please include this notice and a
list of any changes you made if you choose to distribute it.


STANDARD PARANOID ANTI-LITIGATION STUFF:

I believe that the information contained in this document is
reasonably accurate. However, I make *no* guarantees that the document
is error free. If you find something wrong, please mail me a
correction and I'll fix it. I hope that's all that needs to be said on
this point.
 Also, any mention made within this document of commercial or shareware
products is purely for informational purposes, and does not imply an
endorsement of any kind by anybody.


VERSION HISTORY:

Version 0.1. Written 4/26/93 Neil K. (neilg@sfu.ca)


WHAT IS TADS?

TADS is a particularly groovy system for programming text adventure
games on a variety of computer platforms. It's a shareware package
written and published by High Energy Software. You can find the
latest and greatest version of TADS in a variety of places,
including /if-archive/programming/tads on ftp.gmd.de. (for those
people who can use FTP on the Internet.)

 If you have access to Usenet and you're interested in discussions on
the topic of writing interactive fiction, check out the group
rec.arts.int-fiction. For discussions on the topic of playing
interactive fiction games, check out the group rec.games.int-fiction.
Furthermore, High Energy Software runs its own bulletin board system
for TADS authors. Consult the TADS documentation for the BBS number
and other ways of getting in contact with High Energy Software.


 +---------+

  Some Tips

 +---------+


READ THE MANUAL.

TADS has a great manual. A wondrous manual. Michael J. Roberts clearly
poured half his soul and an awful lot of time into writing it. Read
it. Worship it. This may be something of an obvious tip, but spending
time with it will minimize time wasted later on figuring out why
something won't work.


READ THE TADVER FILES.

Your copy of TADS should have come with a file called TADSVER.xxx,
where xxx is the operating system you use. This file is extremely
handy - it lists all the new features and fixed problems with the
latest versions of TADS. There are many extremely useful new
features that have been added to TADS since the version 2 manual
was released, and this file documents them.

 Note that there are two versions of the TADSVER file - one that
lists all of the new features up to version 2.1 or so,
(TADSV200.xxx) and another file that lists the changes from 2.1
onwards.


READ SOME SOURCE CODE.

One of the best ways to learn how to program is, in my opinion, to
find some well-written source code and play with it for hours on
end. You can learn a lot through doing this. Tearing someone else's
source apart and modifying it for your own ends is a very
educational activity. TADS ships with the full source for a small
game, Ditch Day Drifter, which illustrates many important basic
features of TADS. You can also buy a much bigger and more
complicated game from High Energy Software, Deep Space Drifter,
that comes with complete source.

 If you're on the Internet and have access to the treasure trove of
stuff in the if-archive at ftp.gmd.de, check out the sample source
code available there. Dave Baggett, in a deeply admirable selfless
gesture, has ported the classic Colossal Cave (ie: "Adventure")
game to TADS and published the source code in order to help train
future generations of TADS authors. There's also the Examples
directory in the TADS programming area that contains a number of
small source code examples written by many TADS authors.

 There are also a lot of other files in the if-archive that, although
not directly related to TADS, nevertheless contain extremely useful
information. For example, Graham Nelson's Inform compiler, which
generates games compatible with the classic Z-machine system
designed by Infocom, has a well-written manual that contains a lot
of useful tips that can be used by any text adventure author.


*NEVER* MODIFY GAME STATE IN VERIFY METHODS.

This is a very very common problem. The way TADS uses the "verify"
method in a verb can be quite confusing, and it can lead to a lot
of problems.

 Basically, remember that TADS doesn't use the verify method solely
to see if an item can be verbed appropriately. It also uses the
*same method* as part of its disambiguation routine. When it does
so it silently calls the method. That means it calls it, but
suppresses any text that the message may try to produce.

 The upshot of all this is that if you change the state of the game
somehow and don't simply display text you can really get strange
things happening when that verify method is triggered inadventently
by a disambiguation routine.

 Here's a concrete example. This is bad:

 badMagicTurkey: item
   sdesc = "magic turkey"
   [ etc. etc. ]
   verDoTouch( actor ) =
   {
     "Good heavens! The magical turkey vanishes
     in a cloud of tangerine-coloured vapour! ";
     self.moveInto( nil );
   }
 ;

 The code is bad because the disambiguation may call that verify
method sometime, and if it does so it'll suppress the text but
modify the game state by moving the turkey into nil. Thus our
turkey may suddenly vanish for no readily apparent reason, and
the player won't be notified when it does. The fix? Simply do this:

 goodMagicTurkey: item
   [ etc. etc. ]
   verDoTouch( actor ) = {}
   doTouch( actor ) =
   {
      "Good heavens! The magical turkey vanishes
      in a cloud of tangerine-coloured vapour! ";
      self.moveInto( nil );
   }
 ;

 Since our verify method has simply an empty method the runtime just
moves on to the actual verb method and executes it. No problems.
Check out pages 36-39 of the TADS manual (a crucial section, by the
way) and particularly page 39 for more details.


COMMON COMPILER ERRORS.

Some of the most common compile-time errors result from forgetting
to add a semicolon or not closing parentheses properly. As
explained on page 201 of the manual, the compiler will try to skip
ahead to the next code object if it encounters a problem. Thus if
you end up with an endless string of compile-time errors appearing
on your screen when you try and compile you can usually safely
ignore most of the errors - it was likely the first one that caused
all the problems.

 Another common problem is accidentally adding a semicolon after an
if statement, thus:

 if ( elvis.isDead );
 {
   say( 'No kidding. ' );
 }

 This will cause a compile-time error as the semicolon after the
if(); statement will tell the compiler that the if statement is
complete.


COMMON RUNTIME ERRORS.

If you forget to return a value from a method or function you'll get
the dreaded 1010 error. This could also mean that you've asked the
game to evaluate a property that doesn't exist. For instance, if
you have something like this:

   if ( Me.location.isUpstairs )

 and Me.location = nil, you'll have problems. You'd be better off
doing this:

   if ( Me.location and Me.location.isUpstairs )


CASE IS USUALLY SIGNIFICANT.

When you play a TADS game you can enter commands in upper, lower or
mixed case and everything works fine. However, TADS *code* is
normally case sensitive. If you're having problems with a variable
or object name not being recognized double-check to make sure that
the capitalization is consistent. Common problems involve
properties like "islit" or "isseen" and methods like "verDoTake"
and the like.

 Newer versions of the compiler do permit case insensitivity, but
you'll likely find your game won't compile with this option turned
on unless you check your entire source code for case issues.


TO MODIFY ADV.T OR NOT TO MODIFY ADV.T?

That is the question. This is one of those perennial Difficult
Decisions that TADS authors have to face. There are essentially two
basic approaches as I see it.

1) Don't touch adv.t at all. The only changes made are done through
   TADS 2.1's "modify" and "replace" features.

   Pros: game doesn't break when a new version of adv.t is released.

   Cons: game can't take advantage of custom adv.t features.

2) Modify adv.t as needed, brazenly ignoring "modify" and "replace."

   Pros: the game can include a myriad features that go beyond the
   fairly basic adv.t defaults.

   Cons: it's a lot more work to maintain as every single time a new
   version of adv.t is released you've got to go through manually and
   check every bit of code to make sure it still works.

 Either way it's probably good practice to document thoroughly any
changes you make to adv.t or std.t.


DON'T USE "THING".

Never create an object of type "thing". You'll get a runtime error
if you try to pick it up. Always create an object of type "item" if
you want something small and takeable. An item with "thing" as its
class is, of course, just fine.


LOCAL STATEMENTS.

Local statements must be the first statements to appear in a block
of code. Otherwise you get a "general syntax error." As mentioned
in the manual on page 71, the only kind of code that can precede a
local statement is another local statement. Thus, this next code
segment won't compile because its local statement is preceded by
something else:

 superFunction: function
 {
   "Hello there! ";
   local burp := true;
 }

 This (admittedly rather pointless) function will work flawlessly
if you put the local statement at the top, before any other statements
within the brackets.


WHY THE HECK WON'T FLOATING ITEMS WORK?

In all likelihood you've defined an object as being a floating item
without also defining it as being a member of whatever class it
needs to be. In other words, this won't work:

   tree: floatingItem

 but this will:

   tree: fixeditem, floatingItem

 You may also have neglected to include the preinit() code that sets
up the list of all floating items in the game.


WHENCE DOUBLE-QUOTED STRINGS?

There's a crucial difference. TADS displays the former whenever it
sees them but won't display the latter unless you explicitly use
the say() function. In other words, this won't work:

   if ( self.colour = "red" )
     self.colour;

 The runtime will automatically display the word "red" whenever it
evaluates this piece of code. You probably don't want that. Instead
you should do this:

   if ( self.colour = 'red' )
     say( self.colour );

 Why does TADS have this unusual double-quoted string concept? Well,
because it's very useful. Adventure games are constantly displaying
text. It'd be extremely tedious to have to say something like
"printf( 'blah blah' );" or whatever all the time just to get words
on the screen. Double-quoted TADS strings are a useful shortcut.
This is explained on page 21 of the manual. 


"OF" IS A SPECIAL WORD.

As described on page 32 of the manual, the word "of" is a special
word that gets removed by the parser. If you want to use it in some
context or other (say "accuse Frank of murder") then you'll have to
do some hacking about with compoundWords or take a different tack.


PLURAL AND SINGULAR PROBLEMS.

Let's say you have an object somewhere with a noun of "blinds".
Let's say you also have another one elsewhere with a plural of
"blinds". Now you won't be able to refer to the item with only a
noun set to "blinds" - you'll get the "I don't see that here"
message. Both have to be either noun only or plural only.


NO CONDITIONAL VOCABULARY.

TADS doesn't permit conditional vocabulary. It sets up a static
table of all known vocabulary words at compile time. So something
like this is not legal:

   noun =
   {
     if ( self.isBig )
       'big'
     else
       'small'
   }

 If you want to have items referred to by different vocabulary words
then you must have two or more different objects and swap them as
necessary. In fact you could define a class for this object and
then define as many instantiations of that class as necessary, each
with separate vocabulary. The objects will inherit their properties
and methods from the class.


SPACES AFTER STRINGS.

It's usually good form to add an extra space after a string of text,
thus:

   ldesc = "It's an ordinary turnip. "

 instead of:

   ldesc = "It's an ordinary turnip."

 Why? Well you never know what text might be displayed next. If, say,
a daemon displays some text without first printing a "\b"; sequence
to add a blank line you'll get your sentences running together.
Adding a blank space prevents this from happening.


TADS IS OBJECT-ORIENTED.

It really is! Don't code up objects with endless case statements and
if-else statements when you can inherit properties and methods from
classes. You'll find the code is much more elegant and easy to
understand and often takes up less room as well. Thus, this following
piece of code is a Really Clunky Way to do things:

 bananaTree: fixeditem, floatingItem
   sdesc =
   {
     switch( Me.location )
     {
        case Forest:
          "big";
          break;
        case Greenhouse:
          "small";
          break;
        case Jungle:
          "enormous";
          break;
      }
      " banana tree";
   }
   [ etc etc ]
   locationOK = true
   location =
   {
      if ( Me.location = Forest or Me.location = Greenhouse
      or Me.location = Jungle )
        return( true );
   }
 ;

 This is a somewhat better way to do the same sort of thing:

  bananaTree: fixeditem, floatingItem
    sdesc =
    {
      Me.location.bananaTreeString;
      " banana tree";
    }
    [ etc etc ]
    locationOK = true
    location =
    {
       return( Me.location.hasBananaTree );
    }
  ;

 Then you could set the hasBananaTree property to nil in the "room"
class, so that all other rooms would inherit this nil value.
Special locations could have the property set to true. Likewise,
those special locations could have their bananaTreeString
properties set to display the appropriate message.

 What's the advantage of doing it this way? Well, putting special
case code into special rooms means you don't have to hardcode a
whole pile of unwieldy conditional coding into the floating item.
Also, special case stuff (ie: the forest having a tree or whatever)
is associated with special case locations.

 (note: this isn't the world's greatest piece of sample code as it
has one major inconsistency in it. That is, unless you've also
modified the chair item class in adv.t to pass the hasBananaTree
value through, the tree will mysteriously vanish if you sit down on
anything. Just something to keep in mind!)


BREAKING OUT OF THE RUNTIME.

Sometimes TADS gets stuck and only displays an error message instead
of accepting input. Other times you type something but absolutely
nothing is displayed - the game just returns you to the > prompt.
These usually indicate a bug in your TADS code. In either case
typing the special command $$ABEND in the runtime window should
force the runtime to quit.


 +--------------+

  Macintosh Tips

 +--------------+


 Here are some tips for the users of the Macintosh version of TADS.
I don't normally do compilation on the DOS or UNIX or other flavours
of TADS so I'm not going to include any tips for those platforms
unless someone sends me some.


COMPILER BOMBS.

If you have a very large game and you're having problems with the
compiler crashing when you try to compile, it's possible you don't
have enough memory allocated. Try increasing the amount of memory
set aside for the compiler by changing the setting in the "Get
Info" box. 1500 K is usually enough for even a big game.


MULTIFINDER OR SYSTEM 7.

Don't forget that TADS can run in the background under MultiFinder
or under System 7. However once TADS has finished loading in the
files and starts the actual compilation it'll lock up your Mac for
the duration of the compile.


TEXT EDITOR.

If you're struggling with editing code on a word processor or
something, take a look at some of the freeware and shareware text
editors out there. There are quite a few, including Edit II and
Alpha. Probably one of the best (IMHO) is BBEdit, however.
 BBEdit itself is now payware, but thee's still a freeware "Lite"
(sic) version available. Any version is ideal for editing TADS
code. They do bracket and brace balancing, semi-automatic
formatting, GREP search and replace, no wordwrap and a thousand and
one other features. Try to find version 2.2.2 which, unlike the
newer BBEdit Lite, can do file comparisons. This function,
analogous to a "diff" utility, is an extremely nice way to compare
older versions of source with the new. Of course obssessive hackers
can check out the port of emacs...


COMMAND-PERIOD.

Don't forget that TADS can now break out of endless loops by hitting
command-period, like all good Mac programs do.
