Inform.t
Version 3.3
This file is part of the Inform.t
Author’s Manual
Copyright © 1999 by Kevin
Forchione. All rights reserved.
Message System
With inform.t release
3.3, the Message System has undergone major revisions, making it easier to
understand and use. Some of the more significant changes include:
·
The scheduling
maintenance methods from message class have been moved to the messagepump.
·
The message class has
been replaced with an eventObj class, which is dynamically created when needed,
and re-usable.
·
The eventList has
been replaced with attributes used to store command variables, and providing
the validate() and process() methods with this information in the standard
ADV.T format.
·
The Message System
now makes it possible for authors writing their own parser to use the system
for all event handling: messages, daemons, fuses, and notifys.
·
Command-related
messaging is now a reality. The inform_event.t file demonstrates simple ADV.T
modifications that allow an author to tailor messages in any fashion they like.
As with its
predecessor, the message system enables an author to generate an event which
will process immediately from a method or function, or to place the event into
one of three different processing queues based on the stage attribute set on
the event object.
The first processing
queue is invoked at the very beginning of the POSTACTION
stage, the second occurs at the very beginning of the ENDCOMMAND stage, and the final queue occurs at the very end of the ENDCOMMAND stage. At each of these stages the messagepump is called
using the invokeMessagepump() function. This function passes the queueStage to
the messagepump.processQueue() method.
The Messaging System
consists of three basic elements: event generation functions, the eventObj
class, and the messagepump object.
event
generation functions
From any point in the
EXECUTION phase, before the final call to invokeMessagepump() in
endCommand() a function or method can call one of the two inform.t event
generation functions: commandEvent() and alertEvent(). Both of these take the
same arguments:
commandEvent(
obj, &process, ... );
alertEvent(
obj, &process, ... );
The parameters should
be entered in the following order, and have the following meaning:
obj This is the object whose
process() method should execute when the event is processed by messagepump.
&process A method
pointer to the method to be processed
&validate A method
pointer to the method used for validation
stage The queue stage at which the
event is to process
nil: process immediately, or in at the first
queue stage in which this event passes validate
1: process at the beginning of
postAction()
2: process at the beginning of endCommand
3: process at the end of endCommand
retain This indicates whether the event
is to be retained on the messagepump schedule after it has been processed. This is for the simulation of daemon / fuse
behaviour.
true: retain this event on the schedule after it
has processed.
nil: remove this
event from the schedule after it has processed.
frequency: Determines the frequency which the event
will process and, in combination with retain, can be used to simulate daemon /
fuse / notify behaviour. In general this behaves like a count-down based on
turns, but is dependent upon the stage at which the event is set.
commArg: Determines the number and types of
arguments passed to &validate and &process.
true: send the following values to &validate
and &process: actor, verb, dobjList, prep, iobj, eventCnt, pendingReason.
nil: send the following values to
&validate an d&process: eventCnt and pendingReason.
actor The actor object to be passed to
&validate and &process when commArg is true.
verb: The verb object to be passed to
&validate and &process when commArg is true.
dobj: The direct object to be passed
to &validate and &process when commArg is true.
prep: The prep object to be passed to
&validate and &process when commArg is true.
iobj: The iobj object to be passed to
&validate and &process when commArg is true.
Only the obj
and &process arguments are required by commandEvent() and alertEvent().
The other arguments default to the following values:
commandEvent( obj, &process, nil, 2, nil, 0, true, parserGetObj(PO_ACTOR), parserGetObj(PO_VERB), parserGetObj(PO_DOBJ), parserGetObj(PO_PREP), parserGetObj(PO_IOBJ))
&validate: nil. No validate method.
stage: 2. Process at the beginning of
endCommand.
retain: nil. Remove from the schedule,
once it processes.
frequency: 0. Each time the messagepump.processQueue
is called.
commArg: true. Pass actor, verb, dobjList, prep,
iobj, eventCnt, and pendingReason to &validate and &process
actor: parserGetObj(PO_ACTOR)
verb: parserGetObj(PO_VERB)
dobj parserGetObj(PO_DOBJ)
prep: parserGetObj(PO_PREP)
iobj: parserGetObj(PO_IOBJ)
alertEvent( obj, &process, nil, 2, nil, 0, true, parserGetObj(PO_ACTOR), parserGetObj(PO_VERB), parserGetObj(PO_DOBJ), parserGetObj(PO_PREP), parserGetObj(PO_IOBJ))
&validate: nil. No validate method.
stage: nil. Process immediately,
or at the first queue stage that this event passes validate.
retain: nil. Remove from the schedule,
once it processes.
frequency: 0. Each time the
messagepump.processQueue is called.
commArg: nil. Pass only eventCnt, and
pendingReason to &validate and &process
actor: nil.
verb: nil.
dobj nil.
prep: nil.
iobj: nil.
Although these
parameters may seem complicated, they give the author great flexibility and
control over the message system. Much of the time, however, only the object,
&process, and &validate need to be supplied, letting the function
defaults handle the rest.
Suppose you want the
following display in your game:
>Take the coins
Three coins taken.
>Take the apple
and the grapes
An apple and four
grapes taken.
You would first
replace the code in the doTake() method of thing to generate an event to queue
the display your “Taken.” Message at a stage when all the direct objects have
processed.
Modify
thing
doTake(actor) =
{
local totbulk, totweight;
totbulk = addbulk(actor.contents) +
self.bulk;
totweight = addweight(actor.contents);
if (! actor.isCarrying(self))
totweight = totweight + self.weight +
addweight(self.contents);
if (totweight > actor.maxweight)
"%Your% load is too heavy.
";
else if (totbulk > actor.maxbulk)
{
if
( self.moveToSackItem( actor ) )
{
self.moveInto(
actor );
commandEvent(takeVerb,
&mpDoTake);
}
else
"%You've%
already got %your% hands full. ";
}
else
{
self.moveInto(actor);
commandEvent(takeVerb,
&mpDoTake); // “Taken.” Replaced.
}
}
;
Notice that we have
only replaced the “Taken.” Message from the very last line of the method with
the following command:
commandEvent(takeVerb, &mpDoTake);
This function is
called each time we doTak(). In the case of our coins it is called three times.
The first time it is called an event is scheduled on the messagepump. Each
subsequent doTake() updates this scheduled event, specifically it adds the
doTake() object to the event’s dobjListPtr and increments its eventCnt.
At the beginning of
endCommand() the messagepump is called using
invokeMessagepump( 2 ). Our scheduled
event is passed to the messagepump.checkQueue() method, which checks to see if
the event has a validate method. Since our event doesn’t have a validate method
it calls takeVerb.mpDoTake(actor, verb, dobjList, prep, iobj, eventCnt,
pendingReason).
modify
takeVerb
mpDoTake( actor, verb, dobjList, prep,
iobj, eCnt, pReason ) =
{
"\n";
if (actor != parserGetMe())
{
"<<actor.thedesc>> takes ";
if (length(dobjList) == 1)
{
local dobj = car(dobjList);
dobj.thedesc;
}
else
listcont(dobjList);
". ";
}
else
{
caps();
if (length(dobjList) > 1)
{
listcont(dobjList); " ";
}
"taken. ";
}
}
;
Our accumulated dobjList (from each generate of our event) is then passed to listcont() and the “taken.” appended to its result.*
&validate
This is an
author-defined method that allows constraints to be put on the conditions under
which the message is to process. This method should return nil if the message
is to process; otherwise it should return a non-nil value if the message should
remain in the messagepump schedule.
Some basic return
codes have been defined in Inform.t:
MP_PURGE: Message should be purged from the schedule
MP_WAITING Message fails due to player waiting
MP_LOCATION Message fails due to player location
The system is
flexible enough to accommodate author-defined return codes (any non-nil return
value will fail &validate).
The non-nil return
value will be kept in eventObj.pendingReason and can be used to tailor the
processEvent. For instance, if the player’s waiting invalidates the processing
of the message then returning a value, such as MP_WAITING can be used by
&process.
&process
This is an
author-defined method that handles the processing of the event. The method can
make use of information provided by actor, verb, dobjList, prep, iobj,
eventCnt, and pendingReason to make refinements to its processing.
Note that this method
need not produce any display. It can instead be used to perform scheduled
state-changes or generate new events.
eventObj
class objects
eventObj class ========== isscheduled objPtr processPtr validatePtr stage retain frequency frequencyCnt commArg actorPtr verbPtr dobjListPtr prepPtr iobjPtr eventCnt pendingReason
This class of object
is created dynamically as needed, and then re-initialised for reuse during the
game. It consists of a collection of attributes that carry the essential
information of the event, specifically:
isscheduled: Indicates
whether the eventObj is currently on the messagepump schedule.
objPtr: The object whose
methods the &validate and &process
processPtr: Holds the method pointer of the object method which is called when the event processes.
validatePtr: Holds
the method pointer of the object method which is called when the event is
validated.
This method should return nil, if the event passes validation; otherwise it should return a non-nil value. An author may add values of his own. Some of the standard values are:
MP_PURGE: Message should be purged from the schedule
MP_WAITING:
Message fails due to player waiting
MP_LOCATION:
Message fails due to player location
stage: Holds the value of
the stage at which this event is to process (see stage above).
retain: Indicates whether this
event is to be kept on the messagepump schedule after processing.
frequency: Indicates the frequency of
validation / processing for this event.
frequencyCnt: A counter used as a count-down to
processing.
commArg: Indicates the number and
types of arguments to be passed to &validate and &process (see above)
actorPtr: The actor object for this
event
verbPtr: The verb object for this
event
dobjListPtr: A
list of direct objects for this event
prepPtr: The prep object for this
event
iobjPtr: The indirect object for
this event.
eventCnt: A count of how many generateEvent() requests were made for this event up to the point it processed.
pendingReason: The
non-nil return value from the &validate method.
message
pump object
messagepump ========== schedule ---------------- generateEvent scheduleEvent getEvent processQueue checkQueue checkFrequency deletePending unscheduleEvent
This object maintains
a schedule of events (messages to be processed). It is called each time
invokeMessagepump() is invoked, looping through its scheduled list of events
and processing each event for that stage whose frequencyCnt has reached 0 and
that has passed its validate method.
generateEvent(
o, p, v, s, r, f, c, actor, verb, dobj, prep, iobj )
This method calls
both scheduleEvent and checkQueue. The event is first scheduled on the
messagepump, then checked to see if it passes validate; otherwise, depending
upon its attributes, it may be queued or purged.
scheduleEvent(
o, p, v, s, r, f, c, actor, verb, dobj, prep, iobj)
This method is used
to schedule an event on the messagepump schedule, increment the messageCnt, add
any passed direct object to the dobjList.
Authors should use
the commandEvent() or alertEvent() methods, which issue generateEvent() rather
than scheduleEvent() as the generateEvent() method also checks the queue status
of the event.
getEvent(
o, p, v, s, r, f )
This method searches
the schedule for an event matching the object, &process, &validate,
stage, retain, and frequency parameters passed. If there is a match the
schedule event is returned; otherwise the method returns nil.
processQueue(
queueStage )
This method is called
from the invokeMessagepump function at the beginning of the POSTACTION and ENDCOMMAND stages, as well as at the end of the ENDCOMMAND stage.
It calls checkQueue(
queueStage ) for each object listed in schedule, processing them in the order
that they were added to the schedule.
checkQueue(
event, queueStage )
This method handles
validation, processing, and cleanup of a scheduled event. It is called
initially from generateEvent to determine if the event should process
immediately (eventObj.stage = nil). Subsequent calls to this method are made
each time the messagepump is invoked.
checkFrequency(
event )
Called from
checkQueue() before validation. If the event frequencyCnt is not 0 then the
event is not yet ready to process. This method decrements the count in that
case and returns MP_FUSE; otherwise this method returns nil. If the
eventObj.retain = true then this method resets the eventObj.frequencyCnt =
eventObj.frequency when the frequencyCnt reaches 0.
deletePending(
o, p, v, s, r, f )
This method should be
used for explicit removal of events from the schedule by author code. This
method uses the getEvent() to identify a method on the schedule from its unique
attributes.
UnscheduleEvent(
event )
The following shows
diagrammatically the message pump process. As the EXECUTION Phase runs its
course object methods and functions generate events by calling the
commandEvent() and alertEvent() functions.
These events are
scheduled by the messagepump and then checkQueue() determines whether the event
is to be processed immediately, or kept in the schedule queue.
At the beginning of
postAction() the invokeMessagepump() is executed for queue stage 1. These are
potentially all the messages queued during the ACTION
stage of execution, but may contain messages with stage nil that are now valid
to process.
At the beginning of
endCommand() the invokeMessagepump() is executed for queue stage 2. These are
potentially all the messages queued during the ACTION,
POSTACTION and DAEMON/FUSES stages of execution, but may contain messages with stage
nil that are now valid to process.
At the end o endCommand()
the invokeMessagepump() is executed for queue stage 3. These are potentially all the messages
queued during ACTION,
POSTACTION, DAEMON/FUSES, and ENDCOMMAND stages of execution, but may contain messages with stage
nil that are now valid to process.
* You may be wondering what happened to the object.sdesc; “:”; messages that print automatically through the parser. They were suppressed by inform.t between the call of object.multisdesc and verb.verbAction().