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:
- Specify basic information about your game (its name, author, etc.)
- Define the response to informational commands like ABOUT and CREDITS.
The properties and methods of versionInfo you'll typically need to define are:
- IFID: a random 32-digit hex number to uniquely identify the game; you can generate one at http://www.tads.org/ifidgen/ifidgen
- name: The name of your game, e.g. 'Amazing Quest'
- byline: Your name or pseudonym, e.g. 'by I.F. Author'
- htmlByline: the main author credit as an HTML fragment
- authorEmail: the authors' names and email addresses (in GameInfo format) (e.g. 'I.F.Author <i.author@whatevermail.com>')
- desc: a short blurb describing the game, in plain text format
- htmlDesc: the descriptive blurb as an HTML fragment
- version:the game's version number string
- showAbout(): the method to run in response to an ABOUT command.
- showCredit(): the method to run in response to a CREDITS command.
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:
- releaseDate - the release date string (YYYY-MM-DD)
- licenseType - freeware, shareware, etc.
- copyingRules - summary rules on copying
- presentationProfile - Multimedia, Plain Text
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:
- showIntro(): Show the game's introduction. This routine is called by the default newGame() just before entering the main command loop. The command loop starts off by showing the initial room description,so there's no need to do that here. Most games will want to override this, to show a prologue message setting up the game's initial situation for the player.
- showGoodbye(): Show the "goodbye" message. This is called after the main command loop terminates. It doesn't show anything by default. If you want to show a "thanks for playing" type of message as the game exits, override this routine with the desired text.
- setAboutBox(): Define how the about box should appear (typically in response to selecting Help, About from the menu of an HTML TADS interpreter. For more details on how to do this see the notes below.
- maxScore(): The maximum number of points possible in the game. If the game includes the scoring module at all, and this is non-nil, the SCORE and FULL SCORE commands will display this value to the player as a rough indication of how much farther there is to go in the game. By default, the library initializes this on demand, by calculating the sum of the point values of the Achievement objects in the game. The game can override this if needed to specify a specific maximum possible score, rather than relying on the automatic calculation.
- beforeRunsBeforeCheck: Should the "before" notifications (beforeAction and roomBeforeAction) run before or after the "check" phase? The adv3 library traditionally ran the "before" notifiers first, but the adv3Lite runs the "before" notifiers after the check phase, so in the adv3Lite library the default value is nil. In many ways it's more logical and useful to run "check" first. That way, you can consider the action to be more or less committed by the time the "before" notifiers are invoked. Of course, a command is never truly committed until it's actually been executed, since a "before" handler could always cancel it. But this is relatively rare - "before" handlers usually carry out side effects, so it's very useful to be able to know that the command has already passed all of its own internal checks by the time "before" is invoked - that way, you can invoke side effects without worrying that the command will subsequently fail.
- usePastTense: This can be set to true if you want your game to be narrated in the past tense (it has the effect of making all the library messages appear in the past tense). If it's left at nil, the default, the game will be narrated in the present tense. The adv3Lite library, courtesy of the Mercury code it inherits, is actually capable of narration in six different tenses, but the present and the past are the ones most likely to be use in at least 99% of games. If you want to use one of the other tenses see notes below.
- allVerbsAllowAll: This can be set to nil to prevent the player using ALL (e.g. EXAMINE ALL) with all but a handful of inventory-handling verbs (TAKE, DROP, PUT and DOFF), since some game authors may feel that allowing ALL with every verb allows a sledge-hammer approach to their games. The default value is true.
- useParentheticalListing: If this flag is true (the default is nil) then room description listings and examine listings use a parenthetical style to show subcontents (e.g. "On the table you see a box (in which is a brass key)") instead of showing each item and its contents in a separate paragraph.
- paraBrksBtwnSubcontents: If this flag is true (the default) then room description listings will include a paragraph break between each set of subcontents listings (i.e. the listing of the contents of each item in the room that has visible contents). If it is nil the subcontents listings will all be run into a single paragraph. Note that the global setting defined here can be overridden on individual rooms.
- againRepeatsParse: This controls how the AGAIN command is interpreted. It is nil (the default, representing the previous behaviour of adv3Lite), AGAIN repeats the resolved command, that is, the command as it was interpreted on the previous turn, with the same objects. If it is true, then the text of the previous command is reparsed, which may result in a different interpretation involving different objects. For example, suppose there are two coins on the floor. If againRepeatsParse is nil then TAKE COIN followed by AGAIN will result in one of the coins being taken, followed by an attempt to take the same coin again (which will fail with a message that you're already holding the coin). If againRepeatsParse is true, however, than TAKE COIN followed by AGAIN will result in one coin being taken, and then the other, just as if the player had typed TAKE COIN twice. Which is the better behaviour depends on context; while in the coin example the second interpretation seems better, if a player were to type EXAMINE BOOK and be prompted to say which one, the second interpretation would result in the disambiguation prompt being repeated, which might seem perverse.
- autoSwitchAgain: this tries to give the player the best of both worlds by switching againRepeatsParse between true and nil depending on whether the command to be repeated is the kind of command it would make sense to repeat with the same objects (if it isn't, the command is reparsed in the hope of finding a more sensible match). This is far from absolutely foolproof, and should be regarded as experimental. It is true (switched on) by default, but game authors may wish to set it to nil if it proves troublesome.
- verbose: flag - is this game in verbose mode (full room descriptions are shown every time) or in brief mode (full room descriptions are shown only on the first visit or in response to an explicit LOOK command). By default verbose is true, but players can change it using the commands BRIEF and VERBOSE.
- fastGoTo. By default fastGoTo is nil, but if it is set to true then the GO TO command will move the player character continuously to his/her destination (unless some obstacle stops the journey) without the player needing to use the CONTINUE command.
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.