One of the most common traps a beginner to TADS runs into is the misuse of the ver* methods. What are those? Well, each object that intends to respond to a particular verb supplies a pair of methods so that the TADS runtime (the program the user runs to play your game) can check to see if that object is a valid object for that verb.
For example, lets say you create a potion and you want your player to be able to drink it. Here's a simple definition of a potion:
potion: item
noun = 'potion'
sdesc = "potion"
ldesc = "It's all bubbly. "
verDoDrink( actor ) = {
if ( not self.isIn( actor ) ) {
"You have to be holding the potion in order to drink it! ";
}
}
doDrink( actor ) = {
"You drink the potion. Yum. ";
self.moveInto( nil );
}
;
Looks simple enough, right? We define a noun for the parser to recognize, we define an sdesc for the parser to print, we define an ldesc for the 'examine potion' command, and then we create a pair of methods which the parser uses to determine if this object is really drinkable.
In simplified terms, first the parser sees the command 'drink potion'. It then
checks to see if the potion actually CAN be drunk by calling the
verDoDrink() method. If that method prints out any text, then the
parser concludes that the potion is not drinkable.
Our verDoDrink() method is simple enough: check to see if the
player is holding the potion. Notice that all we do is print out a message if
the player ISN'T holding the potion. The parser detects this condition and
stops right there.
But let's say that our player did, in fact, pick up the potion before trying to
drink it. The parser will notice that no text comes out of the
verDoDrink() method and concludes that "drink" is a valid verb to
apply to the potion. After reaching that conclusion, the parser calls the
doDrink() method. The doDrink() goes ahead and
performs the necessary actions: print a message telling the player what he/she
has done, and move the potion somehwere inaccessible.
So, you might think, why bother having two functions? Why not write the
verDoDrink() method to do all the dirty work?
The problem is that when the parser calls verDoDrink() the first
time to see if it generates output (remember: if the verDoDrink()
method generates output, then the parser assumes that the command was invalid,
and stops), it calls it "silently". In other words, any text that was
generated is NOT printed out. If text *is* generated, then the parser calls
the verDoDrink() method a SECOND time so that the text is actually
printed.
Now say we changed the potion definition above to remove the
doDrink() method and change the verDoDrink() method
to look like this:
verDoDrink( actor ) = {
if ( not self.isIn( actor ) ) {
"You have to be holding the potion in order to drink it! ";
} else {
"You drink the potion. Yum. ";
self.moveInto( nil );
}
What happens? If you run it, you'll notice that whether or not you pick up the potion, the game will tell you that you have to hold it! Why?
First the parser takes the players command ("drink potion"), and determines it has to run the verDoDrink() method on the potion. But, remember, it calls it "silently"; in other words, any output is hidden from the player.
Lets say that the player isn't holding the potion. In that case, the
verDoDrink() method checks to see if the player is holding it,
sees that he/she isn't, and prints out an error message. Since this message is
hidden, the player won't see it. Next, the parser detects that output was
generated by the verDoDrink() method, so it runs the method again,
this time WITHOUT hiding the output. So we get the error message we expect
because, again, the player isn't holding the potion.
Now lets say our intrepid player picks up the potion and then drinks it.
Again, TADS's parser calls the verDoDrink() method silently. So
the method determins that the player is holding it, it outputs the "yum"
message, and moves the potion to 'nil'. The parser then says "a-ha! output was
generated!" so it calls the method AGAIN, but this time with the output turned
on. What happens? The "if ( not self.isIn( actor ) )" line executes and,
since the potion was moved the first time this method was called, it figures
out that the player is no longer holding it! It prints out the "You have to be
holding..." message this time round.
The bottom line is: do not "change the game state" in the verDo* or verIo* methods. Changing the game state includes: modifying variables, moving objects, starting daemons, creating new objects, and so on. Just about all a verDo or verIo method should do is CHECK the variables/objects in a game and print out a message only if there's a problem.
You can take a look at sample code which demonstrates this problem if you like.