advlite.h

documentation
#charset "us-ascii"

/* 
 *   Copyright (c) 2000, 2006 Michael J. Roberts.  All Rights Reserved. 
 *   
 *   TADS 3 Library - main header
 *   
 *   This file provides definitions of macros, properties, and other
 *   identifiers used throughout the library and in game source.
 *   
 *   Each source code file in the library and in a game should generally
 *   #include this header near the top of the source file.  
 */

#ifndef ADV3LITE_H
#define ADV3LITE_H

/* ------------------------------------------------------------------------ */
/*
 *   Include the system headers that we depend upon.  We include these here
 *   so that each game source file will pick up the same set of system
 *   headers in the same order, which is important for intrinsic function
 *   set definitions.  
 */
#include <tads.h>
#include <tok.h>
#include <t3.h>
#include <vector.h>
#include <strbuf.h>
#include <file.h>
#include <dict.h>
#include <bignum.h>
#include <gramprod.h>
#include <strcomp.h>


/* ------------------------------------------------------------------------ */
/*
 *   Turn on sourceTextGroup property generation in the compiler.  (This lets
 *   us determine which module defined an object, and the order of the module
 *   in the overall project build.  We use this for determining the
 *   precedence of certain items based on their definition order in the
 *   source code.)  
 */
#pragma sourceTextGroup(on)
/* ------------------------------------------------------------------------ */

/* ------------------------------------------------------------------------ */
/*
 *   The '+' property sets an object's location to the lexically preceding
 *   object. 
 */
+ property location;


/*
 *   The VerbRule macro starts the definition of a verb grammar rule.  The
 *   tag is just an identifying name for the rule, so that you can refer to
 *   it with 'replace' or 'modify'.  
 */
#define VerbRule(tag)  grammar predicate(tag):

/*
 *   Verb rule noun phrase macros.  These are convenience macros for
 *   specifying the most common noun phrase slots in the grammar templates
 *   for verb rules.  
 */
#define singleDobj     singleNoun->dobjMatch
#define singleIobj     singleNoun->iobjMatch
#define singleAcc      singleNoun->accMatch
#define singleAobj     singleNoun->accMatch
#define multiDobj      nounList->dobjMatch
#define multiIobj      nounList->iobjMatch
#define accList        nounList->accMatch
#define aobjList       nounList->accMatch
#define multiAcc       nounList->accMatch
#define multiAobj      nounList->accMatch
#define numberDobj     numberPhrase->dobjMatch
#define topicDobj      topicPhrase->dobjMatch
#define topicIobj      topicPhrase->iobjMatch
#define topicAcc       topicPhrase->accMatch
#define topicAobj      topicPhrase->accMatch
#define literalDobj    literalPhrase->dobjMatch
#define literalIobj    literalPhrase->iobjMatch
#define literalAcc     literalPhrase->accMatch
#define literalAobj    literalPhrase->accMatch
#define singleDir      directionName->dirMatch
#define numericDobj    numberPhrase -> dobjMatch
#define numericIobj    numberPhrase -> iobjMatch
#define numericAcc     numberPhrase -> accMatch
#define numericAobj    numberPhrase -> accMatch

/* Also add a couple of synonyms familiar froma adv3 */
#define dobjList      nounList->dobjMatch
#define iobjList      nounList->iobjMatch

/* ------------------------------------------------------------------------ */
/*
 *   Establish the default dictionary for the game's player command parser
 *   vocabulary.
 */
dictionary cmdDict;

/*
 *   The standard parts of speech for the dictionary.
 */
dictionary property noun, nounApostS;


/* ------------------------------------------------------------------------ */
/*
 *   Flags for matching noun phrases during parsing.  These are bit flags, so
 *   they can be combined with '|' (bitwise OR).
 *   
 *   Note that the arithmetic values also matter: the values are in order of
 *   priority for noun phrase matches.  That is, a higher arithmetic value is
 *   a better match.  The best match is one with no truncation and no
 *   approximation, so that will have the highest arithmetic value when you
 *   combine those two bit flags.  The next best is character approximation
 *   but no truncation - truncation is more important than approximation, so
 *   it has a higher arithmetic value.  
 */

/* matched a preposition (the phrase contains at least one preposition) */
#define MatchPrep      0x0001

/* 
 *   matched a weak token (which we'll treat as equivalent to matchimg a
 *   preposition).
 */
#define MatchWeak      0x0001

/* matched an adjective (the phrase contains at least one adjective) */
#define MatchAdj       0x0002

/* matched a noun (the phrase contains at least one noun word) */
#define MatchNoun      0x0004

/* matched a plural (the phrase contains at least one plural word) */
#define MatchPlural    0x0008

/* matched a phrase */
#define MatchPhrase    0x0010 

/* mask to select only the part-of-speech flags */
#define MatchPartMask  0x0FFF


/* 
 *   all words were matched WITHOUT character approximations (such as
 *   matching 'a' to 'a-umlaut') 
 */
#define MatchNoApprox  0x1000

/* all words were matched WITHOUT truncation */
#define MatchNoTrunc   0x2000

/* mask to select only the match-strength flags */
#define MatchStrengthMask  0xF000

/* ------------------------------------------------------------------------ */
/*
 *   Flags for object selection during parsing.  These flags reflect how well
 *   an object matched a noun phrase during the resolution process.  
 */

/* 
 *   The noun phrase required disambiguation, because more than one object
 *   was in scope that matched the noun phrase.  We were able to figure out
 *   which one(s) the player meant from context, without having to ask the
 *   player for help.
 *   
 *   For example, there are two doors in the room, one open and one closed.
 *   The player types OPEN DOOR.  It's fairly obvious that they must be
 *   talking about the closed door.  So, we choose that object and set the
 *   Disambig flag.
 *   
 *   (Note that this flag specifically does NOT mean that we had to ask the
 *   user for help with the dreaded "Which do you mean..." question.  It's
 *   kind of the opposite: it means that we had a noun phrase that was
 *   initially ambiguous, but that we managed to disambiguate it on our own.
 *   When we get the user involved, there's ambiguity *before* we ask the
 *   question, but the user's response removes the ambiguity by telling us
 *   exactly which alternative they intended.  This flag indicates that we
 *   made an educated guess about what the user must have intended, without
 *   asking.  The flag lets the parser tell the player about the guess, which
 *   is desirable because the guess is sometimes wrong.  
 */
#define SelDisambig         0x0001

/*
 *   This object was chosen arbitrarily from a larger set, because the noun
 *   phrase construction says we should do so.  This flag is set when the
 *   noun phrase is something TAKE A BOOK or TAKE ANY OF THE BOOKS.  
 */
#define SelArbitrary        0x0002

/*
 *   The noun phrase that we matched was a manifestly plural construction,
 *   such as TAKE ALL or TAKE THE BOOKS.  
 */
#define SelPlural           0x0004

/*
 *   We selected an object as a default.  This is set when the player leaves
 *   out a noun phrase, but we can guess what was probably meant based on
 *   context: e.g., ASK ABOUT THE HOUSE is probably directed to Bob if Bob is
 *   the only person nearby.  
 */
#define SelDefault          0x0008

/*
 *   This object was set internally by the program; it did not come from
 *   parsing any player input.  This generally means that the whole command
 *   was constructed by the program, to handle an event or other internal
 *   processing, rather than by parsing player input.  
 */
#define SelProg             0x1000

/* ------------------------------------------------------------------------ */
/*
 *   readMainCommandTokens() phase identifiers.  We define a separate code
 *   for each kind of call to readMainCommandTokens() so that we can do any
 *   special token processing that depends on the type of command we're
 *   reading.
 *   
 *   The library doesn't use the phase information itself for anything.
 *   These phase codes are purely for the author's use in writing
 *   pre-parsing functions and for differentiating prompts for the different
 *   types of input, as needed.
 *   
 *   Games that read additional response types of their own are free to add
 *   their own enums to identify the additional phases.  Since the library
 *   doesn't need the phase information for anything internally, it won't
 *   confuse the library at all to add new game-specific phase codes.  
 */

/* reading a normal command line */
enum rmcCommand;

/* reading an unknown word response, to check for an "oops" command */
enum rmcOops;

/* reading a response to a prompt for a missing object phrase */
enum rmcAskObject;

/* reading a response to a prompt for a missing literal phrase */
enum rmcAskLiteral;

/* reading a response to an interactive disambiguation prompt */
enum rmcDisambig;


/* ------------------------------------------------------------------------ */
/* 
 *   A couple of utility macros we use internally for turning macro values
 *   into strings.  STRINGIZE(x) expands any macros in its argument and then
 *   turns the result into a single-quoted string, which can then be used in
 *   regular program text or in directives that evaluate constant
 *   expressions, such as #if.  (STRINGIZE is the real macro; _STRINGIZE is
 *   needed to force expansion of any macros in the argument, which is
 *   required because of the weird ANSI C expansion-order rules, and which
 *   works because of same.)  
 */
#define _STRINGIZE(x) #@x
#define STRINGIZE(x)  _STRINGIZE(x)




/* ------------------------------------------------------------------------ */
/*
 *   Parser global variables giving information on the command currently
 *   being performed.  These are valid through doAction processing.  These
 *   should never be changed except by the parser.
 */

/* the actor performing the current command */
#define gActor (libGlobal.curActor)

/*
 *   For convenience, define some macros that return the current direct and
 *   indirect objects from the current action.  The library only uses direct
 *   and indirect objects, so games that define additional command objects
 *   will have to add their own similar macros for those.  
 */
#define gDobj (gAction.curDobj)
#define gIobj (gAction.curIobj)
#define gLiteral (gAction.literal)
#define gNumber (gAction.num)


/* the Action object of the command being executed */
#define gAction (libGlobal.curAction)
#define gCommand (libGlobal.curCommand)

#define gTentativeDobj (gCommand.dobjs.mapAll({x: x.obj}).toList)
#define gTentativeIobj (gCommand.iobjs.mapAll({x: x.obj}).toList)

/* 
 *   the probable Action objects of the command being executed for use in verify routines when
 *   resolution of both objects may not be complete. If the gDobj or gIobj is non-nil, use that,
 *   otherwise use the first object in the tentative resolution list if the list has any items,
 *   otherwisse evaluate to failVerifyObj.
 */
#define gVerifyDobj (gDobj ?? (gTentativeDobj.length > 0 ? gTentativeDobj[1] : failVerifyObj))
#define gVerifyIobj (gIobj ?? (gTentativeIobj.length > 0 ? gTentativeIobj[1] : failVerifyObj))

/* 
 *   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.
 */
#define gTentativeDobjIn(lst)  (gTentativeDobj.overlapsWith(lst))
#define gTentativeIobjIn(lst)  (gTentativeIobj.overlapsWith(lst))

/* The previous action and command */
#define gLastAction (libGlobal.lastAction)
#define gLastCommand (libGlobal.lastCommand)

/*
 *   Determine if the current global action is the specified action.  Only
 *   the action prefix is needed - so use "Take" rather than "TakeAction"
 *   here.
 *   
 *   This tests to see if the current global action is an instance of the
 *   given action class - we test that it's an instance rather than the
 *   action class itself because the parser creates an instance of the
 *   action when it matches the action's syntax.  
 */
#define gActionIs(action) \
    (gAction != nil && gAction.ofKind(action))

/* is the current global action ANY of the specified actions? */
#define gActionIn(action...) \
    (gAction != nil \
     && (action#foreach/gAction.ofKind(action)/||/))

/* is the current action a Travel action going dirn */
#define gTravelActionIs(dirn) \
  (gAction != nil && gAction.ofKind(Travel) && gAction.direction == dirn ## Dir)

/* the list of objects involved in the action just completed */
#define gActionList (nilToList(gCommand.action.actionList))

/* a displaying string version of the above */
#define gActionListStr makeListStr(gCommand.action.reportList, &theName)

/* 
 *   an object that is singular or plural according to whether gActionList
 *   represents a single object of a plurality of objects, and which picks
 *   up the correct gender if there is only a single object.
 */

#define gActionListObj (object: Thing \
             { \
                 plural = (gAction.reportList.length > 1 || \
                           gAction.reportList[1].plural); \
                 isIt = (gAction.reportList.length == 1 ? \
                    gAction.reportList[1].isIt : nil);\
                 isHim = (gAction.reportList.length == 1 ? \
                    gAction.reportList[1].isHim : nil);\
                 isHer = (gAction.reportList.length == 1 ? \
                    gAction.reportList[1].isHer : nil);\
                 name = gActionListStr; \
                 qualified = true; \
             } )


/* ------------------------------------------------------------------------ */
/*
 *   Miscellaneous macros
 */

/* get the current player character Actor object */
#define gPlayerChar (libGlobal.playerChar)

/* get the current player character object even if the game hasn't yet initialized gPlayerChar */
#define gSafePlayerChar ((gPlayerChar ?? gameMain.initialPlayerChar) ?? findPlayerChar())

/* get the player character's location */
#define gLocation (getPlayerChar().location)

/* get the current turn count */
#define gTurns (libGlobal.totalTurns)

/* 
 *   get the player character's current room (which may or may not be the same
 *   as his/her location)
 */
#define gRoom (getPlayerChar().getOutermostRoom)

/* Get the current actor's curren room (which may not be the same as its location) */
#define gActorRoom (gActor.getOutermostRoom)

#define objFor(which, action) propertyset '*' ## #@which ## #@action

#define dobjFor(action) objFor(Dobj, action)
#define iobjFor(action) objFor(Iobj, action)
#define gTopic (gAction.curTopic)
#define gTopicText (gTopic.getTopicText)
#define gTopicMatch (gTopic.getBestMatch)

#define reportAfter(msg) gCommand.afterReports += msg

#define sLoc(which) subLocation = &remap##which


/*
 *   Treat an object definition as equivalent to another object definition.
 *   These can be used immediately after a dobjFor() or iobjFor() to treat
 *   the first action as though it were the second.  So, if the player types
 *   "search box", and we want to treat the direct object the same as for
 *   "look in box", we could make this definition for the box:
 *   
 *   dobjFor(Search) asDobjFor(LookIn)
 *   
 *   Note that no semicolon is needed after this definition, and that this
 *   definition is completely in lieu of a regular property set for the
 *   object action.
 *   
 *   In general, a mapping should NOT change the role of an object:
 *   dobjFor(X) should not usually be mapped using asIobjFor(Y), and
 *   iobjFor(X) shouldn't be mapped using asDobjFor(Y).  The problem with
 *   changing the role is that the handler routines often assume that the
 *   object is actually in the role for which the handler was written; a
 *   verify handler might refer to '{dobj}' in generating a message, for
 *   example, so reversing the roles would give the wrong object in the role.
 *   
 *   Role reversals should always be avoided, but can be used if necessary
 *   under conditions where all of the code involved in the TARGET of the
 *   mapping can be carefully controlled to ensure that it doesn't make
 *   assumptions about object roles, but only references 'self'.  Reversing
 *   roles in a mapping should never be attempted in general-purpose library
 *   code, because code based on the library could override the target of the
 *   role-reversing mapping, and the override could fail to observe the
 *   restrictions on object role references.
 *   
 *   Note that role reversals can almost always be handled with other
 *   mechanisms that handle reversals cleanly.  Always consider Doer.doInstead()
 *   first when confronted with a situation that seems to call for a
 *   role-reversing asObjFor() mapping, as doInstead() specifically allows for
 *   object role changes.  
 */
#define asObjFor(obj, Action) \
    { \
        preCond { return preCond##obj##Action; } \
        verify() { verify##obj##Action; } \
        remap() { return remap##obj##Action; } \
        check() { check##obj##Action; } \
        action() { action##obj##Action; } \
        report() { report##obj##Action; } \
    }

#define asDobjFor(action) asObjFor(Dobj, action)
#define asIobjFor(action) asObjFor(Iobj, action)

/* 
 *   Define mappings of everything except the action.  This can be used in
 *   cases where we want to pick up the verification, preconditions, and
 *   check routines from another handler, but not the action.  This is often
 *   useful for two-object verbs where the action processing is entirely
 *   provided by one or the other object, so applying it to both would be
 *   redundant.  
 */
#define asObjWithoutActionFor(obj, Action) \
    { \
        preCond { return preCond##obj##Action; } \
        verify() { verify##obj##Action; } \
        remap() { return remap##obj##Action(); } \
        check() { check##obj##Action; } \
        action() {  } \
    }

#define asDobjWithoutActionFor(action) asObjWithoutActionFor(Dobj, action)
#define asIobjWithoutActionFor(action) asObjWithoutActionFor(Iobj, action)

#define asObjWithoutVerifyFor(obj, Action) \
    { \
        preCond { return preCond##obj##Action; } \
        remap() { return remap##obj##Action(); } \
        check() { check##obj##Action; } \
        action() { action##obj##Action(); } \
        report() { report##obj##Action(); } \
    }

#define asDobjWithoutVerifyFor(action) asObjWithoutVerifyFor(Dobj, action)
#define asIobjWithoutVerifyFor(action) asObjWithoutVerifyFor(Iobj, action)

#define askForDobj(action)  askMissingObject(action, DirectObject)
#define askForIobj(action)  askMissingObject(action, IndirectObject)
#define askForAobj(action)  askMissingObject(action, AccessoryObject)
#define askForAcc(action)   askMissingObject(action, AccessoryObject) 

/*  Convenience macros for synthesizing travel in a given compass direction */

#define goInstead(dirn) doInstead(Go, dirn##Dir)
#define goNested(dirn) doNested(Go, dirn##Dir)




#define asExit(dir) : UnlistedProxyConnector { direction = dir##Dir }

/* ------------------------------------------------------------------------ */
/*
 *   Define an action with the given base class.  This adds the *Action
 *   suffix to the given root name, and defines a class with the given base
 *   class.  We also define the baseActionClass property to refer to myself;
 *   this is the canonical class representing the action for all subclasses.
 *   This information is useful because a language module might define
 *   several grammar rule subclasses for the given class; this lets us
 *   relate any instances of those various subclasses back to this same
 *   canonical class for the action if necessary.  
 */

/* 
 *   Define an action OBJECT with the given name inheriting from the given base
 *   class, for use with the Mercury parser.
 */

#define DefineAction(name, baseClass...) \
    name: ##baseClass \
    baseActionClass = name

/*
 *   Define a "system" action.  System actions are meta-game commands, such
 *   as SAVE and QUIT, that generally operate the user interface and are not
 *   part of the game world.  
 */
#define DefineSystemAction(name) \
    DefineAction(name, SystemAction)

/*
 *   Define a concrete IAction, given the root name for the action.  We'll
 *   automatically generate a class with name XxxAction. 
 */
#define DefineIAction(name) \
    DefineAction(name, IAction)

/*
 *   Define a concrete TAction, given the root name for the action.  We'll
 *   automatically generate a class with name XxxAction, a verProp with name
 *   verXxx, a checkProp with name checkXxx, and an actionProp with name
 *   actionDobjXxx.  
 */
#define DefineTAction(name) \
    DefineTActionSub(name, TAction)

/*
 *   Define a concrete TAction with a specific base class.  
 */
#define DefineTActionSub(name, cls) \
    DefineAction(name, cls) \
    verDobjProp = &verifyDobj##name \
    remapDobjProp = &remapDobj##name \
    preCondDobjProp = &preCondDobj##name \
    checkDobjProp = &checkDobj##name \
    actionDobjProp  = &actionDobj##name \
    reportDobjProp = &reportDobj##name \

#define DefineLiteralTAction(name)\
    DefineTActionSub(name, LiteralTAction)

#define DefineLiteralAction(name)\
    DefineAction(name, LiteralAction)

#define DefineTopicTAction(name)\
    DefineTActionSub(name, TopicTAction)

#define DefineTopicAction(name)\
    DefineAction(name, TopicAction)

#define DefineNumericTAction(name)\
    DefineTActionSub(name, NumericTAction)

#define DefineNumericAction(name) \
    DefineAction(name, NumericAction)


/*
 *   Define a concrete TIAction, given the root name for the action.  We'll
 *   automatically generate a class with name XxxAction, a verDobjProp with
 *   name verDobjXxx, a verIobjProp with name verIobjxxx, a checkDobjProp
 *   with name checkDobjXxx, a checkIobjProp with name checkIobjXxx, an
 *   actionDobjProp with name actionDobjXxx, and an actionIobjProp with name
 *   actionIobjXxx.  
 */
#define DefineTIAction(name) \
    DefineTIActionSub(name, TIAction)

/*
 *   Define a concrete TIAction with a specific base class.  
 */
#define DefineTIActionSub(name, cls) \
    DefineAction(name, cls) \
    verDobjProp = &verifyDobj##name \
    verIobjProp = &verifyIobj##name \
    remapDobjProp = &remapDobj##name \
    remapIobjProp = &remapIobj##name \
    preCondDobjProp = &preCondDobj##name \
    preCondIobjProp = &preCondIobj##name \
    checkDobjProp = &checkDobj##name \
    checkIobjProp = &checkIobj##name \
    actionDobjProp  = &actionDobj##name \
    actionIobjProp = &actionIobj##name \
    reportDobjProp = &reportDobj##name \
    reportIobjProp = &reportIobj##name \

/*
 *   The following macros relating to the TIAAction class are only relevant when
 *   the TIAAction extension is used. The macros are nevertheless included here
 *   for convenience when using the TIAAction extension.
 *
 *   Define a concrete TIAAction, given the root name for the action.  We'll
 *   automatically generate a class with name XxxAction, a verDobjProp with name
 *   verDobjXxx, a verIobjProp with name verIobjxxx, a checkDobjProp with name
 *   checkDobjXxx, a checkIobjProp with name checkIobjXxx, an actionDobjProp
 *   with name actionDobjXxx, and an actionIobjProp with name actionIobjXxx.
 */
#define DefineTIAAction(name) \
    DefineTIAActionSub(name, TIAAction)

/*
 *   Define a concrete TIAction with a specific base class.  
 */
#define DefineTIAActionSub(name, cls) \
    DefineAction(name, cls) \
    verDobjProp = &verifyDobj##name \
    verIobjProp = &verifyIobj##name \
    verAobjProp = &verifyAobj##name \
    remapDobjProp = &remapDobj##name \
    remapIobjProp = &remapIobj##name \
    remapAobjProp = &remapAobj##name \
    preCondDobjProp = &preCondDobj##name \
    preCondIobjProp = &preCondIobj##name \
    preCondAobjProp = &preCondAobj##name \
    checkDobjProp = &checkDobj##name \
    checkIobjProp = &checkIobj##name \
    checkAobjProp = &checkAobj##name \
    actionDobjProp  = &actionDobj##name \
    actionIobjProp = &actionIobj##name \
    actionAobjProp = &actionAobj##name \
    reportDobjProp = &reportDobj##name \
    reportIobjProp = &reportIobj##name \
    reportAobjProp = &reportAobj##name \


#define aobjFor(action) objFor(Aobj, action)
#define asAobjFor(action) asObjFor(Aobj, action)
#define accFor(action) objFor(Aobj, action)
#define asAccFor(action) asObjFor(Aobj, action)
#define gAobj gAction.curAobj
#define gAcc gAction.curAobj


/* 
 *   Macros for use in verify routines, returning various kinds of verify
 *   results
 */

#define gVerifyList gAction.verifyList

#define logical gAction.addVerifyResult (new VerifyResult(100, '', true, self))
    
#define illogical(msg) \
    gAction.addVerifyResult(new VerifyResult(30, msg, nil, self))

#define illogicalNow(msg) \
    gAction.addVerifyResult(new VerifyResult(40, msg, nil, self))


/* 
 *   IllogicalAlready doesn't do anything different from IllogicalNow in
 *   adv3Lite, but is supplied so that game authors familiar with adv3 can use
 *   it without getting a compilation error. It may also be slightly useful for
 *   documentary purposes to clarify why a verify routine in game code is ruling
 *   out an action.
 */
#define illogicalAlready(msg) \
    gAction.addVerifyResult(new VerifyResult(40, msg, nil, self))

#define illogicalSelf(msg) \
    gAction.addVerifyResult(new VerifyResult(20, msg, nil, self))

#define logicalRank(score) \
    gAction.addVerifyResult(new VerifyResult(score, '', true, self))

#define inaccessible(msg) \
    gAction.addVerifyResult(new VerifyResult(10, msg, nil, self))

#define implausible(msg) \
    gAction.addVerifyResult(new VerifyResult(35, msg, nil, self))

#define nonObvious \
    gAction.addVerifyResult(new VerifyResult(30, '', true, self, nil))

#define dangerous \
    gAction.addVerifyResult(new VerifyResult(90, '', true, self, nil))


/* ------------------------------------------------------------------------ */
/*
 *   Command interruption signal macros.  
 */

/* a concise macro to throw an ExitSignal */
#define exit throw new ExitSignal()

/* a concise macro to throw an ExitActionSignal */
#define exitAction throw new ExitActionSignal()

/* a concise macro to throw an AbortImplicitSignal */
#define abortImplicit throw new AbortImplicitSignal()

/* a concise macro to throw an Abort signal */
#define abort throw new AbortActionSignal()



/* ------------------------------------------------------------------------ */
/*
 *   aHref() flags 
 */
#define AHREF_Plain  0x0001    /* plain text hyperlink (no underline/color) */



/* ------------------------------------------------------------------------ */
/*
 *   An achievement defines its descriptive text.  It can also optionally
 *   define the number of points it awards.  
 */
Achievement template +points? "desc";



/* ------------------------------------------------------------------------ */
/*
 *   Templates for style tags 
 */
StyleTag template 'tagName' 'openText'? 'closeText'?;

/* ------------------------------------------------------------------------ */
/*
 *   Object definition templates 
 */

Thing template 'vocab' @location? "desc"?;
Topic template 'vocab' @familiar?;

Room template 'roomTitle' 'vocab' "desc"?;
Room template 'roomTitle' "desc"?;

Region template [rooms];

Door template  'vocab' @location? "desc"? ->otherSide;
Door template  ->otherSide 'vocab' @location? "desc"?;

TravelConnector template 'vocab'? @location? "desc"? ->destination;
TravelConnector template ->destination "travelDesc";

Enterable template inherited ->connector;
Enterable template ->connector inherited;

Unthing template 'vocab' @location? 'notHereMsg'?;

SensoryEmanation template inherited [eventList]?;

ActorState template "specialDesc" 'stateDesc' | "stateDesc" ?;

TopicGroup template +scoreBoost? 'convKeys' | [convKeys] ? ;


TopicEntry template
   +matchScore?
   @matchObj | [matchObj] | 'matchPattern'
   "topicResponse" | [eventList] ?;

/* a ShuffledEventList version of the above */
TopicEntry template
   +matchScore?
   @matchObj | [matchObj] | 'matchPattern'
   [firstEvents] [eventList];

/* we can also include *both* the match object/list *and* pattern */
TopicEntry template
   +matchScore?
   @matchObj | [matchObj]
   'matchPattern'
   "topicResponse" | [eventList] ?;

/* a ShuffledEventList version of the above */
TopicEntry template
   +matchScore?
   @matchObj | [matchObj]
   'matchPattern'
   [firstEvents] [eventList]; 

/* Version ActorTopicEntry template for use with Facts and kTag */
ActorTopicEntry template +matchScore? "topicResponse" | [eventList];

QueryTopic template
   +matchScore? 'matchPattern'
    "topicResponse" | [eventList] ?;

QueryTopic template
    +matchScore? 'matchPattern'
    [firstEvents] [eventList];    

QueryTopic template
   +matchScore? 'qtype'
   @matchObj | [matchObj] | 'matchPattern'
   "topicResponse" | [eventList] ?;

/* a ShuffledEventList version of the above */
QueryTopic template
   +matchScore? 'qtype'
   @matchObj | [matchObj] | 'matchPattern'
   [firstEvents] [eventList];

/* we can also include *both* the match object/list *and* pattern */
QueryTopic template
   +matchScore? 'qtype'
   @matchObj | [matchObj]
   'matchPattern'
   "topicResponse" | [eventList] ?;

/* a ShuffledEventList version of the above */
QueryTopic template
   +matchScore? 'qtype'
   @matchObj | [matchObj]
   'matchPattern'
   [firstEvents] [eventList];

SayTopic template
    +matchScore?
    'tTag' 'extraVocab'
    "topicResponse" | [eventList] ?;

CommandTopic template +matchScore? 
    @matchObj | [matchObj]
    @matchDobj @matchIobj? "topicResponse" | [eventList]? ;

CommandTopic template +matchScore? 
    @matchObj | [matchObj] [matchDobj] @matchIobj "topicResponse" | [eventList]? ;

DefaultTopic template "topicResponse" | [eventList];
DefaultConsultTopic template "topicResponse" | [eventList];
DefaultThought template "topicResponse" | [eventList];

/* miscellanous topics just specify the response text or list */
MiscTopic template "topicResponse" | [eventList];
MiscTopic template [firstEvents] [eventList];
NodeContinuationTopic template "topicResponse" | [eventList];
NodeContinuationTopic template [firstEvents] [eventList];

/* AltTopics just specify the response text or list */
AltTopic template "topicResponse" | [eventList];
AltTopic template [firstEvents] [eventList];

/* The ProxyActor template just specifies the location (i.e. the base Actor) */
ProxyActor template @location;

Doer template 'cmd';

/* Templates for use with test sequences */
Test template 'testName' [testList] @location? [testHolding]?;
Test template 'testName' [testList] [testHolding]? @location?;

/* Define convenient named constants for use with ConvAgendaItem */
#define InitiateConversationReason 1
#define ConversationLullReason 2
#define DefaultTopicReason 3



/* ------------------------------------------------------------------------ */
/*
 *   Command interruption signal macros.  
 */

/*
 *   Terminate execution of the command line.  This aborts the current
 *   command, including any remaining object iterations for the current
 *   action, and discards anything else on the command line.  
 */
#define exitCommandLine  throw new ExitCommandLineSignal()
/*----------------------------------------------------------------------------*/
/*
 *   enums for different types of lock:
 */    
   
enum notLockable, lockableWithoutKey, lockableWithKey, indirectLockable;

enum masculine, feminine, neuter;

/* ------------------------------------------------------------------------ */
/*
 *   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.
 *   
 *   This message object isn't generally used for parser messages or action
 *   replies - most of those come from the objects given by the current
 *   actor's getParserMessageObj() or getActionMessageObj(), respectively.
 *   
 *   By default, this is set to libMessages.  The library never changes this
 *   itself, but a game can change this if it wants to switch to a new set of
 *   messages during a game.  (If you don't need to change messages during a
 *   game, but simply want to customize some of the default messages, you
 *   don't need to set this variable - you can simply use 'modify
 *   libMessages' instead.  This variable is designed for cases where you
 *   want to *dynamically* change the standard messages during the game.)  
 */
#define gLibMessages (libGlobal.libMessageObj)

/* 
 *   the exit lister object - if the exits module isn't included in the
 *   game, this will be nil 
 */
#define gExitLister (libGlobal.exitListerObj)

/*
 *   the hint manager object - if the hints module isn't included in the
 *   game, this will be nil 
 */
#define gHintManager (libGlobal.hintManagerObj)


/*
 *   the extra hint manager object - if the hints module isn't included in the
 *   game, this will be nil 
 */
#define gExtraHintManager (libGlobal.extraHintManagerObj)


/* ------------------------------------------------------------------------ */
/*
 *   Convenience macros for controlling the narrative tense.
 */

/*
 *   Set the current narrative tense.  Use val = true for past and
 *   val = nil for present.
 */
#define setPastTense(val) (gameMain.usePastTense = (val))

/*
 *   Shorthand macro for selecting one of two values depending on the
 *   current narrative tense.
 */
#define tSel(presVal, pastVal) \
    (gameMain.usePastTense ? (pastVal) : (presVal))

/*
 *   Temporarily override the current narrative tense and invoke a callback
 *   function.
 */
#define withPresent(callback) (withTense(nil, (callback)))
#define withPast(callback)    (withTense(true, (callback)))


/* ------------------------------------------------------------------------ */
/*
 *   Object role identifiers.  These are used to identify the role of a noun
 *   phrase in a command.
 *   
 *  
 */


/*
 *   A special role for the "other" object of a two-object command.  This
 *   can be used in certain contexts (such as remapTo) where a particular
 *   object role is implied by the context, and where the action involved
 *   has exactly two objects; OtherObject in such contexts means
 *   DirectObject when the implied role is IndirectObject, and vice versa. 
 */
enum OtherObject;


/* ------------------------------------------------------------------------ */
/* 
 *   A couple of utility macros we use internally for turning macro values
 *   into strings.  STRINGIZE(x) expands any macros in its argument and then
 *   turns the result into a single-quoted string, which can then be used in
 *   regular program text or in directives that evaluate constant
 *   expressions, such as #if.  (STRINGIZE is the real macro; _STRINGIZE is
 *   needed to force expansion of any macros in the argument, which is
 *   required because of the weird ANSI C expansion-order rules, and which
 *   works because of same.)  
 */
#define _STRINGIZE(x) #@x
#define STRINGIZE(x)  _STRINGIZE(x)


/* ------------------------------------------------------------------------ */
/*
 *   Msg() - define a custom message to override a library message.  'id' is
 *   the message ID, which is the same ID used for the DMsg() message that
 *   you wish to override.  Do NOT use quotes around the ID - just enter it
 *   as though it were a variable name.  'txt' is the message text, as a
 *   single-quoted string.  
 *   
 *   This is used in CustomMessages objects to define message overrides.  See
 *   CustomMessages for full details.  
 */
#define Msg(id, txt)  #@id, txt



/* ------------------------------------------------------------------------ */
/*
 *   DMsg() - default English library message cover macro.
 *   
 *   Whenever the library displays a message, it uses the DMsg() macro.  The
 *   arguments are a message ID, and the default English message text to
 *   display.  The message ID is a string that identifies the message; this
 *   is used to look for overriding customizations of the message.  Refer to
 *   the CustomMessages class for information on customizing the standard
 *   library messages.
 *   
 *   In our approach, the library defines the default English text of the
 *   messages in-line, directly in the code.  On the surface, this is
 *   contrary to standard practices in most modern programming projects,
 *   which strive to make translations easier by separating the message text
 *   from the program code, gathering all of the text into a central message
 *   file that can be replaced for each language.  Despite appearances, we're
 *   accomplishing the same thing - but in our system, we have the advantage
 *   that we *also* define the default English message text in-line as part
 *   of the code it applies to.  This makes it easier to read the code by
 *   keeping a message and its full context in one place; this way you don't
 *   have to shuttle between the code and message file.
 *   
 *   Here's how we accomplish the message separation required for
 *   translations, and also for games that wish to customize the library
 *   defaults.  The DMsg() macro requires both the default English message
 *   text *and* an ID key for the message.  The message display function
 *   receives both.  The display function proceeds to look up the ID key in a
 *   translation table; if it finds an entry, it uses the version of the
 *   message in the translation table instead of the English default passed
 *   in via DMsg().  A language module can provide a message table that
 *   defines the language translations, and a game can provide a table that
 *   further customizes the library messages to fit its narrative style.
 *   
 *   There's one additional element.  Translators and game authors need to be
 *   able to see all of the messages in one place, so they can create their
 *   tables.  We would seem to lack that central list of English messages.
 *   Fortunately, by using a standard macro for each message, we can extract
 *   a comprehensive English message list automatically via a special tool.
 *   We use this as part of the library release process to create the English
 *   message file for reference.
 *   
 *   Note that the macro expansion includes the default English text only in
 *   English builds.  It omits the text in non-English builds.  This is to
 *   save space - we assume that the English messages will all be overridden
 *   anyway by each translated library version, so there's no point in
 *   including their text in the final compiled program.  
 */
#if STRINGIZE(LANGUAGE) == 'english'
#define DMsg(id, txt, args...)  message(#@id, txt, ##args)
#define BMsg(id, txt, args...)  buildMessage(#@id, txt, ##args)
#else
#define DMsg(id, txt, args...)  message(#@id, nil, ##args)
#define BMsg(id, txt, args...)  buildMessage(#@id, nil, ##args)
#endif

/* ------------------------------------------------------------------------ */
/*
 *   Debugging.  When we compile in development mode, we'll include a number
 *   of functions and methods that display information for debugging
 *   purposes.  We omit these in release builds to keep the compiled file
 *   size smaller, and to avoid making it too easy for end users to
 *   snoop around in the program internals.
 */
#ifdef __DEBUG
# define IfDebug(key, code) \
    if (DebugCtl.enabled[#@key]) { code; } else { }
#else
# define IfDebug(key, code)
#endif

#define gOutStream (outputManager.curOutputStream)

#ifdef __DEBUG
#include <dynfunc.h>
#endif

/*
 *   Some message processors add their own special parameters to messages,
 *   because they want to use expansion parameters (in the "{the dobj/him}"
 *   format) outside of the set of objects directly involved in the command.
 *   
 *   The Action method setMessageParam() lets you define such a parameter,
 *   but for convenience, we define this macro for setting one or more
 *   parameters whose names exactly match their local variable names.  In
 *   other words, if you call this macro like this:
 *   
 *   gMessageParams(obj, cont)
 *   
 *   then you'll get one parameter with the text name 'obj' whose expansion
 *   will be the value of the local variable obj, and another with text name
 *   'cont' whose expansion is the value of the local variable cont.  
 */
#define gMessageParams(var...) \
    (gAction.setMessageParams(var#foreach/#@var, var/,/))

/* ------------------------------------------------------------------------ */
/*
 *   Definitions for the menu system
 */

/* 
 *   The indices for the key values used to navigate menus, which are held
 *   in the keyList array of MenuItems.  
 */
#define M_QUIT      1
#define M_PREV      2
#define M_UP        3
#define M_DOWN      4
#define M_SEL       5

/* some templates for defining menu items */
MenuItem template 'title' 'heading'?;
MenuTopicItem template 'title' 'heading'? [menuContents];
MenuLongTopicItem template 'title' 'heading'? 'menuContents';

/* templates for hint system objects */
Goal template ->closeWhenAchieved? 'title' 'heading'? [menuContents];
Hint template 'hintText' [referencedGoals]?;

/* 
 *   A Template to facilitate the definition of ExtraHints. We can define it 
 *   here and not in a header file since ExtraHints are only defined in this 
 *   source file. */

ExtraHint template +hintDelay? "hintText" | [eventList];


/* templates for EventLists */

EventList template [eventList];
ShuffledEventList template [firstEvents] [eventList];

/* template for Scenery Class */
Scenery template @location? [scenList];

/* template and macros for Facts module */
Fact template 'name' [topics]? 'desc' [initiallyKnownBy]?;

#define gFact(tag) (factManager.getFact(tag))
#define gFactDesc(tag) (factManager.getFactDesc(tag))

/* Convenient synonyms for two ActorTopicEntry properties used in conjunction with Facts. */
#define rTag aTag
#define iTag tTag


/* ------------------------------------------------------------------------ */
/*  
 *   Property synonyms
 */

/* 
 *   The library uses isEdible rather than isEatable, since edible is the more
 *   natural word to use, but strict consistency might have dictated isEatable,
 *   so we make it an effective synonym in case some game authors use it.
 */
#define isEatable isEdible


/*  
 *   Conversely, authors alive to the Latin root of edible might try the
 *   latinate isPotable instead of isDrinable.
 */
#define isPotable isDrinkable

/* ------------------------------------------------------------------------ */
/*
 *   String templates for room descriptions etc.
 */

string template <<mention a * >> mentionA;
string template <<mention an * >> mentionA;
string template <<mention the * >> mentionThe;
string template << list of * is >> listStrIs;
string template << list of * >> makeListInStr;
string template << is list of * >> isListStr;
string template << exclude * >> makeMentioned;

/* ------------------------------------------------------------------------*/
/*
 *   Some useful macros for command text.
 */

/* Get the first word the player entered for the current command. */
#define gVerbWord (gCommand == nil || gCommand.verbProd == nil ? '' \
    : getTokVal(gCommand.verbProd.tokenList[1]))

/* Get the command tokens for the current command. */
#define gCommandToks (gCommand == nil || gCommand.verbProd == nil ? [] \
    : gCommand.verbProd.tokenList.mapAll({t: getTokVal(t)}))

/* Get the command phrase for the current command. */
#define gVerbPhrase (gCommand.getCommandPhrase())

/* ------------------------------------------------------------------------ */
/*
 *   Size classes.  An object is large, medium, or small with respect to
 *   each sense; the size is used to determine how well the object can be
 *   sensed at a distance or when obscured.
 *   
 *   What "size" means depends on the sense.  For sight, the size
 *   indicates the visual size of the object.  For hearing, the size
 *   indicates the loudness of the object.  
 */

/* 
 *   Large - the object is large enough that its details can be sensed
 *   from a distance or through an obscuring medium.
 */
enum large;

/* 
 *   Medium - the object can be sensed at a distance or when obscured, but
 *   not in any detail.  Most objects fall into this category.  Note that
 *   things that are parts of large objects should normally be medium.  
 */
enum medium;

/*
 *   Small - the object cannot be sensed at a distance at all.  This is
 *   appropriate for detailed parts of medium-class objects.  
 */
enum small;

/*  Enums for Goals in the Hint system */
enum OpenGoal, ClosedGoal, UndiscoveredGoal;

/* Enums for Footnotes */
enum FootnotesFull, FootnotesMedium, FootnotesOff;

/* Template for Footnotes */
Footnote template "desc";

/* Template for ClockEvent */
ClockEvent template [eventTime];

/* String Templates for Objective Time module */
string template <<take * seconds>> takeTime;
string template <<take * second>> takeTime;
string template <<take * sec>> takeTime;
string template <<take * secs>> takeTime;

string template <<add * seconds>> addTime;
string template <<add * second>> addTime;
string template <<add * sec>> addTime;
string template <<add * secs>> addTime;


/* ------------------------------------------------------------------------- */
/*
 *   Communication Link Types
 *
 *   AudioLink means audio communication only is available VideoLink means both
 *   audio and visual links are available.
 */
#define AudioLink 1
#define VideoLink 2


/* ------------------------------------------------------------------------ */
/*
 *   Conversation manager macros
 */

/* has a topic key been revealed through <.reveal>? */
#define gRevealed(key)  (libGlobal.getRevealed(key)) 

/* reveal a topic key, as though through <.reveal> */
#define gReveal(key, args...) (libGlobal.setRevealed(key, ## args))

/* remove a topic key, as though through <.unreveal> */
#define gUnreveal(key) (libGlobal.setUnrevealed(key))

/* mark a Topic/Thing as known/seen by the player character */
#define gSetKnown(obj) (gPlayerChar.setKnowsAbout(obj))
#define gSetSeen(obj) (gPlayerChar.setHasSeen(obj))

/* does the player character know about obj? */
#define gKnown(obj) (gPlayerChar.knowsAbout(obj))
#define pcKnows(obj) (gPlayerChar.knowsAbout(obj))

/* has a topic key been revealed to an NPC through <.inform>? */
#define gInformed(key) (getActor.informedAbout(key))

/* is the topic key or object known to the NPC we're in conversaation with? */
#define npcKnows(obj) (getActor.knowsAbout(obj))

/* the last topic mentioned in the course of the current conversation */
#define gLastTopic (libGlobal.lastTopicMentioned)

/* the last fact mentioned in the course of the current conversation */
#define gLastFact (libGlobal.lastFactMentioned)

/* Associated knowledge enums */

enum likely, dubious, unlikely, untrue;
enum speaker;

/* Macros to deal with knowledge enums and their associated objects */

#define BV(x) (defined(beliefManager) ? beliefManager.bvTab[x] : x)

/* ------------------------------------------------------------------------- */
/*
 *   Define some synonyms for potentially confusing property names
 */

#define checkTouchMsg checkFeelMsg
#define feelResponseMsg touchResponseMsg
#define isTouchable isFeelable
#define cannotTouchMsg cannotFeelMsg
#define touchDesc feelDesc
#define checkHitMsg checkAttackMsg
#define hitResponseMsg attackResponseMsg

/*--------------------------------------------------------------------------- */
/* 
 *   Define some macros to give abbreviated synonyms to inputManager method
 */

#define more inputManager.pauseForMore()
#define input(x...) inputManager.getInputLine(x)
#define waitKey(x...) inputManager.getKey(x)

/*----------------------------------------------------------------------------*/
/*
 *   Definitions for the rules.t extension
 */

#define stop return stopValue;

/* 
 *   Null value to return from Rules that don't stop a RuleBook from continuing
 *   to consider rules.
 */
enum null;

/* 
 *   Convenient abbreviations for rules that want to allow their RuleBook to
 *   continue processing more rules.
 */
#define rnull return null
#define nextrule return (rulebook.contValue)
#define nostop return (rulebook.contValue)

Rule template @location? &action | [action]?;

/*----------------------------------------------------------------------------*/
/*
 *   Definitions for the relations.t extension
 */

enum oneToOne, oneToMany, manyToOne, manyToMany;
Relation template 'name' 'reverseName'? @relationType? +reciprocal?;

enum normalRelation, reverseRelation;

/*----------------------------------------------------------------------------*/
/*
 *   Definitions for the Signals Extension
 */

#define DefSignal(sig, nam) sig##Signal: Signal \
    name = #@nam\
    handleProp = &handle_##sig
/*----------------------------------------------------------------------------*/
/*
 *   Definitions for the SymConn Extension
 */

SymConnector template ->destination;
SymConnector template @room1 @room2;

SymPassage template ->destination 'vocab' "desc"?;
SymPassage template 'vocab' ->destination "desc"?;
SymPassage template 'vocab' @room1 @room2 "desc"?;
SymPassage template 'vocab' [rooms] "desc"?;
 
string template <<* by room>> byRoomFunc;

/* Equivalent template for DSCon objects in main linrary. */
DSCon template 'vocab' @room1 @room2 "desc"?;

DSTravelConnector template @room1 @room2;
DSTravelConnector template ->room1 ->room2;


/*----------------------------------------------------------------------------*/
/*
 *   Definitions for the eventListItem Extension
 */
EventListItem template @myListObj? ~isReady? +minInterval? *maxFireCt? "invokeItem"? ;

/*-----------------------------------------------------------------------------*/
/*
 *   Definitions for Moods and Stances
 */

/* Define a new Stance */
#define DefStance(name_, score_) \
    name_ ## Stance: Stance \
    name = #@name_ \
    score = score_

/* Define a new Mood */
#define DefMood(name_) \
    name_ ## Mood: Mood \
    name = #@name_ 

/* Is the actor whose agenda item, topic entry or whatever we're looking from in this stance? */
#define gStance (getActor().stance)
#define gStanceIs(st_) (getActor().stance == st_ ## Stance)

/* What stance is this actor in? */
#define aStance(actor) (actor.stance)

/* Is actor in this stance ? */
#define aStanceIs(actor, st_) (actor.stance == st_ ## Stance)

/* Is actor in one of these stances? */
#define gStanceIn(st_...) \
    (gStance is in (st_#foreach: st_##Stance:, :))

#define aStanceIn(actor, st_...) \
    (actor.stance is in (st_#foreach: st_##Stance:, ))

/* What mood is getActor in? */
#define gMood (getActor().mood)

/* Is getActor in one of these moods? */
#define gMoodIs(mood_) (getActor().mood == mood_ ## Mood)


/* What mood is actor in? */
#define aMood(actor) (actor.mood)

/* Is actor in this mood? */
#define aMoodIs(actor, mood_) (actor.mood == mood_ ## Mood)

/* Is getActor in one of these moods? */
#define gMoodIn(mood_...) \
    (gMood is in (st_#foreach: mood_##Mood:, :))

/* Is actor in one of these moods? */
#define aMoodIn(actor, mood_...) \
    (actor.mood is in (mood_#foreach: mood_##Mood:, :))

/* Set stances towards the player character */
#define gSetStance(st_) (getActor.setStanceToward(gPlayerChar, st_ ## Stance))
#define aSetStance(actor, st_) (actor.setStanceToward(gPlayerChar, st_ ## Stance))

/* Set moods */
#define gSetMood(mood_) (getActor.setMood(mood_ ## Mood))
#define aSetMood(actor, mood_) (actor.setMood(mood_ ## Mood))

 /*----------------------------------------------------------------------------*/
/*
 *   Include the header for the Date intrinsic class. For some reason the
 *   compiler seems to prefer this to be at the end of this header file.
 */
#include <date.h>

#endif
Adv3Lite Library Reference Manual
Generated on 25/04/2024 from adv3Lite version 2.0