Macros
Introduction — Convenient Abbreviations
A macro is a kind of convenient abbreviation. Put more technically, it is a string of text that the TADS 3 preprocessor replaces with another (usually more elaborate) string of text before the source file is passed to the compiler for compilation, so that the compiler "sees" the text that has replaced the macro. This can be used to save typing as well as easing the job of coding by allowing game authors to use a relativelty short and memorable name for something in place of a longer and more complex expression it may be harder to remember.
TADS 3 has no concept of a global variable (that is a variable with a name like turnCount) accessible from any part of game code. Instead global valuables have to be stored as object properties, such as libGlobal.totalTurns. This may be a little tricky to remember and a little long-winded to type, so the adv3Lite library defines the macro gTurns which can be used in its place. Similarly the library defines the macro gRoom as convenient shorthand for (gPlayerChar.getOutermostRoom) as a convenient way to refer to the room in which the player character is currently located (which is not necessarily the same as the player character's location if the player character is in a nested room such as a bed or chair). This makes absolutely no difference to the compiler, but it can make it quite a bit easier for you to write and read your code.
A second kind of macro found in the adv3Lite library resembles a function (and in some, though not all cases, could have been implemented as a function). For example, when a verify routine calls illogical(msg)), this is in fact a macro, which in this case expands to gAction.addVerifyResult(new VerifyResult(30, msg, nil, self)), an expression it would once again be trickier to type and less clear to read. This could have been written as a function, although in theory the macro should be slightly more efficient at run-time (since it avoids the overhead of the function call, probably minimal in practice), but there are some function-like macros that can do things a function call could not. For example, goInstead is syntactic sugar for doInstead(go, northDir), avoiding the need to add "Dir" to the end of the direction name. This macro is defined as goInstead (dirn) doInstead(Go, dirn##Dir), which makes use of token-pasting (via the ## synmbol) to add "Dir" to the end of the parameter name passed to it, something a function call could not have done.
A third kind of macro used in adv3Lite executes a command and might resemble a reserved in the TADS 3 programming language, at least in appearance. For example, the macro exit superficially resembles the reserved word break in the TADS 3 language, but while break is a keyword used to break out of certain programming structures, exit is a macro that expands to throw new ExitSignal(). This is something most game authors won't need to worry about too much under most circumstances, but it may occasionally be helpful to be aware of the difference.
A fourth kind of macro used in adv3Lite is used to create or defined certain library-specific programming structures as dobjFor(Whatever) or DefineTIAction(Whatever), again in ways that can save quite a bit of typing and make the code more readable.
The fifth kind of macro is the one that's simply an alias for a numerical value. This isn't an abbreviation, but rather more of an aid to readibility. For example, the macro TypeCode (intended as a possible return value of propType() or dataType()) evaluates to 11. While the meaning of myObj.propType(&myprop) == 11 would be quite obscure, myObj.propType(&myprop) == TypeCode is a good deal clearer (and a good deal easier to remember if you want to test whether a particular property or value contains code).
Most of the time you won't need to know too much about what's going on under the hood with these macros, provided you know how to use them. The use of the most common ones has been covered in earlier chapters. For a complete list of all the macros defined in the adv3Lite library, refer to the Macros tab of the adv3Lite Library Reference Manual. One thing you do need to knowm though, is that none of these macros will work unless they're defined in whichever source file you're working on. This is why you need to include advlite.h in each of your game source files, along with tads.h:
#charset "us-ascii" #include <tads.h> #include "advlite.h"
Some Useful g Macros
As atated above, a complete list of adv3Lite macros can be found in the adv3Lite Library Reference Manual, so there is no need to reproduce it here. Instead we give a list of some of the more useful macros with names beginning g (because they often mimic global variables, or at least global values that may be of use to game authors):
- gAcc: gAction.curAobj - the current auxiliary object of a TIA Action
- gAction: (libGlobal.curAction) - the current action
- gActionIn (action, ...): (gAction != nil \ && (action#foreach/gAction.ofKind(action)/||/)) - is the current global action ANY of the specified actions?
- gActionIs:((gAction != nil && gAction.ofKind(action)) - Determine if the current global action is the specified action.
- gActionList: (nilToList(gCommand.action.actionList)) - the list of (dirrect) objects involved in the action just completed.
- gActionListObj - an object that is singular or plural according to whether gActionList represents a single object or a plurality of objects, and which picks up the correct gender if there is only a single object.
- gActionListStr: makeListStr(gCommand.action.reportList, &theName) a string version of gActionList.
- gActor: (libGlobal.curActor) - the actor performing the current command.
- gActorRoom: (gActor.getOutermostRoom) - the room containing the actor performing the current command.
- gAobj: gAction.curAobj - the auxiliary object of the current TIA Action.
- gCommand: (libGlobal.curCommand) - the current Command object.
- gCommandToks:(gCommand == nil || gCommand.verbProd == nil ? [] \ : gCommand.verbProd.tokenList.mapAll({t: getTokVal(t)})) - Get the command tokens for the current command (i.e., the words entered by the player as a list of strings). See also gVerbPhrase.
- gDobj: (gAction.curDobj) - the direct object of the current action.
- gExitLister: (libGlobal.exitListerObj) the exit lister object - if the exits module isn't included in the game, this will be nil.
- gExtraHintManager: (libGlobal.extraHintManagerObj) - the extra hint manager object - if the hints module isn't included in the game, this will be nil.
- gHintManager: (libGlobal.hintManagerObj) - the hint manager object - if the hints module isn't included in the game, this will be nil.
- gInformed(key): (getActor.informedAbout(key)) - has a topic key been revealed to an NPC through <.inform>?
- gIobj: (gAction.curIobj) - the current indirect object of the current action.
- gLastAction: (libGlobal.lastAction) - the previous action.
- gLastCommand: (libGlobal.lastCommand) - the previous command (as a Command object).
- gLibMessages: (libGlobal.libMessageObj) - The current library messages object. This is the source object for messages that don't logically relate to the actor carrying out the comamand. It's mostly used for meta-command replies, and for text fragments that are used to construct descriptions.
- gLiteral: (gAction.literal) - the text of the literal string entered by the player for a LiteralAction or LiteralTAction.
- gLocation: (gPlayerChar.location) - get the player character's location; note that this is not necessarily the same as the player character's current room, for which use gRoom.
- gMessageParams(var, ...): (gAction.setMessageParams(var#foreach/#@var, var/,/)) - allows you to define your own temporary Message Substitution Parameters.
- gNumber: (gAction.num) - the numerical value entered by the player in a NumericAction or NumericTAction.
- goInstead(dirn): doInstead(Go, dirn##Dir) - replacedthe current action with travel in the direction specified, e.g. goInstead(north).
- goNested(dirn): doNested(Go, dirn##Dir) - like goInstead(dirn) except that the travel action is carried out as part of the current action instead of replacing it.
- gOutStream: (outputManager.curOutputStream) - the current OutputStream object.
- gPlayerChar: (libGlobal.playerChar) - the current player character object.
- gReveal(key): (libGlobal.setRevealed(key)) reveal a topic key, as though through <.reveal>.
- gRevealed(key): (libGlobal.revealedNameTab[key] != nil) - has a topic key been revealed through <.reveal>?
- gRoom: (gPlayerChar.getOutermostRoom) - get the player character's current room (which may or may not be the same as their location).
- gSetKnown(obj): (gPlayerChar.setKnowsAbout(obj)) - mark a Topic/Thing as known by the player character.
- gSetSeen(obj): (gPlayerChar.setHasSeen(obj)) - mark a Thing as seen by the player character.
- gTentativeDobj: (gCommand.dobjs.mapAll({x: x.obj}).toList) - a list of the possible direct objects of the current action, before the direct object has been fully resolved (for use in the verify routine in the iobjFor() block of a TIAction where the indirect object is resolved first).
- gTentativeDobjIn(lst): (gTentativeDobj.overlapsWith(lst)) - An alternative way of dealing with the potential object not yet resolved problem, by testing whether any of the objects in lst are in the Tentative Object List.
- gTentativeIobj: (gCommand.iobjs.mapAll({x: x.obj}).toList) - a list of the possible indirect objects of the current action, before the direct object has been fully resolved (for use in the verify routine in the dobjFor() block of a TIAction where the direct object is resolved first).
- gTentativeIobjIn(lst): (gTentativeIobj.overlapsWith(lst)) - An alternative way of dealing with the potential object not yet resolved problem, by testing whether any of the objects in lst are in the Tentative Object List.
- gTopic: (gAction.curTopic) - the ResolvedTopic object associated with the current TopicAction or TopicTAction.
- gTopicMatch: (gTopic.getBestMatch) - the Thing or Topic object that's the best match to the topic entered by the player in the current TopicAction or TopicTAction.
- gTopicText: (gTopic.getTopicText) - the topic text entered by the player for the current TopicAction or TopicTAction.
- gTravelActionIs(dirn): (gAction != nil && gAction.ofKind(Travel) && gAction.direction == dirn ## Dir) - tests whether the current action is a Travel action going dirn.
- gTurns: (libGlobal.totalTurns) - get the current turn count.
- gUnreveal(key): (libGlobal.setUnrevealed(key)) - remove a topic key, as though through <.unreveal>.
- gVerbPhrase: (gCommand.getCommandPhrase()) - Get the command phrase for the current command.This will be in a form in which the actual object names replaced by place-holders: '(dobj)' for the direct object and '(iobj)' for the indirect object. So if the command were ATTACK BIG JIM WITH SHARP SWORD, gVerbPhrase would be 'attack (dobj) with (iobj)'. This can provide additional useful information if the meaning of the command depends on more than one word (e.g. PUT ON versus PUT IN).
- gVerbWord: (gCommand == nil || gCommand.verbProd == nil ? '' \ : getTokVal(gCommand.verbProd.tokenList[1])) - Get the first word the player entered for the current command.
- gVerifyDobj: (gDobj ?? (gTentativeDobj.length > 0 ? gTentativeDobj[1] : failVerifyObj)) - the parser's best guess at the direct object of the current command (for use in the verify routine in the iobjFor() block of a TIAction where the indirect object is resolved first).
- gVerifyIobj: (gIobj ?? (gTentativeIobj.length > 0 ? gTentativeIobj[1] : failVerifyObj)) - the parser's best guess at the indirect object of the current command(for use in the verify routine in the dobjFor() block of a TIAction where the iirect object is resolved first).