A TADS Tip Sheet, version 0.2a


About the Tip Sheet

TADS, Michael J. Roberts’ Text Adventure Development System, is a very powerful and flexible programming system for developing text adventures. (interactive fiction) However with that power comes a good deal of complexity. Here are some tips that I’ve come up with over the past four years of TADS authoring. And I thought 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 tela@tela.bc.ca.

Note that this tip sheet was written prior to the publication of the TADS 2.2 Release Notes. A lot of the material in this tipsheet is actually explained, in far more detail, in those notes, especially the rewritten Book of the Parser.

- N. K. Guy, tela design.

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 redistribute it.

Standard paranoid anti-litigation stuff

I believe that the information contained in this document is reasonably accurate. However, I make no guarantees of any kind that the document is error free or will meet your needs or anything like that. You assume full risk for using or not using any information in this file. 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. (tela@tela.bc.ca)
updated to 0.1a (banana tree example code fixed) 3/18/94
updated to 0.2 to reflect TADS 2.2.1.0 Freeware 10/19/96
updated to 0.2a. Added the #pragma C+ warning 10/21/96

What is TADS?

TADS, the Text Adventure Development System, is a particularly groovy system for programming text adventure games (sometimes called “interactive fiction”) on a variety of computer platforms. TADS and Graham Nelson’s Inform are the two most powerful, and likely most popular, development systems for writing text adventures currently available at time of writing.

TADS was originally a shareware package written and published by High Energy Software. However, in mid 1996 TADS author Michael J. Roberts publicly announced that TADS was to become a freeware product and that High Energy Software would be shutting down the bulletin board system that it used to support the program.

He has made good on his promise, and has even freely released the source code that makes up the program. You no longer have to pay anyone to use TADS - it’s totally free! However, Mike Roberts retains copyright to the TADS system, so you can’t go and change it or try (operative word here is try) and make lots of money off it without his permission.

Where can I get it?

You can find the latest and greatest version of TADS in a variety of places, including, if you have access to Internet FTP:

ftp://ftp.gmd.de/if-archive/programming/tads/

This site, the Interactive Fiction Archive (if-archive) is at GMD, the German National Research Centre for Information Technology. It’s run by volunteer maintainers Volker Blasius and David Kinder, to whom we all owe a debt of gratitude. Note that if you don’t live in Germany and find the connection to be rather slow, or if you want to be a good net.citizen and minimize your use of overtaxed international links, you can access the if-archive at a number of mirror sites. For example, US users may want to try Washington University’s mirror at wu-archive:

ftp://ftp.wustl.edu/doc/misc/if-archive/programming/tads/

Canadian users might want to try Internex Online’s mirror at io.org:

ftp://ftp.io.org/pub/mirrors/if-archive/programming/tads/

European users outside Germany could try the Finnish University and Research Network’s mirror:

ftp://ftp.funet.fi/pub/misc/if-archive/programming/tads/

Additionally, if you have access to Usenet news and you’re interested in discussions on the topic of writing interactive fiction, check out the newsgroup:

news:rec.arts.int-fiction.

For discussions on the topic of playing interactive fiction games, check out the newsgroup:

news:rec.games.int-fiction.


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.

The manual is available in two forms at time of writing. The first is typeset using TeX, the powerful and deeply arcane typesetting language popular within the academic community. If you have a program that can display TeX files (or, more accurately, that can convert the TeX files into DVI files for display purposes) then you might want to get that. The TeX files can be found at:

ftp://ftp.gmd.de/if-archive/programming/tads/manuals/

The second is an HTML (HyperText Markup Language) translation of the TeX files for Web browsers, converted by yours truly. This version is on the Web at:

http://www.tela.bc.ca/tela/tads-manual/

I’ll probably upload it to the GMD if-archive as well. And I’m sure the TeX files will be converted into all kinds of other formats over the next while.

Read the TADSVER.* 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 useful new features that have been added to TADS since the version 2 manual was released, and this file documents them. No TADS user should be without it!

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. I’ve put the most recent version of the Macintosh TADSVER file online at:

http://www.tela.bc.ca/tela/tads-manual/tadsver-mac.html

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.

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. For instance, there is an Examples directory in the TADS programming area that contains a number of small source code examples written by many TADS authors. A lot of these examples are written specifically to teach people about various principles of game design. Others are written to implement commonly-used features. For example, I wrote a little set of modules that I find very useful for testing games in progress. That file, wizard.t, is available in the examples directory. Another useful file is bugs.t by Stephen Granade, which provides patches for some minor bugs in the adv.t TADS libraries.

ftp://ftp.gmd.de/if-archive/programming/tads/examples/

In addition to the mini code examples there is original TADS source to a number of complete full-length games on ftp.gmd.de as well. For example, 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. He’s also made the source to his epic game “Legend” available. 1995 Interactive Fiction Contest winner Magnus Olsson has put the source to his winning entry “Uncle Zebulon’s Will” in the same directory, and TADS author Michael J. Roberts has put his games “Deep Space Drifter” and “Perdition’s Flames” online as well.

ftp://ftp.io.org/pub/mirrors/if-archive/games/source/tads/

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. Check out his fascinating “Craft of Adventure” document.

ftp://ftp.wustl.edu/doc/misc/if-archive/info/

Now let’s get into some of the technical tips.

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. Verify methods are object methods that start with “ver”.

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. If any text is produced and hidden then the game knows that the item in question can’t be verbed by the verb.

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 function might 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. This is the section titled “Disambiguation” in Chapter 4.

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.

Yet another easily made error is to name a local variable the same name as a function. You can’t do that.

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 because TADS will try to figure out the isUpstairs property of a non-existent object. You’d be better off doing this:

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

Another common problem involves the number of arguments you send to a function. Let’s say you’ve invented a nice function like this:

   superDuperFunction: function( parm1, parm2, parm3 )
   {
      // ingenious code in here.
   }

Now if you call your function using this code:

  superDuperFunction( nil, true, nil, nil );

you’ll get the annoying TADS-1026 error. The same error will occur if you call a method with the wrong number of arguments.

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 three 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.
  3. Ditch adv.t altogether and use WorldClass instead.

    Pros & Cons: See next section.

Either way it’s good practice to document thoroughly any changes you make to either adv.t or std.t or both.

Take a look at WorldClass

David Baggett, half of the Adventions team famous for the Unnkulian Unventures, has designed a complete replacement class library system for TADS. WorldClass replaces the standard adv.t and std.t TADS libraries altogether.

WorldClass is an extremely powerful and flexible class library system that lets you do some pretty impressive stuff. It’s much more consistent in structure and naming, and supports a lot of very useful concepts. For instance, it lets you treat all five senses equally in a game, and can handle abstractions like knowledge of items.

There are two disadvantages to WorldClass as I see it, however. The first is that it’s considerably slower than adv.t on old machines. If you have a reasonably modern computer, this won’t be an issue, but if you have a really ancient clunker (eg: Mac Plus, IBM PC AT) then you’ll find WorldClass games take a long time thinking between moves. This is because all items in the game are considered, rather than just those in your immediate surroundings. This means that cool stuff like knowledge classes are supported, but does slow things down.

Second, WorldClass is quite a bit more complicated than adv.t. It lets you do a lot more than adv.t, but does have a steeper learning curve as a result. If you want to knock off a quick mini game, you might want to stick with adv.t. But if your ambitions are a bit higher, consider spending the time to learn what WorldClass can do for you!

WorldClass, including Paul Gilbert’s excellent manual, is in the if-archive at:

ftp://ftp.gmd.de/if-archive/programming/tads/worldclass/

Case is normally 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. WorldClass is considerably more consistent than adv.t on this score.

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.

Check out the TADS debugger

One of the coolest things about TADS is that it has a fabulous source code debugger. This was one of the major perks for buying the shareware version of TADS back when it was shareware. And now that TADS has gone freeware the debugger is freely available at your disposal. At least, if you use Macintosh or MS-DOS computers it is. At time of writing the debugger wasn’t available for any other platforms, but since the source code is available I’m sure busy hands are at work as we speak, porting the program to every platform under the sun.

So be sure to check the thing out if you can get your hands on it. It lets you run a game and step and trace through the source, making it really easy to find bugs. You can even go in and change variables and stuff, making it quite simple to test your code. The Mac version is particularly nice, as it uses multiple windows to display the game, the command line for the debugger and the source code.

Don’t use “thing”

Never create an object of type thing. You’ll get a runtime error if the player tries 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

It may sound like a bit of a contradiction in terms to have a tree that’s both fixed and floating, but there you go. If you’ve coded your tree this way and it still doesn’t work then it’s possible that you neglected to include the preinit() code that sets up the list of all floating items in the game.

Whence double-quoted strings?

By “double-quoted” I mean strings of text that are delineated by quotation marks rather than apostrophes. 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 printed manual.

“Of” is a special word in older versions of TADS

As described on page 32 of the manual, the word of is a special word that gets removed by the parser in older versions of TADS. If you want to use it in some other context (say, “accuse Ronald of murder”) then be sure to upgrade to version 2.2, which lets you use of as a preposition and removes the previously hardcoded restriction.

Plural and singular problems

Let’s say you have an object somewhere with a noun of “blinds”. Let’s say you also have another object 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.

Conditional vocabulary

TADS doesn’t permit conditional vocabulary in noun and adjective definitions. It sets up a table of vocabulary words at compile time. So something like this is not legal:

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

However, starting with version 2.2 you can add and delete words at will using the addword() and delword() built-in functions. These don’t let you set up conditional code like the example above, but do let you add and delete vocabulary words at runtime. Very handy feature!

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( Me.location );
   }
 ;

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 =
    {
       if ( Me.location.hasBananaTree )
         return( Me.location );
    }
  ;

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’ runtime 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.

Beware control characters

It seems that accidentally embedding a control character in your TADS source will often cause the compiler to bomb. If you’re getting mysterious compiler errors from a piece of code that looks perfectly legitimate, you might have accidentally typed an invisible control character into your code - maybe your finger slipped off the shift key or something.

In cases like this it’s a good idea to run your source through a filter program to eliminate any possible control characters before trying to figure out why an otherwise reasonable-looking piece of code isn’t working. For example, Macintosh users of BBEdit can use that text editor’s “Zap Gremlins” feature to toast all control characters.

Beware of #pragma C+

Remember that TADS supports two different styles of operators - its own style, and the style used in C. There are some similarities and some differences between these two styles.

TADS lets you choose which style of operator you want. If you include the header “#pragma C+” at the start of each source code file then the compiler will use C style operators. If you don’t include this header or include the header “#pragma C-” then TADS will use its default style.

The thing to beware of is that some examples of TADS coding out there use the traditional style and some the C style. The vast majority of the code I’ve seen uses the traditional style, but I’ve noticed a handful of code examples (usually written by Mike Roberts in his documentation) that use the C style. This can lead to problems if you copy a chunk of code that uses one style operator and paste it into another file that uses the other style.

So, let’s say you see some code that includes this line:

  if ( v == inspectVerb )

That was written in the C style. The traditional TADS operator style would have read like this:

  if ( v = inspectVerb )

Likewise, you might see some code that looks like this:

  if ( self.value != nil )

The traditional TADS way to code that is:

  if ( self.value <> nil )

The other common problem operator is the assignment operator. In the C style it’s this:

  trombone.noiseValue = true;

but in the traditional TADS style it’s this:

  trombone.noiseValue := true;

So be sure to check which operator method the code you’re using is written in. If the code contains the line “#pragma C+” at the start then you know for sure it’s written in the C style. However if it doesn’t have anything then you’re best off checking the code carefully to make sure that it is, in fact, written in the traditional TADS style. Some of the operators are compatible between styles - but others are not and will cause the compiler to choke.

The C style operators are described in detail in the TADS 2.2 Release Notes.


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.x and System 8.x

Don’t forget that TADS can run in the background under System 6.0.x MultiFinder or under System 7.x and 8.x. 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 there’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 break out of endless loops by hitting command-period, like all good Mac programs do.


This file normally lives at:

http://www.tela.bc.ca/tela/tads-manual/tads-tip-sheet.html