This file is part of the TADS 2 Author’s Manual.
Copyright © 1987 - 2002 by Michael J. Roberts. All rights reserved.
The manual was converted to HTML and edited by NK Guy, tela design.


Chapter One


A Sample Game

This section introduces many of the concepts of writing a TADS program by explaining how a simple example works. The chapter starts with a very basic TADS program, to demonstrate the general structure of a game, then expands it to show more features of the language. Although the example is very simple, it is a complete, working TADS program.

We are starting with a sample game because it provides an overview of how a TADS program fits together. When you are reading later sections, which describe the elements of the language in detail, it may be helpful to have an idea of where the details fit into the general structure of a game. This chapter should help provide that overview.


A Very Simple Game

We’ll start with about the simplest game possible: two rooms, and no objects. (We could conceivably start with only one room, to make things even simpler, but then there would be nothing to do while playing the game; with two rooms, we at least can move between them.)

If you want to try running this game, create a file with the program text as shown below using your text editor or word processor. The TADS Compiler will accept an ASCII file produced with any editor.

  /* This is a comment, just like in C */
  #include <adv.t>                  /* read generic adventure game "adv.t" */
  #include <std.t>                          /* read standard underpinnings */
  startroom: room                 /* the game always starts in "startroom" */
    sdesc = "Outside cave"          /* the "Short DESCription" of the room */
    ldesc = "You're standing in the bright sunlight just
    outside of a large, dark, foreboding cave, which
    lies to the north. "
    north = cave               /* the room called "cave" lies to the north */
  ;
  cave: room
    sdesc = "Cave"
    ldesc = "You're inside a dark and musty cave. Sunlight
    pours in from a passage to the south. "
    south = startroom
  ;

To run this example, all you have to do is compile it with tc, the TADS Compiler, and run it with tr, the TADS Run-time system. If you named your sample program sample.t, on most operating systems you can compile your game by typing this:

  tc sample 

and you can run it by typing this:

  tr sample 

Those two commands - tc and tr - are the only operating system commands you’ll need to learn to write games, apart from the commands you use to run your text editor and operating system utilities.

Now we’ll walk through the sample game line by line.

The #include command inserts another source file into your program. The file called adv.t is a set of basic definitions common to most adventures. You should be able to use these definitions, with few changes, for most adventure games. By including adv.t in your game, you don’t need to worry about definitions for basic words such as “the,” a large set of verbs (such as “take,” “north,” and so forth), and many object classes (more on these in a bit).

Note one critical detail - if you copied and pasted the text into your word processor or text editor, be absolutely certain that the #include command has no space characters in front of it. The # must be the first symbol on the line.

The line including std.t is similar; it contains some additional standard definitions. The reason for placing these definitions in a separate file is that they will usually be customized in a finished game. When you’re in the final stages of polishing your game, you’ll usually want to remove the std.t definitions and use your own (for example, you will probably replace the init function in std.t with your own version which displays an introductory message appropriate for your game). However, the definitions in std.t are good enough to let you get started with a game.

The line that says startroom: room tells the compiler that you’re going to define a room named “startroom”. Now, a room is nothing special to TADS, but the file adv.t that you included defines what a room is. A room is one of those object classes we mentioned. The next line defines the sdesc for this room. An sdesc is a short description; for a room, it is normally displayed whenever a player enters the room. The ldesc is the long description; it is normally displayed the first time a player enters the room, and can be displayed by the player by typing “look”. Finally, the north definition says that another room, called cave, is reached when the player types “north” while in startroom.

A bit of terminology: startroom and cave are objects, belonging to the class room; sdesc, ldesc, north, and the like are properties of their respective objects. In the context of TADS programming, an object is a named entity which is defined like startroom; each object has a class, which defines how the object behaves and what kind of data it contains. Note that our usage is sometimes a little loose, and we will also use “object” the way a player would, to refer to something in the game that a player can manipulate. In fact, each item that the player thinks of as an object is actually represented by a TADS object (sometimes several, in fact); but your TADS program will contain many objects that the player doesn’t directly manipulate, such as rooms.

If you’re familiar with other programming languages, you may notice that the program above appears to be entirely definitions of objects; you may wonder where the program starts running. The answer is that the program doesn’t have a beginning, exactly.

TADS is a different style of programming than you have encountered before; this new style may take a little getting used to, but you’ll find that it is quite powerful for writing adventure games and simplifies the task considerably. Most programming languages are “procedural”; you specify a series of steps that the computer executes in sequence. TADS, on the other hand, is more “declarative”; you describe objects to the system. While TADS programs usually have procedural sections, in which steps are executed in sequence, the overall program doesn’t have a beginning or an end.

The reason TADS programs aren’t procedural is that the player is always in control of the game. When the game first starts, the system calls a bit of procedural code in your program that displays any introductory text you wish the player to see, then the system waits for a command from the player. Based on the command, the system will manipulate the objects you defined according to how you declare these objects to behave. You don’t have to worry about what the player types; you just have to specify how your objects behave and how they interact with one another.


Adding Items to the Game

Now let’s add a few items to the game that can be manipulated by the player, so he or she can do something besides walk back and forth between our two rooms. We’ll add a solid gold skull, and a pedestal for it to sit upon.

  pedestal: surface, fixeditem
    sdesc = "pedestal"
    noun = 'pedestal'
    location = cave
  ;
  goldSkull: item
    sdesc = "gold skull"
    noun = 'skull' 'head'
    adjective = 'gold'
    location = pedestal
  ;

Here we’ve defined two objects, pedestal and goldSkull.

The pedestal belongs to two classes, surface and fixeditem. This means that it has attributes of both classes; when there’s a conflict, the surface class takes precedence, because it’s first in the list of classes. Objects of the surface class can have other objects placed on top of them; objects of the fixeditem class can’t be carried. Note that the surface class has a default ldesc property which lists any objects on the surface.

The goldSkull belongs to the item class, which is the generic class for objects without any special properties.

Since these objects can be manipulated directly by the player, the player needs words to refer to them. This is what the noun and adjective properties define. All objects that the player can manipulate must have at least one noun. Note that the goldSkull has two nouns; they are simply listed with a space between them. Objects can also have adjectives; these serve to distinguish between objects which have the same noun, but are otherwise optional. A good game will recognize all of the words it uses to describe an object, so if you describe the skull as a “gold skull,” you should understand it when the player says “take the gold skull.”

This brings us to a subtlety. Notice that the ldesc and other properties use double quotes around their strings, but the noun and adjective properties have single quotes. Vocabulary words (nouns, adjectives, verbs, prepositions, and articles) always use single quotes. Almost everything else will use double quotes. The distinction is that a string enclosed in double quotes is displayed immediately every time it is evaluated, while a string enclosed in single quotes is a string value that can be manipulated internally. Double-quoted strings are displayed automatically as a convenience, since most strings in text adventures are displayed without further processing. (Note that the double quote mark is a separate character on the keyboard, and is not simply two single quote marks.)

These two objects have another new property, location. This simply defines the object that contains the object being defined. In the case of the pedestal, the containing object is the cave room; since the goldSkull is on the pedestal, its location is pedestal. (Note that the system makes no distinction, at this level, between an object being inside another object and the object being on another object. This means that an object can’t be both a surface and a container.)


Making the Items Do Something

The game is still rather bland; it has no puzzles. So, let’s introduce a small puzzle. Let’s assume that the gold skull wasn’t merely left laying around; instead, whoever left it there arranged for a trap to go off if it should be lifted off the pedestal. To implement this, we need to add a method to the goldSkull object. A method is a special type of property which contains code to be executed; it is very much like a function in C or Pascal. The new goldSkull with the method looks like this:

  goldSkull: item
    sdesc = "gold skull"
    noun = 'skull' 'head'
    adjective = 'gold'
    location = pedestal
    doTake( actor ) =
    {
      "As you lift the skull, a volley of poisonous
      arrows is shot from the walls! You try to dodge
      the arrows, but they take you by surprise!";
      die();
    }
  ;

The method doTake (which stands for “direct object take”) takes as an argument the character who is trying to take the object (since we have no characters besides the player in this game yet, actor will be the player’s character, named Me.) Here, we’ve simply defined it to display a message (since the message is enclosed in double quotes, it is displayed immediately upon being evaluated), then calls a special function called die. (The parentheses following die tell TADS that you would like to call die as a function.)

You should note that we didn’t just pick the name doTake out of thin air. The doTake method in the goldSkull object is called by TADS when the player types a “take” command with goldSkull as the direct object. Each verb the player types results in the system calling particular methods in the object or objects named in the command. The naming of these methods is described in detail later in this manual.

You might wonder why we didn’t need a doTake method in our original definition of goldSkull, or you might have assumed that the system automatically knows what to do if no doTake is defined for an object. In fact, all objects do need a doTake method, and the system doesn’t automatically know anything about it. However, since practically every object has the same doTake, with a few exceptions such as goldSkull, it would be extremely tedious to type a doTake method for every object in the game. Instead, we use something called “inheritance.” By defining the goldSkull to be a member of the item class, you tell TADS that goldSkull “inherits” all of the definitions for item, in addition to any definitions it makes on its own. The item class, which appears in the adv.t file included at the beginning of the program, defines a doTake method, so anything that is defined to be an item inherits that definition. However, if something is defined in both item and goldSkull, as doTake is in this example, the definition in goldSkull takes precedence - it “overrides” the inherited method.

We actually don’t have a very good puzzle here, because there’s no way to take the gold skull without dying. So, let’s put a rock on the cave floor:

  smallRock: item
    sdesc = "small rock"
    noun = 'rock'
    adjective = 'small'
    location = cave
  ;

Now, let’s change the doTake method of the goldSkull.

  doTake( actor ) =
  {
    if ( self.location<>pedestal or        /* am I off the pedestal? */
    smallRock.location=pedestal )           /* or is the rock there? */
      pass doTake;                            /* yes - take as usual */
    else                                  /* no - the trap goes off! */
    {
      "As you lift the skull, a volley
      of poisonous arrows is shot from
      the walls! You try to dodge the
      arrows, but they take you by surprise!";
      die();
    }
  }

This new doTake first checks to see if the object being taken (the special object self, which is the object to which the message doTake was originally sent), which in this case is the gold skull, is already off the pedestal; if it is, we don’t want anything to happen, so we pass the doTake message. We also pass the message if the small rock is on the pedestal. When we pass the message, the doTake method that we inherit from our parent class (in this case, item) is invoked. This allows us to override a method only under certain special circumstances, and otherwise do business as usual. If we don’t satisfy one of these two requirements, the volley of poisonous arrows is released as before.

So, the solution to the puzzle is to put the rock on the pedestal before taking the skull, thereby fooling the pedestal into thinking the skull is still there.

This should give you some idea of how a TADS program looks. In later sections, we’ll cover the details of the language, and explore all of the classes defined in adv.t. You should note that the source code to a much more complex sample game is included with the TADS software; you may find it useful to refer to that sample while reading the rest of this manual for examples of the language constructs. You may also find that parts of the sample game can provide a model for objects in your own games. Also, note that chapter six describes the implementation process in somewhat more detail, and shows you how to build a larger sample game; you may want to look at chapter six after you’ve become a little more familiar with TADS.

For an expanded version of this basic demonstration game, take a look at The Golden Skull. This is a demo game for multimedia TADS, and uses the sample code in this chapter as a starting point.




Example is always more efficacious than precept.
SAMUEL JOHNSON, Rasselas (1759)

Preface Table of Contents Chapter Two