Verify
The verify method has two main functions: (a) to veto actions that plainly should not be allowed to continue (e.g. EAT MOUNTAIN) and explain the veto, and (b) to help the parser decide which object to choose in the case of ambiguity (e.g. if OPEN DOOR could refer to either the currently open red door or the currently closed blue door, verify can be used to prefer the blue door, since you can't open a door that's already open).
A verify() routine should never alter the game state (since, among other things, it will probably be called multiple times during object resolution). Neither should it directly display a string using a double-quoted string or say(). Normally it should only contain one or more of the macros designed to be used in a verify routine, if-statements to determine which macro to use, or the inherited keyword to invoke a superclass's verify behaviour. A verify routine can also simply be empty (i.e. contain no code at all); this is often useful when you want to allow an action to proceed unconditionally.
The macros that can be used in verify routines to define verify results are:
|
logical
|
logicalRank(rank, key)
|
logicalRankOrd(rank, key, ord)
|
dangerous
|
illogicalAlready(msg, params
)
|
illogicalNow(msg, params
)
|
illogical(msg, params
)
|
illogicalSelf(msg, params
)
|
nonObvious
|
inaccessible(msg, params
)
|
|
For example, suppose your game has a red door, a red box and a red cup. It's perfectly logical to open a door, but it's not a good choice for an OPEN command if it's already open. Likewise, a box can probably be opened, but perhaps this red box can be broken, and once broken, it's no longer openable. Finally, a cup is never something that can be opened; the command OPEN CUP would never make sense. You might define the corresponding verify methods as follows:
|
dobjFor(Open)
{
verify()
{
if(isOpen)
illogicalAlready('The red door is already open. );
}
}
;
redBox: OpenableContainer 'red box*boxes' 'red box'
dobjFor(Open)
{
verify()
{
if(isBroken)
illogicalNow('You can no longer open it; it\'s broken. ');
}
}
;
redCup: Container 'red cup*cups' 'red cup'
dobjFor(Open)
{
illogical('You can\'t open a cup. ');
}
;
|
|
The non-i macros all allow the action to proceed (at least to the check stage, see below), but again affect how likely the object is to be chosen by the parser in case of ambiguity. The logical macro is simply the default; a verify() routine that consists solely of the keyword logical is identical to one that contains nothing at all. The logical macro is thus strictly speaking redundant, but it may improve the readability of code to use it in a complex verify() routine with several different conditions producing different results.
The logicalRank(rank, key) macro allows you to choose the priority the parser gives to selecting different objects in cases of ambiguity. The default rank is 100; an object with a logical rank of 150 is regarded as a particularly suitable target for a command, while one with a logical rank of 50 would be a possible but not very likely one.
Suppose that in the previous example, when the the door is closed and the box not broken we wanted the parser to prefer the door to the box in response to an open red command.. To boost the ranking of the door, we might use logicalRank thus:
|
dobjFor(Open)
{
verify()
{
if(isOpen)
illogicalAlready('The red door is already open. );
else
logicalRank(120, 'door');
}
}
;
|
|
The dangerous macro is intended to prevent an object being used in an action when carrying out the action on that object would be plainly dangerous (e.g. drink poison) even though the action is perfectly possible and the object might be the only suitable one in scope. Thus, to prevent a bare drink command from making the PC drink the poison when the poison is the only potable object around, you would define the poison's verifyDobjDrink() method as dangerous.
|
dobjFor(Drink)
{
verify() { dangerous; }
action()
{
"You feel an unpleasant choking sensation as the poison
burns down your throat; then you feel no more. ";
finishGameMsg(ftDeath); // this ends the game
}
}
;
The nonObvious macro works similarly, but makes the object so marked even less likely to be chosen in the event of ambiguity. Its main purpose is to prevent a puzzle being solved accidentally by having an action carried out implicitly or by default. For example, if we hadn't wanted to make it obvious that the nest could be moved with the stick, we could have put nonObvious in the verify() section of the iobjFor(MoveWith) on the stick.
|
So far we have discussed verify() mainly in terms of how the parser selects objects in case of ambiguity. Another way of looking at is that verify() should be used to veto an action only if it should be obvious to the player (not the player character) that the action is illogical (e.g. eating a mountain or opening an already-open door). In fact, these two ways of looking it amount to the same thing: the purpose of verify() is to help the parser decide what the player probably meant in case of ambiguity. If you want to veto an action which is perfectly 'logical' (i.e. one that the player could well have meant) you should therefore use check() instead.
Getting Started in TADS 3
[Main]
[Previous] [Next]