ContAction
User’s Guide
Version 2.5
ContAction is a collection of interrelated modules
that provide a mechanism for continuous actions, actions that consist of a
series of actions that are continuously requeued until
they have either satisfied a condition or are externally terminated.
Thanks
to Michael J. Roberts for his many suggestions.
Copyright © 2001-2004 by
Kevin Forchione. All rights reserved.
Contents
ContIAction,
ContTAction, and ContTIAction Classes..
Global
Pending Command Info and the Modification to PendingCommandInfo Classes
Modifications
to ContAction and ContIAction Classes..
Querying
to Continue a Continuous Action
Nothing
could be easier. Simply add the files
to
your project source files after those for the TADS 3 system and library files.
#include
the following header file to your game source files:
Next
we simply define continuous verbs. Three verb classes are supported:
ContIAction – a Continuous
Intransitive Action
ContTAction – a Continuous
Transitive Action
ContTIAction – a Continuous
Transitive-with-indirect Action.
Macros
are provided in contaction.h for defining continuous
actions similarly to those in adv3.h.
Suppose
we want to define a continuous intransitive action that will display “Foo!” five times.
#include "contaction.h"
DefineContIAction(Foo)
execAction()
{
"Foo!
";
}
checkActionCompleted()
{
if (execActionCnt_
> 4)
return true;
else
return nil;
}
;
VerbRule(Foo)
'foo'
: FooAction
verbPhrase = 'foo/fooing'
;
That’s
all there is to it! Notice that the VerbRule is defined exactly as we would for any IAction. The action definition
is also very similar to that of an ordinary IAction, except that in addition to providing the
required execAction() method (which simply
displays “Foo!”) we are also required to define a checkActionCompleted() method that tells
the action when it is to be considered completed.
The example above is
trivial. Why not simply code a loop to display “Foo!”
five times in the execAction of an IAction?
Continuous actions are
actions that are requeued at the end of the afterAction phase of their execution. What happens is this:
when an action is first parsed it is converted into a pending command info
object (PendingCommandInfo) and stored in the appropriate actor’s pending
command queue (pendingCommand). The command execution cycle then determines which
actor is next on the schedule and processes its first pending command.
With normal commands the
command is then removed from the pending command queue after execution and
that’s the end of the story, at least as far as that command is concerned. But
with a continuous command a new pending command info object will be inserted
into the actor’s pending command queue after the action has executed. Since the
pending command is placed at the head of the queue it will be the next that the
actor executes. This process doesn’t stop until the action’s checkActionCompleted() method returns true.
So, while “Foo!” displays five times in our example above, it is
actually occurring over five execution
cycles. You can see that this is indeed the case if you examine the
turn counter displayed in the game status line.
The effect is most
noticeable for commands that are given to NPCs that
have been set to issue commands synchronously.
In this case the actor will proceed to execute the command over an extended
series of interchanges.
The practical upsweep of
this is that it enables things like time-oriented game play to be easily
implemented. But continuous actions may have other applications, such as a
command given to an actor to eat a loaf of bread that extends over a series of
turns.
The ContAction
class is a mix-in class that handles the lifecycle of a continuous action. The
class is combined with IAction, TAction,
and TIAction classes to provide continuous action
counterparts of these TADS actions.
The ContAction class defines the following methods:
afterActionMain() – This method first performs any inherited
operations, then checks if the action has completed. If the action has
completed it performs our action execution completed method; otherwise the
action is requeued in the actor’s pending command
queue.
afterContActionMain() – This method calls execActionCompleted(),
then sets the current actor state to the nextState
stored in the current actorState. If no nextState is available then the actorState
persists.
beforeActionMain() – This method first performs any inherited operations,
then gets the previously executed action (if this isn’t the first time the
action has been performed), synchronizes the current action’s state with that
of the previously executed action, issues a call to the initialization method,
performs any action completion checking requested, and updates the action
execution counter.
beforeContActionMain() – This method sets the current actor state to the actionState specified by this action, then calls execActionInitial().
checkActionCompleted() – This method controls whether the continuous
action is to be requeued or not. It must return
either true, the action has completed and will not be requeued;
or nil, the action has not completed and will be requeued.
By default this method will return true, meaning the action will not be requeued.
doAction(issuingActor,
targetActor, targetActorPhrase,
countsAsIssuerTurn) – This method synchronizes the a current continuous action with the
previous continuous action of a given series and initializes the current
action.
execAction() – This method must be defined by concrete subclasses of ContAction. For ContIAction, the author must define this just as he would for
any IAction. ContTAction and ContTIAction define this method.
execActionCompleted() – This method is called when checkActionCompleted() returns true, either during beforeActionMain() or afterActionMain() depending on the checkBeforeExec_ property. This method should do any work required
by an author when an action has completed.
execActionInitial() – This method is called when our action execution
counter (execActionCnt_) is zero, indicating that this is the first time
this action is being executed. This method should do any initial work required
by the action.
execActionInterrupted() – This method is called when gActor.issueContActionInterrupt()
is issued successfully. This method should do any work required by the author
when an action has been terminated before completion.
incrQueryActionCnt() – handles the incrementing of counters for queries
for interruption of an actor’s continuous action for both the current action
and the continuous action series.
incrQueryActionCurrCnt() – handles the incrementing of the counter for
queries for interruption of an actor’s continuous action for the current
action.
incrQueryActionSeriesCnt() – handles the incrementing of the counter for
queries for interruption of an actor’s continuous action for the continuous
action series.
incrQueryActionSeriesCnt() – increments the queryActionSeriesCnt.
incrQueryActionCurrCnt() – increments the queryActionCurrCnt.
initializeActionProps() – initializes the current continuous action.
interruptedContActionMain() – this method handles the termination of the
continuous action series.
isEquivContAction(action) – Returns true if this action is equivalent to
action. To be equivalent the two actions must be part of the same series of
continuous actions. A series of continuous actions consists of an initial
action followed by subsequent actions generated from the same command tokens or
action as the initial one, until the series is completed or terminated.
removeActorPendingCommand() - Method will remove any requeued
pending continuous command that is identical to the one being executed if the
current command hasn't been requeued (i.e. it's a
newly issued player command). This will prevent, if necessary, any nesting of
pending continuous commands due to the same command being reissued by the
player.
requeuePendingAction() – This method is called when the continuous action
has been defined as pendingCommandRequeue == nil. When this variable is set to nil it
indicates that we wish to requeue the player command
action, and the objects that resulted from parsing. This method will requeue a clone of the command’s action, and the objects
required by the specific type of action. (Concrete subclasses of ContAction must define this method). The method will then
update the resulting pending command info object with information specific to
the continuous action.
requeuePendingCommand() – This method is called when the continuous action
has been defined as pendingCommandRequeue == true. When this variable is set to true it
indicates that we wish to requeue the player command
tokens, not the action and objects that resulted from parsing those tokens.
This method will requeue the command’s original token
list and update the resulting pending command info object with information
specific to the continuous action.
setActionCompleted(val) – Sets the
action’s isActionCompleted property to val.
setCurrActorState(actor, newState, nextState) – This method sets the current state of actor to an
instance of newState, with the actor’s next state
indicated by nextState.
setActorNextRunTime(actor) – allows the action to handle the actor’s next run
time when the action has been terminated.
setNextActorState(actor) – This method sets the actor’s current state to the
nextState indicated by its curState
object. If no nextState is available then the actor’s
current state is unchanged.
synchronizeActionProps(obj1,
obj2) – synchronizes obj1’s state
for the properties listed in updActPropList with
those of obj2.
updateActionState(action) – Keeping track of certain data may be desirable
from one execution of a continuous action to another. This method loops over
the property pointers supplied in updActPropList and
sets the currently executing action’s properties to those of the previously
executed continuous action. This allows the current action to “know” what the
previous one knew.
updatePendingCommandInfo() – This method will updated the newly queued pending
command info object, indicating that it is a requeued
action, and storing a pointer to the last continuous action processed.
The library extension
defines ContIAction, ContTAction
and ContTIAction counterparts to the normal IAction, TAction and TIAction classes. These classes allow an author to define a
continuous actions using macros similar to those found in the adv3.h header
file.
These classes define:
The ContActorState class is a HermitActorState that is used by continuous actions to assign a
continuous actor state throughout the execution of a continuous action. It can
be used, for example, to provide an actor with a WaitingActorState during WaitTimeActions in TimeSys.
The class defines the
following properties:
construct(actor, next) – This method passes the actor and nextState information to the construction of the new state.
execActionInitial(action) – by default we do nothing.
execAction(action) – by default we do nothing.
execActionCompleted(action) – by default we do nothing.
execActionInterrupted(issuingActor, action) – by default we do nothing.
checkActionCompleted(action) – by default we return true.
The pending command info
class serves to store command tokens or actions that are to be queued on the
actor’s pending command queue for command execution. The continuous action
mechanism sets the following properties on a pending command info object once a
continuous action has been requeued.
The PendingCommandToks
and PendingCommandAction classes modify the following
method:
executePending() – This method stores the pending command info
object as a global variable gPendingCommaindInfo,
valid during the duration of its execution.
The cont_actor.t
module modifies the ContAction class to redirect the following methods to the gActor:
The ContIAction class redirects the following methods to the gActor:
While ContTAction
and ContTIAction handle actions through the usual dobjFor() and iobjFor() methods,
it would seem desirable to redirect the extra continuous methods to the actor
as well.
In addition, the following
properties have been added or modified on the Actor class:
allowsContActionInterrupt(issuingActor, action,) – Returns true if the actor allows the action to be interrupted;
otherwise returns nil. If the actor is the player character then issues a
query asking the player whether they wish to stop the continuous action.
contActionExpiredFor(actionClass) –
This method returns true if the actor’s most recent action is an expired
continuous action of class actionClass; otherwise it
returns nil.
contActionPendingFor(actionClass) –
This method returns true if the first pending command info object in the
actor’s pending command queue is of class actionClass;
otherwise it returns nil.
getExpContActionFor(actionClass) –
Returns the expired action for this actor for this action class. If the most
recent action isn’t ofKind() actionClass
or is not completed then the method returns nil.
getPendContActionFor(actionClass) –
Builds a list of actions for this actor for this action class. The list
consists of the most recent action, first pending action, and query action.
issueCommandsSynchronously – By default we set this to nil, meaning that
actors will perform a series of player command line orders over a series of
turns, rather than all at once. It may seem confusing, but this setting works
best for continuous actions.
issueContActionInterrupt(issuingActor, actionClass, queryActor) –
This method handles all the details of issuing an interrupt of a continuous
action for this actor. The actionClass should be a
continuous action subclass. A queryActor value of
true will issue a query to the actor’s allowsContActionInterrupt()
method, while a value of nil will attempt to issue an interrupt without asking
the actor whether they will allow one.
The contActionYesOrNo(action)
issues a yes/no query for indicating whether to terminate a continuous action
or not. The function takes one argument, the action to be terminated. It
returns true if the player wishes to continue with the continuous action;
otherwise it returns nil.
These classes work
essentially the same as the normal TADS 3 library fuses and daemons, with the
extra feature of attempting to issue a request for a continuous action
interrupt to the player character after the fuse or daemon has fired and sent
its message to the target object.
This mix-in facilitates the
attempted interrupt of continuous actions for the player character.
This fuse construct
argument values are:
obj: same as for
Fuse
prop: same as for Fuse
turns: same as for Fuse
actor: the actor to be notified for a continuous action
interrupt
action: the continuous action to be interrupted
queryActor: indicate
whether or not the actor should be queried to see if it will allow the
interrupt.
This fuse construct argument
values are:
obj: same as for SenseFuse
prop: same as for SenseFuse
turns: same as for SenseFuse
actor: the actor to be notified for a continuous action
interrupt
action: the continuous action to be interrupted
queryActor: indicate
whether or not the actor should be queried to see if it will allow the
interrupt.
source: same as for SenseFuse
sense: same as for SenseFuse
This daemon construct
argument values are:
obj: same as for
Daemon
prop: same as for Daemon
interval: same as for Daemon
actor: the actor to be notified for a continuous action
interrupt
action: the continuous action to be interrupted
queryActor: indicate
whether or not the actor should be queried to see if it will allow the
interrupt.
This daemon construct
argument values are:
obj: same as for SenseDaemon
prop: same as for SenseDaemon
interval: same as for SenseDaemon
actor: the actor to be notified for a continuous action
interrupt
action: the continuous action to be interrupted
queryActor: indicate
whether or not the actor should be queried to see if it will allow the
interrupt.
source: same as for SenseDaemon
sense: same as for SenseDaemon