#charset "us-ascii"
#include "advlite.h"
/*
* ****************************************************************************
* action.t
* This module forms part of the adv3Lite library
* (c) 2012-13 Eric Eve
*/
/*
* The library doesn't yet provide any support for actions that take three
* objects (TIAActions, as they might hypothetically be called), but since the
* Mercury parser does we provide a number of hooks that other code could use.
* To this end we need to define the properties a TIAAction might use so the
* compiler recognizes them.
*/
property curAobj, verAobjProp, preCondAobjProp, remapAobjProp;
property verifyAobjDefault, preCondAobjDefault;
class Action: ReplaceRedirector
/*
* Flag; should this action be considered a failure? This should be reset
* to true at the start of the action processing cycle but can be tested
* later to prevent, e.g., inappropriate reporting.
*/
actionFailed = nil
/*
* The execGroup() method is called by the current Command object before
* it calls the action on individual objects, to allow processing of the
* group of objects as a whole. By default we do nothing here in the
* library.
*/
execGroup(cmd) { }
/*
* The checkAction() method calls the check routines on the objects
* involved in the command (where there are objects). Subclasses such as
* TAction and TIAction need to override this to carry out the appropriate
* handling.
*/
checkAction() { }
/*
* The main routine for handling an action. This is the method called by
* the command object; the cmd parameter gives the calling Command object.
*/
exec(cmd)
{
/* Resest actionFailed to nil */
actionFailed = nil;
/* Resent the scope list */
scopeList = [];
/* Note the current actor */
libGlobal.curActor = cmd.actor;
/* Note the location of the current actor */
oldRoom = gActor.getOutermostRoom;
/*
* Note whether the current actor's location starts out illuminated;
* we need to know this so we can display a notification if the
* illumination changes.
*/
wasIlluminated = oldRoom.isIlluminated();
/* execute the action-processing cycle */
execCycle(cmd);
}
/*
* The action-processing cycle carries out the before action
* notifications, then executes the action. This needs to be overridden on
* various subclasses since the beforeAction notifications can occur at
* different points in different kinds of action.
*/
execCycle(cmd)
{
try
{
IfDebug(actions,
"[Executing <<actionTab.symbolToVal(baseActionClass)>>
<< if cmd.dobj != nil>> : <i><<dqinfo>></i>
<<cmd.dobj.name>><<end>>
<< if cmd.iobj != nil>> : <i><<iqinfo>></i>
<<cmd.iobj.name>><<end>> ]\n" );
/* Carry out the before action notifications. */
beforeAction();
/* Execute the main action handling. */
execAction(cmd);
/*
* If the action is repeatable, make a note of it in case the
* player issues an AGAIN command.
*/
if(isRepeatable)
libGlobal.lastAction = self.createClone();
}
catch(ExitActionSignal ex)
{
}
/* If an exit signal is issued we skip to here. */
catch(ExitSignal ex)
{
/*
* If the exit macro is used in the course of the command,
* consider the command a failure.
*/
actionFailed = true;
}
}
/* The main action handler. Subclasses must override. */
execAction(cmd)
{
}
/* The room the actor was in when the action started */
oldRoom = nil
/* Flag to indicate whether the actor's location started out illuminated */
wasIlluminated = nil
/*
* A list of any PreConditions that apply to this action as a whole, as
* opposed to any of its objects. This is most likely to be relevant to an
* IAction.
*/
preCond = nil
checkActionPreconditions()
{
local preCondList;
local checkOkay = true;
/*
* Construct a list or preCondition objects on the appropriate object
* property.
*/
preCondList = valToList(preCond);
/* Sort the list in preCondOrder */
preCondList = preCondList.sort(nil,
{a, b: a.preCondOrder - b.preCondOrder});
try
{
/* Iterate through the list to see if all the checks are satisfied */
foreach(local cur in preCondList)
{
/*
* If we fail the check method on any precondition object,
* note the failure and stop the iteration.
*/
if(cur.checkPreCondition(gActor, true) == nil)
{
checkOkay = nil;
break;
}
}
}
/*
* Game authors aren't meant to use the exit macro in check methods,
* but in case they do we handle it here.
*/
catch (ExitSignal ex)
{
checkOkay = nil;
}
/*
* If the check method failed on any of our precondition objects note
* that the action is a failure.
*/
if(checkOkay == nil)
actionFailed = true;
/*
* Otherwise, if we're not an implicit action, display any pending
* implicit action announcements.
*/
else if(!isImplicit)
"<<buildImplicitActionAnnouncement(true, true)>>";
/* Return our overall check result. */
return checkOkay;
}
beforeAction()
{
/*
* Check any Preconditions relating to the action as a whole (as
* opposed to any of its objects.
*/
if(!checkActionPreconditions())
exit;
/*
* Call the before action handling on the current actor (in its
* capacity as actor/
*/
gActor.actorAction();
/*
* If the sceneManager is present then send a before action
* notification to every currently active Scene.
*/
if(defined(sceneManager) && sceneManager.notifyBefore());
/*
* Call roomBeforeAction() on the current actor's location, and
* regionBeforeAction() on all the regions it's in.
*/
gActor.getOutermostRoom.notifyBefore();
/*
* If we don't already have a scope list for the current action, build
* it now.
*/
if(nilToList(scopeList).length == 0)
buildScopeList;
/* Call the beforeAction method of every action in scope. */
foreach(local cur in scopeList)
{
cur.beforeAction();
}
}
/*
* Carry out the post-action processing. This first checks to see if
* there's been a change in illumination. If there has we either show a
* room description (if the actor's location is now lit) or announce the
* onset of darkness. We then call the after action notifications first on
* the actor's current room and then on every object in scope.
*
* Note that afterAction() is called from the current Command object.
*/
afterAction()
{
/*
* If the current action is considered a failure, we don't carry out
* any after action handling, since in this case there's no action to
* react to.
*/
if(actionFailed)
return;
/*
* If the actor is still in the same room s/he started out in, check
* whether the current illumination level has changed, and, if so,
* either show a room description or announce the onset of darkness,
* as appropriate.
*/
if(oldRoom == gActor.getOutermostRoom)
{
if(oldRoom.isIlluminated)
{
if(!wasIlluminated)
{
"<.p>";
oldRoom.lookAroundWithin();
}
}
else if(wasIlluminated)
{
DMsg(onset of darkness, '\n{I} {am} plunged into darkness. ');
}
}
"<.p>";
/* Call the afterAction notifications on all currently active scenes. */
if(defined(sceneManager) && sceneManager.notifyAfter());
/*
* Call the afterAction notification on the current room and its
* regions.
*/
gActor.getOutermostRoom.notifyAfter();
/*
* Call the afterAction notification on every object in scope. Note
* that we have to recalculate the scope list here in case the action
* has changed it.
*/
foreach(local cur in Q.scopeList(gActor))
{
cur.afterAction();
}
}
/*
* The turnSequence() method is called from the current Command object. It
* first executes any current daemons (apart from any PromptDaemons) and
* then advances the turn counter. We define this on the Action class
* principally to make it simple for certain kinds of Action such as
* SystemActions to do nothing here (since they don't count as actions
* within the game world).
*/
turnSequence()
{
/* Execute the regionDaemon on every region in which the player character is located. */
local lst = gPlayerChar.getOutermostRoom.allRegions();
foreach(local reg in lst)
{
"<.p>";
reg.regionDaemon();
}
"<.p>";
/* Execute the player character's current location's roomDaemon. */
gPlayerChar.getOutermostRoom.roomDaemon();
/*
* If the events.t module is included, execute all current Daemons and
* Fuses/
*/
if(defined(eventManager) && eventManager.executeTurn())
;
/* Advance the turn counter */
libGlobal.totalTurns += turnsTaken;
}
/*
* The number of turns this action is counted as taking. Normally, this
* will be 1.
*/
turnsTaken = 1
/* Flag: is this an implicit action? By default it isn't. */
isImplicit = nil
/* Can this action be Undone? By default most actions can. */
includeInUndo = true
/* Flag: is this a conversational action? */
isConversational = nil
/*
* Is this action repeatable (with an AGAIN command)? Most actions are so
* the default is true but subclasses can override to exclude actions
* (such as certain system actions) that it would make no sense to repeat.
*/
isRepeatable = true
/*
* If an AGAIN command is used with this command, should the command be
* reparsed from scratch (because it might involve a different object) or
* not (because it should act on the same objects). We generally set this
* to true for actions it wouldn't normally make sense to repeat on the
* same object straight away. Since this applies to the majority of
* actions, we make this the default.
*/
againRepeatsParse = true
/*
* Report on the action. This is only relevant where the action has more
* or one objects, so TAction must override. This is called from the
* current Command object once all the objects have been acted on (in a
* case where multiple direct objects have been specified, as in TAKE ALL
* or TAKE RED BALL AND GREEN PEN). This allows the report routine to
* summarize the action for all the objects acted upon instead of
* displaying an individual report for each one.
*/
reportAction() { }
/*
* Do we have a parent action, and if so what is it? The parent action
* would be the action that's using us as an implicit action or nested
* action.
*/
parentAction = nil
/*
* Carry out the verification stage for this object in this role, and
* carry out any remapping needed. This needs to be defined on Action
* since there might be verification of the ActorRole.
*/
verify(obj, role)
{
local remapResult;
local verifyProp;
local preCondProp;
local remapProp;
/* Clear out any previous verify results */
verifyTab = nil;
/*
* Note which properties to use according to which role we're
* verifying for. (No actions in the adv3Lite library currently use an
* Accessory object but the possibility is included here to ease
* subsequent extension).
*/
switch(role)
{
case DirectObject:
verifyProp = verDobjProp;
preCondProp = preCondDobjProp;
remapProp = remapDobjProp;
break;
case IndirectObject:
verifyProp = verIobjProp;
preCondProp = preCondIobjProp;
remapProp = remapIobjProp;
break;
case AccessoryObject:
verifyProp = verAobjProp;
preCondProp = preCondAobjProp;
remapProp = remapAobjProp;
break;
case ActorRole:
verifyProp = &verifyActor;
remapProp = &remapActor;
preCondProp = &preCondActor;
break;
}
/* first check if we need to remap this action. */
remapResult = obj.(remapProp);
/*
* the object's remap routine can return an object or a list (if it
* returns anything else we ignore it). If it returns an object use
* that object in place of the one we were about to verify for the
* remainder of this action. If it returns a list, the list should
* contain the details of an action that is to replace the current
* action, so run the remapped action instead.
*/
switch(dataType(remapResult))
{
case TypeObject:
obj = remapResult;
break;
case TypeList:
/*
* If the remap result is a list, then we'll need to remap the
* action to another one, but we'll leave that until all the
* objects have been resolved. For the purpose of verifying this
* object we'll just return a standard logical verify result to
* allow the remapping to proceed at a later stage.
*/
DMsg(remap error, '<b>ERROR!</b> The long form of remap is no longer
available; please use a Doer instead. ');
default:
break;
}
/*
* Note which object we're currently verifying in the Action's
* verifyObj property so other routines can find it (particularly a
* verify routine called from a preCondition).
*/
verifyObj = obj;
curObj = obj;
switch(role)
{
case DirectObject:
curDobj = obj;
break;
case IndirectObject:
curIobj = obj;
break;
case AccessoryObject:
curAobj = obj;
break;
}
/*
* if the object is a decoration then we use the catchall Default
* prop, unless we're an action that bypasses it.
*/
if(obj.isDecoration
&& obj.decorationActions.indexWhich({x: self.ofKind(x)}) == nil)
{
switch(role)
{
case DirectObject:
verifyProp = &verifyDobjDefault;
preCondProp = &preCondDobjDefault;
break;
case IndirectObject:
verifyProp = &verifyIobjDefault;
preCondProp = &preCondIobjDefault;
break;
case AccessoryObject:
verifyProp = &verifyAobjDefault;
preCondProp = &preCondAobjDefault;
break;
}
}
/* Execute the appropriate verify routine on the object */
obj.(verifyProp);
/* If we don't already have a verify table, create it */
if(verifyTab == nil)
verifyTab = new LookupTable;
/*
* If executing this verify routine didn't create an entry for this
* object in the verify table, create one now with a default 'logical'
* verify result.
*/
if(verifyTab.isKeyPresent(obj) == nil)
verifyTab[obj] = new VerifyResult(100, '', true, obj);
/*
* Next run through all the items in our precondition list and execute
* their verify methods.
*/
foreach(local cur in valToList(obj.(preCondProp)))
cur.verifyPreCondition(obj);
/*
* Return the entry for this object in our verify table (which may
* have been altered by one of the preconditions above).
*/
return verifyTab[obj];
}
/*
* Flag, do we want an action that fails at the verify stage to count as a turn (in other
* words, if an action fails at the verify stage, do we want to advance the turn
* counter,excecute daemons, and do all the other turn sequence stuff)? By default we do,
* since this has long been the standard behaviour, but game code can override this to nil
* either globally on the Action class on on individual actions to cause failure at the verify
* stage to abort the remainder of the turn sequence.
*/
failedActionCountsAsTurn = true
/*
* Run the verify routine on the current object in the current role to see
* whether it will allow the action. If it won't, display any pending
* implicit action announcements, then display the message explaining why
* the action is disallowed, and finally return nil to tell our caller to
* halt the action. If the verify stage does allow the action to go ahead,
* return true to tell our caller that this routine has no objection.
*/
verifyObjRole(obj, role)
{
local verResult;
local verMsg;
/* Make sure we start with a clean new verify table */
verifyTab = new LookupTable;
verResult = verify(obj, role);
/*
* If the verify result is one that disallows the action then display
* the failure message, along with any failed implicit action message.
*/
if(!verResult.allowAction)
{
/* Note our failure message */
verMsg = verResult.errMsg;
/*
* If this is the direct object of the command and there's more
* than one, and if the option to announce objects in verify
* messages is true, then announce the name of this object to make
* it clear which one is being referred to.
*/
if(announceMultiVerify && role == DirectObject &&
gCommand.dobjs.length > 1)
announceObject(obj);
/*
* If we're an implicit action add a failed implicit action report
* ('trying to...').
*/
if(isImplicit)
"<<buildImplicitActionAnnouncement(nil)>>";
/*
* Display the failure message, unless it's identical to the
* previous one.
*/
if(verMsg != lastVerifyMsg || announceMultiVerify)
{
say(verMsg);
"\n";
lastVerifyMsg = verMsg;
}
/* Note that this action has failed. */
actionFailed = true;
if(!failedActionCountsAsTurn)
abort;
/*
* Stop the processing of the action here by telling our caller
* we've failed.
*/
return nil;
}
/*
* If we're an implicit action and our best verify result doesn't
* allow implicit actions, abort the implicit action.
*/
if(isImplicit && !verResult.allowImplicit)
abortImplicit;
/*
* Otherwise return true to tell our caller we're not objecting to the
* action.
*/
return true;
}
/* The object currently being verified */
verifyObj = nil
/*
* Get a message parameter object for the action. Each action
* subclass defines this to return its objects according to its own
* classifications. The default action has no objects, but
* recognizes 'actor' as the current command's actor.
*/
getMessageParam(objName)
{
switch(objName)
{
case 'pc':
/* return the player character */
return gPlayerChar;
case 'actor':
/* return the current actor */
return gActor;
case 'cobj':
/* return the current object, if there is one */
return curObj;
default:
/*
* if we have an extra message parameters table, look up the
* parameter name in the table
*/
if (extraMessageParams != nil)
return extraMessageParams[objName];
/* we don't recognize other names */
return nil;
}
}
/*
* Define an extra message-specific parameter. Message processors
* can use this to add their own special parameters, so that they
* can refer to parameters that aren't involved directly in the
* command. For example, a message for "take <dobj>" might want to
* refer to the object containing the direct object.
*/
setMessageParam(objName, obj)
{
/*
* if we don't yet have an extra message parameters table,
* create a small lookup table for it
*/
if (extraMessageParams == nil)
extraMessageParams = new LookupTable(8, 8);
/* add the parameter to the table, indexing by the parameter name */
extraMessageParams[objName.toLower()] = obj;
}
/*
* For convenience, this method allows setting any number of
* name/value pairs for message parameters.
*/
setMessageParams([lst])
{
/* set each pair from the argument list */
for (local i = 1, local len = lst.length() ; i+1 <= len ; i += 2)
setMessageParam(lst[i], lst[i+1]);
}
/*
* Synthesize a global message parameter name for the given object.
* We'll store the association and return the synthesized name.
*/
synthMessageParam(obj)
{
local nm;
/* synthesize a name */
nm = 'synth' + toString(synthParamID++);
/* store the association */
setMessageParam(nm, obj);
/* return the synthesized name */
return nm;
}
/* synthesized message object parameter serial number */
synthParamID = 1
/*
* Extra message parameters. If a message processor wants to add
* special message parameters of its own, we'll create a lookup
* table for the extra parameters. Message processors might want to
* add their own special parameters to allow referring to objects
* other than the main objects of the command.
*/
extraMessageParams = nil
/*
* Get a list of all the objects that this action should act on if the
* player typed ALL for role (DirectObject, IndirectObject, or perhaps in
* some future version of the library, AccessoryObject. This is the method
* that can be overridden on subclasses to give action-specific
* definitions of ALL.
*/
getAll(cmd, role)
{
/* by default, return everything in scope */
return World.scope.toList();
}
/*
* Get a list of all the objects this action will act on if the player
* types ALL for role (DirectObject or IndirectObject). This is the method
* actually called by the Parser. We first obtain the list of objects
* returned by getAll() and then filter out any objects for which
* hideFromAll(action) is true for this action. Subclasses should normally
* override getAll() rather than this method.
*/
getAllUnhidden(cmd, role)
{
return getAll(cmd, role).subset({x: x.hideFromAll(self) == nil});
}
/*
* Score a set of objects in a given noun role in the action, in
* order to resolve an ambiguous command. Our job, in brief, is to
* READ THE PLAYER'S MIND: we want to figure out which object or
* objects the player is actually referring to when their words are
* ambiguous.
*
* 'cmd' is the Command object describing the command. The various
* object lists (dobjs, iobjs, accs) have been filled in with the
* in-scope objects that match the noun phrase, but these haven't
* been disambiguated yet, so there might be more objects listed than
* will actually be used in the final command.
*
* 'role' tells us the noun phrase role that we're scoring
* (DirectObject, IndirectObject, AccessoryObject, TopicRole,
* LiteralRole).
*
* 'lst' is the match list. This is a Vector containing NPMatch
* objects. There's one NPMatch for each object that we're
* considering as a match for the player's noun phrase.
*
* For each item in the match list, we must set the NPMatch object's
* 'score' property to a number indicating how likely we think it is
* that the player is referring to this object. The higher the
* score, the more likely we think it is. The score value is purely
* relative - the caller will pick the object or objects with the
* highest score.
*
*
*
* We run through the verify routine for each object, which in turn
* runs through the preconditions of that object. We take the returned
* verify score to be the score for the object (or its replacement if
* remapping took place).
*
* Next, we do any verb-specific adjustments via self.actionScore().
*
* Finally, we call each object's scoreObject() routine to give the
* object a chance to make any adjustments for special affinities (or
* aversions).
*/
scoreObjects(cmd, role, lst)
{
local bestScore = 0;
local bestResult = nil;
local verResult;
gAction = cmd.action;
gActor = cmd.actor;
foreach (local i in lst)
{
/* get this object */
local obj = i.obj;
/*
* Get the verify result by running the verify routine on the
* current Command object's action for this object in this role.
*/
verResult = cmd.action.verify(obj, role);
/*
* Compute the score as being the verify result's result rank
* times 100
*/
i.score = verResult.resultRank * 100;
/*
* If this score is greater than the best score we've found so
* far, note the new best score and the new best verify result.
*/
if(i.score > bestScore)
{
bestScore = i.score;
bestResult = verResult;
}
/*
* The verify process could result in the remapping of the
* original object to a new one.
*/
i.obj = verResult.myObj;
}
/*
* Make a note of which object came out best in case it's needed when
* we come to verify the other object.
*/
if(role == DirectObject)
curDobj = bestResult.myObj;
if(role == IndirectObject)
curIobj = bestResult.myObj;
if(role == AccessoryObject)
curAobj = bestResult.myObj;
/* apply verb-specific adjustments */
foreach (local i in lst)
scoreObject(cmd, role, lst, i);
/* apply object-specific adjustments */
foreach (local i in lst)
i.obj.scoreObject(cmd, role, lst, i);
}
/*
* Wraps a list of objects in NPMatch objects so they can be run through
* the scoreObjects method.
*/
wrapObjectsNP(lst)
{
local nplist = [];
foreach(local cur in lst)
{
nplist += new NPMatch(nil, cur, 0);
}
return nplist;
}
/* Build the scope list for this action. */
buildScopeList(whichRole = DirectObject)
{
/* Start with the scope list supplied by the Query object */
scopeList = Q.scopeList(gActor).toList();
/* Add any additional items to scope as special cases if desired. */
addExtraScopeItems(whichRole);
}
/*
* Add extra scope items if this action needs a wider definition of scope
* than normal. By default we simply allow the current actor's current
* location to add additional items to scope if it wishes to.
*/
addExtraScopeItems(role?)
{
gActor.getOutermostRoom.addExtraScopeItems(self);
}
/* Our currently cached list of items in scope for this action. */
scopeList = []
/* Used by Mercury's spelling corrector code. */
spellingPriority = 10
/*
* Can ALL be used with this action? By default we take our value from
* gameMain.allVerbsAllowAll, though basic inventory-handling actions in
* the library will override this. This property is really only relevant
* on TAction and its descendents, but we define it here just to make sure
* no cases are missed.
*/
allowAll = (gameMain.allVerbsAllowAll)
/*
* If we've been redirected here from another action, store a reference to
* that action.
*/
redirectParent = nil
/* Does the command from which we've been redirected allow ALL? */
parentAllowAll = (redirectParent ? redirectParent.allowAll : nil)
/*
* The message to display if an action fails at the check stage (via an
* exit macro) without any other explanatory text being displayed.
*/
failCheckMsg = BMsg(fail check, '{I} {cannot} do that (but the author of
this game failed to specify why).')
/* optional command is not supported in this game */
commandNotPresent()
{
DMsg(command not present, '<.parser>That command isn’t needed
in this story.<./parser> ');
}
/* acknowledge a change in the score notification status */
acknowledgeNotifyStatus(stat)
{
DMsg(acknowledge notify status, '<.parser>Score notifications are now
<<stat ? 'on' : 'off'>>.<./parser> ');
}
/*
* Flag: is this an action that acts on an object even if it is hidden;
* normally this will only apply to debugging actions.
*/
unhides = nil
/*
* This does nothing in the main library but is provided as a hook for the
* objtime extension to use to add to the time taken by implicit actions.
*/
addImplicitTime() { }
/*
* Advance the game clock time. This does nothing in the main library but
* is provided as a hook for the objtime extension to use.
*/
advanceTime() {}
/*
* Method to get the reports to be displayed immediately after any implicit action reports
* that have been stored via a call to reportPostImplicit(). The language-specific part of the
* library should call this method to append the text it returns to the implicit action
* reports it generatees.
*/
getPostImplicitReports()
{
local rep = '';
foreach(local prp in gCommand.postImplicitReports)
rep += ('\n' + prp);
return rep;
}
;
/*
* The SystemAction class is for actions not affecting the game world but
* rather acting on the game session, such as SAVE, RESTORE and QUIT.
*/
class SystemAction: IAction
/* A SystemAction is not normally undo-able */
includeInUndo = nil
/* A SystemAction is not normally repeatable */
isRepeatable = nil
/*
* Since a SystemAction isn't an action in the game world, we don't want
* it to trigger any after action notifications.
*/
afterAction() { }
/*
* Since a SystemAction isn't an action in the game world, we don't want
* it to count as a turn, so we don't run any Daemons or Fuses and we
* don't advance the turn count.
*/
turnSequence() { }
/*
* A SystemAction doesn't take any turns (this is a bit belt-and-braces
* since turnSequence does nothing in any case).
*/
turnsTaken = 0
/*
* Since this isn't an action within the game world we bypass all the
* normal pre-action handling and just execute a reduced cycle.
*/
exec(cmd) { execCycle(cmd); }
/*
* There's no before notifications for a SystemAction so we simply execute
* the action and, if we should define it as repeatable, make a note of it
* in case the player issues an AGAIN command on the next turn.
*/
execCycle(cmd)
{
try
{
/* Display a message if we're debugging actions. */
IfDebug(actions,
"[Executing <<actionTab.symbolToVal(baseActionClass)>> ]\n" );
/* Execute the action. */
execAction(cmd);
/*
* If we're a repeatable action, note that we were the last action
* to be executed.
*/
if(isRepeatable)
libGlobal.lastAction = self.createClone();
}
catch(ExitActionSignal ex)
{
}
catch(ExitSignal ex)
{
actionFailed = true;
}
}
/*
* Ask for an input file. We call the input manager, which displays the
* appropriate local file selector dialog. This is used for SystemActions
* that need a file to act on, such as SAVE, RESTORE and QUIT.
*/
getInputFile(prompt, dialogType, fileType, flags)
{
return inputManager.getInputFile(prompt, dialogType, fileType, flags);
}
;
/*
* An IAction is an Action that doesn't directly act on any objects. At least
* in this version of the library it works just like the base Action class.
*/
class IAction: Action
/*
* There's usually no point in parsing an IAction again when it's repeated
* since there are no objects to have changed.
*/
againRepeatsParse = nil
/*
* For an IAction there's no point in trying to score anything but the
* Actor object; attempting to score objects via their verify properties
* will cause a run-time error, since IActions don't define verify
* properties and the like.
*/
scoreObjects(cmd, role, lst)
{
if(role == ActorRole)
inherited(cmd, role, lst);
else
{
/* apply verb-specific adjustments */
foreach (local i in lst)
scoreObject(cmd, role, lst, i);
/* apply object-specific adjustments */
foreach (local i in lst)
i.obj.scoreObject(cmd, role, lst, i);
}
}
/*
* These methods are provided to allow an IAction to be invoked as an
* implicit action.
*/
execResolvedAction()
{
/*
* Capture the output from this action in case we don't want to
* display it (if we're an implicit action).
*/
local str = gOutStream.captureOutput({: execAction(gCommand) });
/*
* If this action is being performed implicitly, we should display an
* implicit action report for it.
*/
if(isImplicit)
buildImplicitActionAnnouncement(!actionFailed);
/* Otherwise, display the normal output from this action */
else
say(str);
}
execCycle(cmd)
{
try
{
/*
* Add an output filter to display any pending implicit action
* reports before any other text
*/
gOutStream.addOutputFilter(ImplicitActionFilter);
/* Carry out the inherited handling. */
inherited(cmd);
}
finally
{
/*
* Remove the filter that displays pending implicit action
* reports.
*/
gOutStream.removeOutputFilter(ImplicitActionFilter);
}
}
/* Nothing to do here. */
setResolvedObjects([objs]) { }
/*
* An IAction has no resolved objects, so we simply return true to
* indicate that scope is not a problem.
*/
resolvedObjectsInScope() { return true; }
;
/*
* A TravelAction is one that moves (or at least tries to move) the player
* character from one place to another via a command like GO NORTH, or EAST.
*/
class TravelAction: Action
baseActionClass = TravelAction
/*
* Use the inherited handling but first make a note of the direction the
* actor wants to travel in.
*/
execCycle(cmd)
{
/*
* Obtain the direction from the verbProd of the current command
* object, unless this TravelAction already defines its direction
*/
if(!predefinedDirection)
direction = cmd.verbProd.dirMatch.dir;
/* Display a debug message if we're debugging actions. */
IfDebug(actions,
"[Executing <<actionTab.symbolToVal(baseActionClass)>>
<<direction.name>>]\n" );
/* Carry out the inherited handling. */
inherited(cmd);
}
/*
* Does this TravelAction already define a set direction on its direction
* property (so we don't need to look to what direction object the command
* refers)?
*/
predefinedDirection = nil
/*
* Execute the travel command, first carrying out any implicit actions
* needed to facilitate travel
*/
execAction(cmd)
{
/*
* If the actor is not directly in the room, treat OUT as a request to get out of the
* immediate container.
*/
if(outOfNestedInstead)
return;
/*
* If the actor is not directly in the room, make him/her get out of his immediate
* container(s) before attempting travel, unless the actor is in a vehicle.
*/
getOutOfNested();
/*
* Note and if necessary display any other implicit action reports that may have been
* generated prior to executing this action.
*/
"<<buildImplicitActionAnnouncement(true)>>";
/* Carry out the actual travel. */
doTravel();
}
/*
* If the actor is not directly in the room, treat OUT as a request to get out of the
* immediate container.
*/
outOfNestedInstead()
{
if(!gActor.location.ofKind(Room) && direction == outDir)
{
/* Set up a local variable to hold the action we'll use to get out. */
local getOutAction;
/*
* If the actor is on something, s/he needs to get off it, other s/he needs to get out
* of it.
*/
getOutAction = gActor.location.contType == On ? GetOff : GetOutOf;
/*
* Replace our original action (Out) with the appropriate action for getting out of
* our immediate container.
*/
replaceAction(getOutAction, gActor.location);
/* Then return true to say we've handled the command. */
return true;
}
/* Otherwise return nil to tell our caller to carry on with the original command. */
return nil;
}
/*
* If the actor is not directly in the room, make him/her get out of his immediate
* container(s) before attempting travel, unless the actor is in a vehicle.
*/
getOutOfNested()
{
local traveler = TravelConnector.getTraveler(gActor);
while(!gActor.location.ofKind(Room) && traveler == gActor)
{
/* Note the actor's current location. */
local loc = gActor.location;
/*
* The action needed to remove the actor from its immediate
* container.
*/
local getOutAction = loc.contType == On ? GetOff : GetOutOf;
/*
* Try to get the actor out of his/her current location with an
* implicit action.
*/
tryImplicitAction(getOutAction, loc);
/* Note and if necessary display the implicit action report. */
"<<buildImplicitActionAnnouncement(true)>>";
/*
* if the command didn't work, quit the loop or we'll be stuck in
* it forever.
*/
if(gActor.location == loc)
exit;
}
}
/*
* Carry out travel in direction. For this purpose we first have to define
* what the corresponding direction property of the actor's current
* location refers to. If it's nil, no travel is possible, and we simply
* display a refusal message. If it's an object we execute its travelVia()
* method for the current actor. If it's a double-quoted string or a
* method we execute it and make a note of where the actor ends up, if the
* actor is the player character. If it's a single-quoted string we
* display it.
*
* Note that we only display the various messages announcing failure of
* travel if the actor is the player character. We presumably don't want
* to see these messages as the result of NPCs trying to move around the
* map.
*/
doTravel()
{
/* Note the actor's current location. */
local loc = gActor.getOutermostRoom;
/*
* If we point to an object, assume it's a travel connector and attempt travel via the
* connector.
*/
if(loc.propType(direction.dirProp) == TypeObject)
doTravelViaConn(loc);
/*
* Otherwise, our direction of travel is trying to take us towards nil, a string, or a
* method, in which case call the nonTravel() function to handle it.
*/
else
nonTravel(loc, direction);
}
doTravelViaConn(loc)
{
/*
* Note whether the current location is illuminated, or whether it permits travel in the
* dark (in which case we treat it as illuminated for the purposes of allowing travel).
*/
local illum = loc.allowDarkTravel || loc.isIlluminated;
/* Note our connector */
local conn = loc.(direction.dirProp);
/*
* If the connector is visible to the actor then attempt travel via the connector.
*/
if(conn.isConnectorVisible)
doVisibleTravel(conn);
/*
* Otherwise if there's light enough to travel and the actor is the player character,
* display the standard can't travel message (as if the connector wasn't there.
*/
else if(illum && gActor == gPlayerChar)
loc.cannotGoThatWay(direction);
/*
* Otherwise if the actor is the player character, display the standard message forbidding
* travel in the dark.
*/
else if(gActor == gPlayerChar)
loc.cannotGoThatWayInDark(direction);
}
doVisibleTravel(conn)
{
/* if the actor is the player char, just carry out the travel */
if(gActor == gPlayerChar)
conn.travelVia(gActor);
/*
* otherwise carry out the travel and display the appropriate travel notifications.
*/
else
gActor.travelVia(conn);
}
/*
* The direction the actor wants to travel in. This is placed here by the
* execCycle method and takes the form of A Direction object, e.g.
* northDir.
*/
direction = nil
/* It's generally possible to undo a travel command. */
canUndo = true
;
/*
* This function can be called from a check nethod to prevent the display of text from within the
* check method halting the action. Calling it from anywhere else will have no effect. It's use is
* in conjunction with the TAction class defined immediatelty below.
*/
noHalt()
{
/*
* If we have a current gAction, set its haltOnMessageCheck property to nil. Note that this
* property is set to true near the start of TAction.check() so that it starts out true and
* remains so unless noHalt() intervenes during the course of the check() stage.
*/
if(gAction)
gAction.haltOnMessageInCheck = nil;
}
/*
* A TAction is an action that applies to a single direct object. Other action
* classes that apply to more than one object, such as TIAction, inherit from
* this class so some of the code needs to take that into account.
*/
class TAction: Action
/*
* A list of the direct objects of this action that make it to the report
* stage.
*/
reportList = []
/*
* A list of the direct objects of this action that make it to the action
* stage.
*/
actionList = []
/*
* A LookupTable containing the verify results for this action. This is
* keyes on the object being verified, with the value being the worst
* verify result encountered for that object so far.
*/
verifyTab = nil
/*
* Store the last verify failure message so that if we get several
* identical ones in a row, we don't keep repeating them
*/
lastVerifyMsg = nil
/*
* set this property to true if you want to announce the object before the
* action result when there's more than one object. If the action routine
* summarizes the result at the end you don't want to do this so you
* should then set this to nil.
*/
announceMultiAction = nil
/* The current direct object of this action */
curDobj = nil
/*
* The current object being processed (in a TAction, always the curDObj;
* in a TI Action either the curDobj or the curIOoj).
*/
curObj = nil
/*
* Reset values to their starting state when an action is used to execute
* a new command.
*/
reset()
{
scopeList = [];
reportList = [];
actionList = [];
verifyTab = nil;
isImplicit = nil;
curDobj = nil;
curObj = nil;
lastVerifyMsg = nil;
redirectParent = nil;
}
/*
* Information to allow the DEBUG ACTIONS command to express a complete
* topic phrase
*/
#ifdef __DEBUG
dqinfo = ''
iqinfo = ''
aqinfo = ''
#endif
/*
* Execute the command cycle for this action. This differs from the base
* Action class in not calling beforeAction directly, since the
* beforeAction() notifications occur within the execResolvedAction
* method.
*/
execCycle(cmd)
{
/* If we're debugging actions, display some debugging information. */
IfDebug(actions,
"[Executing <<actionTab.symbolToVal(baseActionClass)>> :
<<dqinfo>> <<cmd.dobj.name>> <<if cmd.iobj != nil>>
: <i><<iqinfo>></i> <<cmd.iobj.name>> <<end>>
<<if cmd.acc != nil>>
: <i><<aqinfo>></i> <<cmd.acc.name>> <<end>>]\n" );
/*
* Disallow ALL (e.g. EXAMINE ALL) if the action does not permit it.
* Since we don't want to block plural matches (for which
* cmd.matchedAll is also true) we all test for the presence of 'all'
* among the command tokens.
*/
if(cmd.matchedAll && !(allowAll || parentAllowAll) )
{
DMsg(all not allowed, 'Sorry; ALL is not allowed with this command.
');
abort;
}
try
{
/* Execute the action. */
execAction(cmd);
/*
* If we're a repeatable action, note that we were the last action
* performed (for use with an AGAIN command).
*/
if(isRepeatable)
libGlobal.lastAction = self.createClone();
}
catch(ExitSignal ex)
{
actionFailed = true;
}
}
/* Execute this action */
execAction(cmd)
{
/*
* Note the current direct object, which should be the direct object
* supplied by the current Command object.
*/
curDobj = cmd.dobj;
/*
* Note the current direct object as a possible antecedent for
* pronouns.
*/
notePronounAntecedent(curDobj);
/* Execute the action with the current direct object. */
execResolvedAction();
}
/*
* Execute this action with a known direct object or objects. Call this
* method when there's no need to resolve the objects used in the command
* but we still want it to pass through every stage
*/
execResolvedAction()
{
/* Create a new LookupTable for our verify results. */
verifyTab = new LookupTable;
/*
* We shouldn't really need to catch any signals here, but the author
* might put an exitAction macro in a check method, say, so we need to
* be able to handle it.
*/
try
{
/*
* Obtain the verify result for the current direct object. Note at
* this point the objects have already been resolved so we're only
* interested in whether the verify command is going to allow the
* action to go ahead. If it doesn't allow the action return nil
* to stop it here.
*/
if(!verifyObjRole(curDobj, DirectObject))
return nil;
/*
* If gameMain defines the option to run the before notifications
* before the check stage, run the before notifications now.
*/
if(gameMain.beforeRunsBeforeCheck)
beforeAction();
/*
* Try the check stage. If the action fails the check stage, stop
* the action here and return nil to tell our caller this action
* has failed.
*/
if(!checkAction(cmd))
return nil;
/*
* If gameMain defines the option to run the before notifications
* after the check stage, run the before notifications now.
*/
if(!gameMain.beforeRunsBeforeCheck)
beforeAction();
/* Carry out the action on a single direct object. */
doActionOnce();
/* Return true to tell our caller the action succeeded. */
return true;
}
catch (ExitActionSignal ex)
{
return nil;
}
}
/*
* Flag: do we want the object name to appear before a check stage failure
* message if multiple objects are involved in the action. By default we
* do, otherwise it might not be clear which object the message referes
* to.
*/
announceMultiCheck = true
/*
* Run the check phase of the action, both on the direct object and on any
* preconditions.
*/
checkAction(cmd)
{
/*
* Try the check phase of any preconditions. If that fails return nil
* to indicate failure of the entire check stage.
*/
if(!checkPreCond(curDobj, preCondDobjProp))
{
return nil;
}
/*
* Then try the check method on the current direct object and return
* the result.
*/
return check(curDobj, checkDobjProp);
}
/*
* This flag is used internally by the library to track whether the output of any text from a
* check() should stop the action, which it normally should. Game code should not directly
* override this property or change its value, other than indrectly via the noHalt() function.
*/
haltOnMessageInCheck = true
/*
* Call the check method (checkProp) on the appropriate object (obj).
* Return true to indicate that the action succeeds or nil otherwise
*/
check(obj, checkProp)
{
local checkMsg = nil;
/* Note which object is the current object of the command. */
curObj = obj;
/* Run the check method on the object and capture its output */
try
{
/* Set this flag to true - the check routine may set it to nil. */
haltOnMessageInCheck = true;
checkMsg = gOutStream.captureOutputIgnoreExit({: obj.(checkProp)});
}
/*
* Game authors aren't meant to use the exit macro in check methods,
* but in case they do we handle it here.
*/
catch (ExitSignal ex)
{
/*
* If for some reason a check method uses exit without displaying
* a method, we supply a dummy failure message at this point.
*/
if(checkMsg is in (nil, ''))
checkMsg = failCheckMsg;
}
/*
* If the check method tried to display something then it wants to
* block the action, so we display the failure message and stop the
* action.
*/
if(checkMsg not in (nil, ''))
{
/*
* If this action wants to announce the object of the action when it fails at the
* check stage and our Command is processing more than one direct object, and we don't
* want to report failed attempts after successful ones, announce the object.
*/
if(announceMultiCheck && gCommand.dobjs.length > 1 && !reportFailureAfterSuccess)
announceObject(obj);
/*
* If we're an implicit action then add a failure message to our
* implicit action list and display the list ("first trying
* to...")
*/
if(isImplicit)
"<<buildImplicitActionAnnouncement(nil)>>";
else if(haltOnMessageInCheck)
/* first flush any pending implicit action reports */
"<<buildImplicitActionAnnouncement(true)>>";
/*
* Display our failure message. If this command is processing more than one direct
* object, and we want to report failed attempts after successrul ones, use
* reportAfter() so that the failure reports come after the report of any actions that
* were successful, otherwise display the failure message straight away.
*/
if(gCommand.dobjs.length > 1 && reportFailureAfterSuccess)
{
if(announceMultiCheck)
checkMsg = gOutStream.captureOutputIgnoreExit({: announceObject(obj)}) +
checkMsg;
reportAfter(checkMsg);
}
else
{
say(checkMsg);
"\n";
}
/*
* Note the outcome of the action -- it failed unless haltOnMesageInCheck was set to
* nil.
*/
actionFailed = haltOnMessageInCheck;
/*
* Return the opposite of haltOnMessageInCheck to tell our caller whether this action
* failed the check stage
*/
return !haltOnMessageInCheck;
}
/*
* Return true to tell our our caller this action passed the check
* stage on this object.
*/
return true;
}
/*
* Flag: when a command processes multiple direct objects, do we want any failed attempts to
* be reported after successful ones?
*/
reportFailureAfterSuccess = nil
/* Run the check stage on the preCondProp of obj */
checkPreCond(obj, preCondProp)
{
local preCondList;
local checkOkay = true;
/* Note which object we're checking */
curObj = obj;
/*
* Construct a list or preCondition objects on the appropriate object
* property.
*/
preCondList = valToList(obj.(preCondProp));
/* Sort the list in preCondOrder */
preCondList = preCondList.sort(nil,
{a, b: a.preCondOrder - b.preCondOrder});
try
{
/* Iterate through the list to see if all the checks are satisfied */
foreach(local cur in preCondList)
{
/*
* If we fail the check method on any precondition object,
* note the failure and stop the iteration.
*/
if(cur.checkPreCondition(obj, true) == nil)
{
checkOkay = nil;
break;
}
}
}
/*
* Game authors aren't meant to use the exit macro in check methods,
* but in case they do we handle it here.
*/
catch (ExitSignal ex)
{
checkOkay = nil;
}
/*
* If the check method failed on any of our precondition objects note
* that the action is a failure.
*/
if(checkOkay == nil)
actionFailed = true;
/* Return our overall check result. */
return checkOkay;
}
/* Carry out the action phase on the direct object */
doActionOnce()
{
local msg;
/*
* If we're iterating over several objects and we're the kind of
* action which wants to announce objects in this context, do so.
*/
if(announceMultiAction && gCommand.dobjs.length > 1)
announceObject(curDobj);
/* Note that the current object is the direct object */
curObj = curDobj;
/*
* Add the ImplicitActionFilter to the current output stream so that
* any pending implicit action reports are prepended to any action
* reports output at this stage.
*/
if(isImplicit)
buildImplicitActionAnnouncement(true, nil);
/*
* If the action method displays anything then we don't add this
* object to the list of objects to be reported on at the report
* stage, on the assumption that the action stage has either produced
* its own report for this object or reported on the failure of the
* action. If, however, the action is carried out silently then we'll
* add this object to the list of objects to be reported on at the
* report stage.
*
* NOTE TO SELF: Don't try making this work with captureOutput(); it
* creates far more hassle than it's worth!!!!
*/
try
{
gOutStream.addOutputFilter(ImplicitActionFilter);
msg = gOutStream.watchForOutput({: doAction() });
}
finally
{
/* Remove any implicit action announcement from the output stream */
gOutStream.removeOutputFilter(ImplicitActionFilter);
}
/*
* If there's no output from the action method, add this object to the
* list of objects to be reported on at the report stage.
*/
if(!(msg))
{
reportList += curDobj;
}
/* Note that we've carried out the action on this object. */
actionList += curDobj;
/*
* Return true to tell our caller we succesfully completed the action.
*/
return true;
}
doAction()
{
try
{
curDobj.(actionDobjProp);
}
catch(ExitActionSignal ex)
{
// actionFailed = true;
}
}
/*
* Flag, do we want to announce the object name before the verify message
* in cases where there's more direct object in the command? By default we
* don't since verify messages generally make it clear enough which
* objects they refer to.
*/
announceMultiVerify = nil
/*
* Return a list of direct objects corresponding to the word ALL in the
* player's command. By default we return everything in scope that isn't a
* a Room.
*/
getAll(cmd, role)
{
return scopeList.subset({ x: !x.ofKind(Room)});
}
/*
* Add a verify result to this action's verify table. This method is
* normally called by one of the macros (logical, illogical, logicalRank,
* etc.) use in an object's verify routine.
*/
addVerifyResult(verRes)
{
/* Note the object to which this verify result relates. */
local obj = verRes.myObj;
/*
* If it isn't the object we're currently meant to be verifying,
* adjust it.
*/
if(obj != verifyObj)
{
obj = verifyObj;
verRes.myObj = obj;
}
/*
* If we don't currently have a verify table for this action, create
* one
*/
if(verifyTab == nil)
verifyTab = new LookupTable();
/*
* Add this verify result to this action's verify table only if it
* doesn't already contain a verify result for the same object with a
* lower resultRank.
*/
if(!verifyTab.isKeyPresent(obj) ||
verRes.resultRank < verifyTab[obj].resultRank)
verifyTab[obj] = verRes;
}
/*
* reportAction() is called only after all the action routines have been
* run and the list of dobjs acted on is known. It only does anything if
* the action is not implicit. It can thus be used to summarize a list of
* identical actions carried out on every object in reportList or to print
* a report that is not wanted if the action is implicit. By default we
* call the dobj's reportDobjProp to handle the report.
*
* Note that this method is usually called from the current Command object
* after its finished iterated over all the direct objects involved in the
* command.
*/
reportAction()
{
/*
* If we're not an implicit action and there's something in our report
* list to report on, execute the report stage of this action.
*/
if(!isImplicit && reportList.length > 0)
{
/* Output any pending implicit action reports */
"<<buildImplicitActionAnnouncement(true)>>";
curDobj.(reportDobjProp);
}
}
/* install the resolved objects in the action */
setResolvedObjects(dobj)
{
curDobj = dobj;
}
/* Check whether the resolved objects for this action are in scope */
resolvedObjectsInScope()
{
buildScopeList();
return scopeList.indexOf(curDobj) != nil;
}
/*
* Get a message parameter object for the action. We define 'dobj'
* as the direct object, in addition to any inherited targets.
*/
getMessageParam(objName)
{
switch(objName)
{
case 'dobj':
/* return the current direct object */
return curDobj;
case 'cobj':
/* return the current object */
return curObj;
default:
/* inherit default handling */
return inherited(objName);
}
}
/*
* A convenience method for putting every game object in scope, which may
* be appropriate for certain commands (not least, certain debugging
* commands). It's intended to be called from addExtraScopeItems when
* needed. */
makeScopeUniversal()
{
/* Note the fist object of the Thing class. */
local obj = firstObj(Thing);
/* Create a vector to store our results. */
local vec = new Vector;
/* Go through every Thing in the game and add it to our vector. */
do
{
vec.append(obj);
obj = nextObj(obj, Thing);
} while (obj!= nil);
/*
* Convert the vector to a list and append it to our scopeList,
* removing any duplicates.
*/
scopeList = scopeList.appendUnique(vec.toList());
}
;
/*
* A TIAction is an action that applies to both a direct object and an
* indirect object. Since it inherits from TAction we only need to define the
* additional methods and properties relating to the handling of indirect
* objects.
*/
class TIAction: TAction
/* The current indirect object of this action. */
curIobj = nil
/* The various methods to call on the indirect object of this action. */
verIobjProp = nil
checkIobjProp = nil
actionIobjProp = nil
preCondIobjProp = nil
/*
* A list of the indirect objects that this actually actually ends up
* acting on at the action stage.
*/
ioActionList = []
/*
* Flag: should we resolve the indirect object of this action before the
* direct object?
*/
resolveIobjFirst = true
/* Reset the action variables to their initial state. */
reset()
{
inherited;
curIobj = nil;
ioActionList = [];
}
/* execute this action. */
execAction(cmd)
{
/*
* Note the current direct object of this command from the Command
* object.
*/
curDobj = cmd.dobj;
/*
* Note the current indirect object of this command from the Command
* object.
*/
curIobj = cmd.iobj;
/* Note both objects as possible pronoun antecedents. */
notePronounAntecedent(curDobj, curIobj);
/* execute the resolved action. */
execResolvedAction();
}
/* Carry out the check phase for this command. */
checkAction(cmd)
{
/*
* If we don't pass the check stage on both the iobj and the dobj's
* preconditions, then return nil to tell our caller we've failed this
* stage.
*/
if(!(checkPreCond(curIobj, preCondIobjProp)
&& checkPreCond(curDobj, preCondDobjProp)))
return nil;
/*
* Return the result of running the check phase on both the indirect
* and the direct objects.
*/
return check(curIobj, checkIobjProp) && check(curDobj, checkDobjProp);
}
/* Set the resolved objects for this action. */
setResolvedObjects(dobj, iobj)
{
curDobj = dobj;
curIobj = iobj;
}
/*
* Test whether both the direct and the indirect objects for this action
* are in scope.
*/
resolvedObjectsInScope()
{
buildScopeList();
return scopeList.indexOf(curDobj) != nil
&& scopeList.indexOf(curIobj) != nil;
}
/*
* Carry out the report phase for this action. If there's anything in the
* ioActionList and we're not an implicit action, call the report method
* on the indirect object. Then carry out the inherited handling (which
* does the same on the direct object). Note that this method is called by
* the current Command object once its finished iterating over all the
* objects involved in the command.
*/
reportAction()
{
/*
* Carry out the inherited handling, which executes the report stage
* on the direct object.
*/
inherited;
/*
* If we're not an implicit action and there's something to report on,
* carry out the report stage on our indirect object.
*/
if(!isImplicit && ioActionList.length > 0)
curIobj.(reportIobjProp);
}
/* Get the message parameters relating to this action */
getMessageParam(objName)
{
switch(objName)
{
case 'iobj':
/* return the current indirect object */
return curIobj;
default:
/* inherit default handling */
return inherited(objName);
}
}
/*
* Execute this action as a resolved action, that is once its direct and
* indirect objects are known.
*/
execResolvedAction()
{
try
{
/*
* If the indirect object was resolved first (before the
* direct object) then we run the verify stage on the indirect
* action first. If it fails, return nil to tell the caller it
* failed.
*/
if(resolveIobjFirst && !verifyObjRole(curIobj, IndirectObject))
return nil;
/*
* Run the verify routine on the direct object next. If it
* disallows the action, stop here and return nil.
*/
if(!verifyObjRole(curDobj, DirectObject))
return nil;
/*
* If the indirect object was resolved after the direct
* object, run the verify routines on the indirect object now, and
* return nil if they disallow the action.
*/
if(!resolveIobjFirst && !verifyObjRole(curIobj, IndirectObject))
return nil;
/*
* If gameMain defines the option to run before notifications
* before the check stage, run the before notifications now.
*/
if(gameMain.beforeRunsBeforeCheck)
beforeAction();
/*
* Try the check stage on both objects. If either disallows the
* action return nil to stop the action here.
*/
if(!checkAction(nil))
return nil;
/*
* If gameMain defines the option to run before notifications
* after the check stage, run the before notifications now.
*/
if(!gameMain.beforeRunsBeforeCheck)
beforeAction();
/* Carry out the action stage on one set of objects */
doActionOnce();
/* Return true to tell our caller the action was a success */
return true;
}
catch (ExitActionSignal ex)
{
// actionFailed = true;
return nil;
}
}
/*
* Execute the action phase of the action on both objects. Note that
* although some TIActions can operate on multiple direct objects, none
* defined in the library acts on multiple indirect objects, so there's
* only minimal support for the latter possibility.
*/
doActionOnce()
{
local msgForDobj, msgForIobj;
/*
* If we're iterating over several objects and we're the kind of
* action which wants to announce objects in this context, do so.
*/
if(announceMultiAction && gCommand.dobjs.length > 1)
announceObject(curDobj);
/*
* Note that the current object we're dealing with is the direct
* object.
*/
curObj = curDobj;
/*
* Add the ImplicitActionFilter to the current output stream so that
* any pending implicit action reports are prepended to any action
* reports output at this stage.
*/
if(isImplicit)
buildImplicitActionAnnouncement(true, nil);
try
{
/*
* First add the ImplicitActionFilter to the output stream so that
* any text output from the action routines are preceeded by any
* pending implicit action reports.
*/
gOutStream.addOutputFilter(ImplicitActionFilter);
/*
* Run the action routine on the current direct object and capture
* the output for later use. If the output is null direct object
* can be added to the list of objects to be reported on at the
* report stage, provided the iobj action routine doesn't report
* anything either.
*
* NOTE TO SELF: Don't try making this work with captureOutput();
* it creates far more hassle than it's worth!!!!
*/
msgForDobj =
gOutStream.watchForOutput({:curDobj.(actionDobjProp)});
/* Note that we've acted on this direct object. */
actionList += curDobj;
/* Note that the current object is now the indirect object. */
curObj = curIobj;
/*
* Execute the action method on the indirect object. If it doesn't
* output anything, add the current indirect object to
* ioActionList in case the report phase wants to do anything with
* it, and add the dobj to the reportList if it's not already
* there so that a report method on the dobj can report on actions
* handled on the iobj.
*/
msgForIobj =
gOutStream.watchForOutput({:curIobj.(actionIobjProp)});
}
finally
{
/* Remove any implicit action announcement from the output stream */
gOutStream.removeOutputFilter(ImplicitActionFilter);
}
/*
* If neither the action stage for the direct object nor the action
* stage for the direct object produced any output then add the
* indirect object to the list of indirect objects that could be
* reported on, and add the current direct object to the list of
* direct objects to be reported on at the report stage.
*/
if(!(msgForDobj) && !(msgForIobj))
{
ioActionList += curIobj;
reportList = reportList.appendUnique([curDobj]);
}
/*
* Return true to tell our caller we completed the action
* successfully.
*/
return true;
}
;
/*
* A LiteralAction is an action that acts on a single literal object, e.g.
* TYPE HELLO
*/
class LiteralAction: IAction
exec(cmd)
{
/* Note the literal string associated with this command. */
literal = cmd.dobj.name;
/* carry out the inherited handling. */
inherited(cmd);
}
/* The string literal on which this command is operating. */
literal = nil
/* The numerical value of our literal */
num = tryNum(literal)
;
/*
* A LiteralTAction is an action that involves one physical object and one
* string, e.g. TYPE HELLO ON TERMINAL.
*/
class LiteralTAction: TAction
execAction(cmd)
{
/*
* Determine which is the Thing-based object and which is the literal
* value and plug each into the right slot (so that the Thing ends up
* as the direct object of the command and the string as the literal).
*/
if(cmd.dobj.ofKind(Thing))
{
curDobj = cmd.dobj;
literal = cmd.iobj.name;
}
else
{
curDobj = cmd.iobj;
literal = cmd.dobj.name;
}
/* Note the direct object as an antecedent for pronouns */
notePronounAntecedent(curDobj);
/* Execute the resolved action (as for a TAction) */
execResolvedAction();
}
/*
* Whichever object slot a verify routine is notionally trying to verify
* for given the grammatical form of the command, in practice only the
* direct object (the thing involved in the command) can be verified. E.g.
* for WRITE FOO ON BALL we treat BALL as the direct object of the command
* and FOO as the literal, even if the Parser thinks it needs to verify
* the Indirect Object to disambiguate BALL.
*/
verify(obj, role)
{
return inherited(obj, DirectObject);
}
/* The literal value associated with this command */
literal = nil
/* The numerical value of our literal */
num = tryNum(literal)
;
/*
* A TopicTAction is an action involving one physical object and one topic,
* e.g. ASK BOB ABOUT TOWER.
*/
class TopicTAction: TAction
execAction(cmd)
{
/*
* determine which is the Thing-type object and which is the topic
* value and plug each into the right slot. We ensure that the
* physical object (the Thing) ends up as the direct object and the
* ResolvedTopic as the indirect object.
*/
if(cmd.dobj && cmd.dobj.ofKind(Thing))
{
curDobj = cmd.dobj;
curIobj = cmd.iobj;
curTopic = cmd.iobj;
}
else
{
curDobj = cmd.iobj;
curIobj = cmd.dobj;
curTopic = cmd.dobj;
}
/* Note the direct object as a potential pronoun antecedent. */
notePronounAntecedent(curDobj);
/* Attempt to resolve any pronouns within the ResolvedTopic */
resolvePronouns();
/* Execute the action as for a TAction */
execResolvedAction();
}
/*
* Although we don't have an indirect object in the conventional sense, we
* use the curIobj property to store the ResolvedTopic involved in the
* command.
*/
curIobj = nil
/*
* We also store the current ResolvedTopic in the curTopic property so it
* can be found by the gTopic macro.
*/
curTopic = nil
/*
* This is a bit of a kludge to deal with the fact that the Parser doesn't
* seem able to resolve pronouns within ResolvedTopics. We do it here
* instead.
*/
resolvePronouns()
{
if(curIobj == nil)
return;
for(local cur in curIobj.topicList, local i = 1;; ++i)
{
if(cur == Him && curDobj.isHim)
curIobj.topicList[i] = curDobj;
if(cur == Her && curDobj.isHer)
curIobj.topicList[i] = curDobj;
if(cur == It && curDobj.isIt)
curIobj.topicList[i] = curDobj;
if(cur == Them && (curDobj.plural || curDobj.ambiguouslyPlural))
curIobj.topicList[i] = curDobj;
}
}
/*
* Whichever object slot a verify routine is notionally trying to verify
* for given the grammatical form of the command, in practice only the
* direct object (the thing involved in the command) can be verified. E.g.
* for WRITE FOO ON BALL we treat BALL as the direct object of the command
* and FOO as the literal, even if the Parser thinks it needs to verify
* the Indirect Object to disambiguate BALL.
*/
verify(obj, whichObj)
{
return inherited(obj, DirectObject);
}
/*
* Is the topic the grammatical Indirect object of this command? This is
* used by Redirector.doOtherAction() to encapsulate the appropriate
* string in a ResolvedTopic. The topic is the grammatical iobj if its the
* second object involved in the commamd, e.g. ASK BOB ABOUT FIRE, where
* FIRE is the topic.
*/
topicIsGrammaticalIobj = true
;
/*
* A NumericTAction is an action that involves one physical object and one
* number, e.g. DIAL 1234 ON PHONR.
*/
class NumericTAction: TAction
execAction(cmd)
{
/*
* Determine which is the Thing-based object and which is the numeric
* value and plug each into the right slot (so that the Thing ends up
* as the direct object of the command and the number as the num).
*/
if(cmd.dobj.ofKind(Thing))
{
curDobj = cmd.dobj;
num = cmd.iobj.numVal;
}
else
{
curDobj = cmd.iobj;
num = cmd.dobj.numVal;
}
/* Note the direct object as an antecedent for pronouns */
notePronounAntecedent(curDobj);
/* Execute the resolved action (as for a TAction) */
execResolvedAction();
}
/*
* Whichever object slot a verify routine is notionally trying to verify
* for given the grammatical form of the command, in practice only the
* direct object (the thing involved in the command) can be verified. E.g.
* for WRITE FOO ON BALL we treat BALL as the direct object of the command
* and FOO as the literal, even if the Parser thinks it needs to verify
* the Indirect Object to disambiguate BALL.
*/
verify(obj, role)
{
return inherited(obj, DirectObject);
}
/* The numeric value associated with this command */
num = nil
;
/*
* A TopicAction is an action referring to a single Topic (e.g. TALK ABOUT THE
* TOWER). It behaves almost exactly like an IAction.
*/
class TopicAction: IAction
exec(cmd)
{
/*
* For a TopicAction the ResolvedTopic will be in the dobj property of
* the cmd object. Store it in the curTopic property.
*/
curTopic = cmd.dobj;
/* Then carry out the inherited handling. */
inherited(cmd);
}
/* The ResolvedTopic object associated with this action. */
curTopic = nil
;
/*
* A NumericAction is an action referring to a single Number (e.g. Footnote
* 1). It behaves almost like an IAction.
*/
class NumericAction: IAction
exec(cmd)
{
/* Note the number associated with this command. */
num = cmd.dobj.numVal;
/* carry out the inherited handling. */
inherited(cmd);
}
/* The number on which this command is operating. */
num = nil
;
/* Try action as an implicit action with [objs] as its objects */
tryImplicitAction(action, [objs])
{
local oldAction;
/*
* Create a new copy of the action we're to try executing so we don't
* contaminate the properties of the same action if it'e being used
* elsewhere in the call chain.
*/
action = action.createInstance();
/* Our new action will be an implict action. */
action.isImplicit = true;
/* Note the previous action being executed. */
oldAction = gAction;
/* install the resolved objects in the action */
action.setResolvedObjects(objs...);
// action.reportImplicitActions = action.formerReportImplicitActions;
/*
* For an implicit action, we must check the objects involved to make
* sure they're in scope. If any of the objects aren't in scope,
* there is no way the actor would know to perform the command, so
* the command would not be implied in the first place. Simply fail
* without trying the command.
*/
if (!action.resolvedObjectsInScope())
return nil;
/*
* Note that the previous current action is our new action's parent action
*/
action.parentAction = gAction;
/* Make our new action the current action. */
gAction = action;
try
{
/* Execute our new action. */
action.execResolvedAction();
/* Provide a hook for the objtime extension to use. */
action.addImplicitTime();
/*
* If all went well, return true to indicate that we were able to
* execute the action.
*/
return true;
}
/*
* If the action threw an AbortImplicitSignal this means that its verify
* routine does not allow the action to be carried out implicitly; return
* nil to signal that we weren't allowed to attempt this implicit action.
*/
catch (AbortImplicitSignal ex)
{
return nil;
}
finally
{
/* Restore the original current action. */
gAction = oldAction;
}
}
/*
* Have an actor other than the current gActor try an implicit action (e.g. if
* an npc moving as the result of an AgendaItem needs to implicitly open a
* door to proceed): actor is the actor performing the action, action is the
* action object to be performs, [objs] is the list of objects (if any) on
* which the action is to be performed.
*/
tryImplicitActorAction(actor, action, [objs])
{
/*
* Set up a local variable to store the result of trying the implicit
* action.
*/
local res = nil;
/* Make a note of the current actor of the current main command. */
local oldActor = gActor;
try
{
/* Temporarily make gActor the actor passed to this function. */
gActor = actor;
/* Try the implicit action with this actor and store the result. */
res = tryImplicitAction(action, objs...);
}
finally
{
/* Restore the original gActor */
gActor = oldActor;
}
/* Return the result of attempting the implicit action. */
return res;
}
/* ------------------------------------------------------------------------ */
/*
* Run a replacement action.
*/
replaceAction(action, [objs])
{
/* run the replacement action as a nested action */
execNestedAction(true, gActor, action, objs...);
/* the invoking command is done */
exit;;
}
/* Run a replacement action for another actor. */
replaceActorAction(actor, action, [objs])
{
/* run the replacement action as a nested action */
execNestedAction(true, actor, action, objs...);
/* the invoking command is done */
exit;
}
/*
* Run a nested action; execution of the parent action continues once the
* nested action is complete.
*/
nestedActorAction(actor, action, [objs])
{
execNestedAction(nil, actor, action, objs...);
}
/* Run a nested action for the current actor. */
nestedAction(action, [objs])
{
execNestedAction(nil, gActor, action, objs...);
}
/*
* Execute a fully-constructed nested action.
*
* 'isReplacement' indicates whether the action is a full replacement or
* an ordinary nested action. If it's a replacement, then we use the
* game time taken by the replacement, and set the enclosing action
* (i.e., the current gAction) to take zero time. If it's an ordinary
* nested action, then we consider the nested action to take zero time,
* using the current action's time as the overall command time.
*
* 'isRemapping' indicates whether or not this is a remapped action. If
* we're remapping from one action to another, this will be true; for
* any other kind of nested or replacement action, this should be nil.
*/
execNestedAction(isReplacement, actor, action, [objs])
{
local oldAction;
local oldActor = gActor;
/*
* Create a new instance of the desired action, so we don't override the
* current state of any similar action higher up the calling chain.
*/
action = action.createInstance();
/* Make the new action make a note of its parent action */
action.parentAction = gAction;
/*
* Treat us an an implicit action if the current (parent) action is
* implicit.
*/
action.isImplicit = gAction.isImplicit;
/* Note the previous (calling) action. */
oldAction = gAction;
/* Set the current actor to the value of our actor parameter. */
gActor = actor;
/* Install the new actor on the current Command object. */
gCommand.actor = actor;
/*
* Change the current Command object's action to the new action with its
* new objects.
*/
gCommand.changeAction(action, objs.element(1), objs.element(2),
objs.element(3));
/* If our objects aren't in scope we can't proceed with the action. */
if (objs.length > 0 && !action.resolvedObjectsInScope())
return nil;
try
{
/* Execute the new action */
action.execAction(gCommand);
/*
* In principle we only want to show the reportAction report if we're
* not a replacement action, leaving a replacement action to display
* its report in the normal course of the Command's action-processing
* cycle, but there are certain circumstances where even a replacement
* action needs to display its reportAction here, specifically (1) if
* there's been a change of actor (in which case replaceAction has
* arguably been misused) or (2) if an object announcement has just
* been displayed for the parent action, in which case we need to
* ensure that the report corresponding to the object announcement is
* displayed immediately after the object name (by displaying the
* report straight away here).
*/
if(!isReplacement || gActor != oldActor ||
(action.parentAction.announceMultiAction && gCommand.dobjs.length > 1))
{
/* report the outcome of the action. */
action.reportAction();
/*
* If this is a replacement action, there won't be anything
* printed after reportAction displays a report so we need to
* print a newline in case there's another object.
*/
if(isReplacement)
"\n";
/*
* Empty the report list to ensure the report isn't duplicated
* later
*/
action.reportList = [];
}
/*
* Return true to indicate that the action was completed successfully.
*/
return true;
}
catch (AbortImplicitSignal ex)
{
/* Return nil to indicate failure. */
return nil;
}
finally
{
/*
* If we're not a replacement action we need to restore the old
* gAction when we're done; and if we're a nested action and not
* implicit, then we should show our action reports (if any) before
* handing back to the main action. We also need to do this if we've
* changed actor, since unpredictable results could occur from
* substituting an action by one actor with one by another.
*
* We try to avoid doing this if this is a replacement action, because
* if possible we want all aspects of the new action, including its
* reporting and after action processing.
*/
if(!isReplacement || gActor != oldActor)
{
/* Restore the original action on the current Command object. */
gCommand.action = gCommand.originalAction;
/* Restore the original current action. */
gAction = oldAction;
/* Restore the original actor. */
gActor = oldActor;
/* Restore the original actor on the Command object. */
gCommand.actor = oldActor;
}
}
}
/*
* Ask for a missing object to fulfil role in action. First see if there's a
* uniquely best match to fill the role, and if so execute the action with
* that object. Otherwise ask the player to supply an object.
*/
askMissingObject(action, role)
{
/* Make action the current action for the current Command. */
gCommand.action = action;
gCommand.action.reset();
/*
* Store the current objects of the current action in the new action, in case
* action.scoreObjects() needs to refer to them below.
*/
action.curDobj = gDobj;
action.curIobj = gIobj;
action.curAobj = gAobj;
gCommand.dobj = action.curDobj;
gCommand.iobj = action.curIobj;
gCommand.acc = action.curAobj;
/*
* Make the action the original action for the current Command; we need to
* do this because otherwise the Command object will overwrite our new
* action with its original one before we're done.
*/
gCommand.originalAction = action;
/*
* Slot the new action's verbRule into the Command's verbProd, so that the
* Command has a verbProd appropriate to the action.
*/
gCommand.verbProd = action.verbRule;
/* See if we can find an obvious best object to select. */
/* First get the scope list for the new action. */
action.buildScopeList(role);
/*
* Then wrap the scopeList in a list of NP objects so we can use it as a
* parameter for scoreObjects().
*/
local matchList = action.wrapObjectsNP(action.scopeList);
/* Make a note of the highest scoring object we find */
local bestObj = nil;
/*
* If we found any objects we could match, determine which of them is the
* best match.
*/
if(matchList.length > 0)
{
/* Score all the objects in scope */
action.scoreObjects(gCommand, role, matchList);
/* Sort the list of objects in descending order of score */
matchList = matchList.sort(SortDesc, {a, b: a.score - b.score});
/* If there's only one object with the top score, select it */
if(matchList.countWhich({o: o.score == matchList[1].score}) == 1)
bestObj = matchList[1].obj;
}
/*
* If we have a best object, check that the command can actually use it
* before finally selecting it.
*/
if(bestObj != nil)
{
/*
* Obtain the verify result for the best object for this action in
* this role.
*/
local verResult = action.verify(bestObj, role);
/*
* Only execute the action with the best object if the action would
* pass the verify stage and the verify stage would allow the action
* to be performed implicitly. That way we won't choose an object with
* a dangerous or nonObvious verify result, and we won't pointlessly
* attempt an impossible action.
*/
if(verResult.allowAction && verResult.allowImplicit)
{
/*
* Announce which object we've chosen; language-specific modules
* will need to implement this.
*/
announceBestChoice(action, bestObj, role);
/*
* Slot our best choice of object into the appropriate object
* property of the current command object.
*/
gCommand.(role.objProp) = bestObj;
/* Execute the new action with the new set of objects. */
gCommand.execDoer([action, gCommand.dobj, gCommand.iobj]);
/*
* If we were able to execute the new action with the new set of
* objects, we're done; and we don't want to continue with the
* original action.
*/
exit;
}
}
/*
* If we couldn't find an obvious best object to use, prompt the player
* for his/her choice of object
*/
/*
* First create a new error for a missing object for our Command in the
* desired role.
*/
local err = new EmptyNounError(gCommand, role);
/*
* When the player's response is reparsed, we only want to resolve the
* nound for the role we're asking about here, so tell the command which
* role we want to resolve for.
*/
gCommand.npToResolve = role;
/*
* Display the corresponding error message (which will be a request to
* specify the missing object.
*/
err.display();
/*
* Set the Parser's question property to a question asking for this
* missing object, so that the Parser is prepared to treat the next input
* as an answer to this question.
*/
Parser.question = new ParseErrorQuestion(err);
/* Skip to the next command line so the player can enter a response */
abort;
}
/*
* This function displays msg, which should be a message inviting the player to choose a suitable
* object for action in role (DirectObject, IndirectObject or AccessoryObject). The action will
* then be performed using the selected object in role.
*/
askChooseObject(action, role, msg)
{
/*
* Store the current objects of the current action in the new action, in
* case action.scoreObjects() needs to refer to them below.
*/
action.curDobj = gDobj;
action.curIobj = gIobj;
action.curAobj = gAobj;
/* Make action the current action for the current Command. */
gCommand.action = action;
gCommand.dobj = (role == DirectObject ? nil : gDobj);
gCommand.iobj = (role == IndirectObject ? nil : gIobj);
gCommand.acc = (role == AccessoryObject ? nil : gAobj);
if(role == DirectObject)
{
gCommand.dobjNPs = [];
gCommand.dobjs = new Vector();
}
if(role == IndirectObject)
{
gCommand.iobjNPs = [];
gCommand.iobjs = new Vector();
}
if(role == AccessoryObject)
{
gCommand.accNPs = [];
gCommand.accs = new Vector();
}
/*
* Make the action the original action for the current Command; we need to
* do this because otherwise the Command object will overwrite our new
* action with its original one before we're done.
*/
gCommand.originalAction = action;
/*
* Slot the new action's verbRule into the Command's verbProd, so that the
* Command has a verbProd appropriate to the action.
*/
gCommand.verbProd = action.verbRule;
local err = new EmptyNounError(gCommand, role);
say(msg);
Parser.question = new ParseErrorQuestion(err);
abort;
}
//------------------------------------------------------------------------------
/*
* Verify Results: objects of this class are created by macros like
* logicalRank() and illogical() that are used in verify routines and stored
* in the verTab table of the current action.
*/
class VerifyResult: object
/*
* Our resultRank; the lower this number the less likely it is that this
* action could succeed, or the more illogical it is.
*/
resultRank = 0
/*
* The error message to display if this verify result prevents an action
* from going ahead.
*/
errMsg = ''
/* Is the action allowed to proceed according to this verify result? */
allowAction = true
/* Can this action be performed as an implicit action? */
allowImplicit = true
/* The object to which this verify result refers */
myObj = nil
/* The constructor for creating a new verify result. */
construct(score_, errmsg_, allowAction_, myObj_, allowImplicit_ = true)
{
resultRank = score_;
errMsg = errmsg_;
allowAction = allowAction_;
myObj = myObj_;
allowImplicit = allowImplicit_;
}
;
//------------------------------------------------------------------------------
/* Note the objects in objlist as potential pronoun antecedents */
notePronounAntecedent([objlist])
{
local itList = [];
local themList = [];
local himList = [];
local herList = [];
/*
* Go through each object in objlist and add it to the appropriate pronoun
* list
*/
foreach(local cur in objlist)
{
/*
* If we refer to a SubComponent, we're really referring to its
* location
*/
if(cur.ofKind(SubComponent) && cur.location)
cur = cur.location;
/* If the object is plural, it's a possible antecedent for 'them' */
if(cur.plural || cur.ambiguouslyPlural)
themList += cur;
/*
* Add the object and any of its facets to the himList, herList and
* itList according to whether it's isHim, isHer or isIt property is
* true.
*/
local lst = valToList(cur.getFacets) + cur;
if(cur.isHim)
{
for(local obj in lst)
himList += obj;
}
if(cur.isHer)
{
for(local obj in lst)
herList += obj;
}
if(cur.isIt && (!cur.plural || cur.ambiguouslyPlural))
{
for(local obj in lst)
itList += obj;
}
}
/*
* If any of the lists have anything in them, use them to set the
* antecedent list on the corresponding pronoun.
*/
if(themList.length > 0)
Them.setAntecedents(themList);
if(itList.length > 0)
It.setAntecedents(itList);
if(herList.length > 0)
Her.setAntecedents(herList);
if(himList.length > 0)
Him.setAntecedents(himList);
}
/* The remainder of this file contains code "borrowed" from the adv3 library */
/* ------------------------------------------------------------------------ */
/*
* PreSaveObject - every instance of this class is notified, via its
* execute() method, just before we save the game. This uses the
* ModuleExecObject framework, so the sequencing lists (execBeforeMe,
* execAfterMe) can be used to control relative ordering of execution
* among instances.
*/
class PreSaveObject: ModuleExecObject
/*
* Each instance must override execute() with its specific pre-save
* code.
*/
;
/*
* PostRestoreObject - every instance of this class is notified, via its
* execute() method, immediately after we restore the game.
*/
class PostRestoreObject: ModuleExecObject
/*
* note: each instance must override execute() with its post-restore
* code
*/
/*
* The "restore code," which is the (normally integer) value passed
* as the second argument to restoreGame(). The restore code gives
* us some idea of what triggered the restoration. By default, we
* define the following restore codes:
*
* 1 - the system is restoring a game as part of interpreter
* startup, usually because the user explicitly specified a game to
* restore on the interpreter command line or via a GUI shell
* mechanism, such as double-clicking on a saved game file from the
* desktop.
*
* 2 - the user is explicitly restoring a game via a RESTORE command.
*
* Games and library extensions can use their own additional restore
* codes in their calls to restoreGame().
*/
restoreCode = nil
;
/*
* PreRestartObject - every instance of this class is notified, via its
* execute() method, just before we restart the game (with a RESTART
* command, for example).
*/
class PreRestartObject: ModuleExecObject
/*
* Each instance must override execute() with its specific
* pre-restart code.
*/
;
/*
* PostUndoObject - every instance of this class is notified, via its
* execute() method, immediately after we perform an 'undo' command.
*/
class PostUndoObject: ModuleExecObject
/*
* Each instance must override execute() with its specific post-undo
* code.
*/
;
Adv3Lite Library Reference Manual
Generated on 03/07/2024 from adv3Lite version 2.1