Beginnings

A work of Interactive Fiction has to start somewhere, and preferably with something a little more informative than a blank screen with a command prompt. In any case, for the game to work properly you need to define at least a basic set of data about the game and some of its starting conditions. We saw a brief example of this in the section on defining a minimal game, but now we should cover the topic a little more formally and in more depth.

The versionInfo Object

Your game should normally define a versionInfo object (of the GameID class) to:

  1. Specify basic information about your game (its name, author, etc.)
  2. Define the response to informational commands like ABOUT and CREDITS.

The properties and methods of versionInfo you'll typically need to define are:

So, for example, a typical versionInfo definition might look like this:

versionInfo: GameID
    IFID = '0D9D2F69-90D5-4BDA-A21F-5B64C878D0AB'
    name = 'Fire!'
    byline = 'by Eric Eve'
    htmlByline = 'by <a href="mailto:eric.eve@nospam.com">
                  Eric Eve</a>'
    version = '1'
    authorEmail = 'Eric Eve <eric.eve@nospam.com>'
    desc = 'A test game for the adv3lite library.'
    htmlDesc = 'A test game for the adv3lite library.'
    
    showAbout()
    {
        aboutMenu.display();
        
        "This is a demonstration/test game for the adv3Lite library. It should
        be possible to reach a winning solution using a basic subset of common
        IF commands.<.p>";
    }
    
    showCredit()
    {
        "Fire! by Eric Eve\b
        adv3Lite library by Eric Eve with substantial chunks borrowed from the
        Mercury and adv3 libraries by Mike Roberts. ";               
    }
;

In addition, you can override the following settings if you don't like the defaults inherited from GameInfoModuleID:

For further details consult the comments on the GameInfoModuleID in the modid.t file and/or consult the article on "Bibliographical Metadata" in the TADS 3 Technical Manual (although this was written for the adv3 library, everything in the Metadata article should apply equally well to adv3Lite).


The gameMain Object

You game must define a gameMain object, which should be of the GameMainDef class. At a basic minimum it must define the initialPlayerChar property to identify the object that is to represent the player character at the start of the game (typically this is called me, though you can call it anything you like). A minimal gameMain definition will therefore look like this:

gameMain: GameMainDef
   initialPlayerChar = me
;

In practice you'll normally want to define rather more than this on your gameMain object. The other properties and methods you may want to define include:

If you look at the definition of GameMainDef in misc.t you'll see a number of other methods and properties. Some of these, such as newGame(), restoreAndRunGame(filename), setGameTitle() and getSaveDesc(userDesc) are probably left as the library defines them unless you really have a need to override them and you know what you're doing. The rest are not guaranteed to work in the current version of the adv3Lite library and are probably best left alone (they have been left in from the adv3 version of GameMainDef for possible implementation in a later version of adv3Lite).

A fairly typical gameMain definition might thus look something like this:

gameMain: GameMainDef
    initialPlayerChar = me
    
    showIntro()
    {       
        cls();
                        
        george.startFollowing;
        
        "<b><font color='red'>FIRE!</font></b>  You woke up just now, choking
            from the smoke that was already starting to fill your bedroom,
            threw something on and hurried downstairs -- narrowly missing
            tripping over the beach ball so thoughtgfully left on the landing
            by your <i>dear</i> nephew Jason -- you <i>knew</i> having him to
            stay yesterday would be trouble -- perhaps he's even responsible
            for the fire (not that he's around any more to blame -- that's one
            less thing to worry about anyway).\b
            So, here you are, in the hall, all ready to dash out of the house
            before it burns down around you. There's just one problem: in your
            hurry to get downstairs you left your front door key in your
            bedroom.<.p>";
    }
   
;

Notes

When defining a showAboutBox() method you'd typically make it display a string beginning and ending with the <ABOUTBOX> and </ABOUTBOX> tags. A very basic setAboutBox() method that picks up all the relevant text from the versionInfo object might look like this:

  setAboutBox()
    {
        "<ABOUTBOX><CENTER><FONT size=+2 color=red><b><<versionInfo.name>>
        </b></FONT>\b
         <<versionInfo.byline>>\b
        Version <<versionInfo.version>></CENTER></ABOUTBOX>";
    }

For tenses other than past or present, override Narrator.tense to be one of Present ('Bob opens the box'), Past ('Bob opened the box'), Perfect ('Bob has opened the box'), PastPerfect ('Bob had opened the box'), Future ('Bob will open the box'), or FuturePefect ('Bob will have opened the box'). By default the library defines Narrator.tense as (gameMain.usePastTense ? Past : Present).

The showIntro() method is primarily for showing the game's introduction. It may also be a convenient place to put small amounts of start-up code used for initializing the game state (such as starting a Daemon running), but for initialization code you should also consider using PreinitObject and InitObject, which are definined in the System Library, and which you can read about in the "Program Initialization" section of the TADS 3 System Manual.


Defining the Player Character

Every TADS 3 game written with the adv3Lite library must define one object to be the player character (the character from whose viewpoint the game is played). It is possible to change the player character in the course of play, although many if not most games will probably stick to the same player character throughout; in any case a game must define which object is the player character at the start of play, in other words, the initial player character. If you do plan to change the player character during the course of the game, it is probably best to define every object that is going to represent the player character at one time or another as an Actor, especially if the different player characters are going to encounter each other. Otherwise, the player character can perfectly well be defined as a Thing or as a Player object, although provided the actor.t module is present in the build, it is always okay to define the player character as an Actor object. Which Actor, Thing or Player object represents the initial player character can be defined either by setting the initialPlayerChar property on the gameMain object, or by setting isInitialPlayerChar = true on the object in question. If both are done, the initialPlayerChar object defined on the gameMain object will take precedence.

To unpack that somewhat terse summary, we may begin by noting that if you create a new adv3Lite new game using the new Project Wizard in TADS 3 Workbench, the Wizard will create a minimal template game that does all this work for you thus:

#charset "us-ascii"

#include <tads.h>
#include "advlite.h"

versionInfo: GameID
    IFID = '$IFID$'
    name = '$TITLE$'
    byline = 'by $AUTHOR$'
    htmlByline = 'by <a href="mailto:$EMAIL$">$AUTHOR$</a>'
    version = '1'
    authorEmail = '$AUTHOR$ <$EMAIL$>'
    desc = '$DESC$'
    htmlDesc = '$HTMLDESC$'
;

gameMain: GameMainDef
    /* Define the initial player character; this is compulsory */
    initialPlayerChar = me
;


/* The starting location; this can be called anything you like */

startroom: Room 'The Starting Location'
    "Add your description here. "
;

/* 
 *   The player character object. This doesn't have to be called me, but me is a
 *   convenient name. If you change it to something else, rememember to change
 *   gameMain.initialPlayerChar accordingly.
 */

+ me: Thing 'you'   
    isFixed = true       
    person = 2  // change to 1 for a first-person game
    contType = Carrier    
;

Except that the versionInfo object will be filled in with the data you entered in the wizard. This way of declaring the initial player character has a couple of advantages: (1) it makes it clear just by looking at the gameMain object which object is the initial player character (you don't have to go hunting for it further down in the source code; (2) it makes it clear that the player character object is simply a Thing with a few extra properties set and in particular (3) it calls attention to the person property which you may want to change for a first-person or third-person game. If you've created a new game via the Workbench wizard this has no real downside, since the wizard will have done virtually all the work for you.

If you are starting a new game without using the wizard (perhaps because you aren't using Workbench) you can save yourself a small amount of typing by using the Player class instead of the Thing class to define the player character object:

#charset "us-ascii"

#include <tads.h>
#include "advlite.h"

versionInfo: GameID
    IFID = '$IFID$'
    name = 'My Game'
    byline = 'by My name'
    htmlByline = 'by <a href="mailto:$EMAIL$">$AUTHOR$</a>'
    version = '1'
    authorEmail = 'my.name@myemail.org'
    desc = 'My blurb'
    htmlDesc = 'My html blurb'
;

gameMain: GameMainDef
    /* We don't now have to define the initial player character here */    
;


/* The starting location; this can be called anything you like */

startroom: Room 'The Starting Location'
    "Add your description here. "
;

/* 
 *   The player character object. This doesn't have to be called me, but me is a
 *   convenient name. The Player class registers this object as the player character
 *   so you don't need to set gameMain.initialPlayerChar accordingly.
 */

+ me: Player 'you'       
;

This isn't a lot less typing, but you may feel it looks a bit neater and makes it clearer which object is the player character. In terms of what it does, it's effectively identical to the first way of doing things.

Finally, as an alternative to both these ways of defining the player character, you can define it as an Actor (provided actor.t is present in your game build), and either define isInitialPlayerChar = true on that player character Actor or define gameMain.initialPlayerChar to point to it. None of these ways of doing it is intrinsically better to any other (unless you plan to switch the player character in the course of the game, in which case using the Actor class may be your best choice), so which way you do it is simply a matter of personal preference.

Whichever way you choose to define your player character object, you will probably want to customize it at least to the extent of defining its desc property so that your players see a customized response to X ME.