Creating New Verbs

When adding grammar statements to your game to extend those provided by verbs.library there are a few points to be aware of. This chapter attempts to identify these points and assist you making your additions as robust and complete as possible.

When adding a new verb, it is important to be sure that you are doing a good thing. Adding a new verb simply to facilitate a guess-the-word type puzzle is definitely a bad thing. On the other hand, having an obvious verb missing is almost as annoying for the player as being made to guess an obscure one. Also consider that adding a new verb doesn't always mean adding a new function. You may find that a required verb is simply a synonym for an existing verb. In this case, simply add a new grammar statement with the synonym and point it to an existing function. In the library file you will find many functions that are mapped to from more than one grammar statement. For example, here is the start of the global function +insert and its matching grammar statements:

grammar insert *held on *present      >insert_on
grammar put *held on *present         >insert_on

{+insert_on 
break +reach noun2
...

Another possiblity it to define "put" as an acutal synonym of "insert". There are potential dangers with this method that are described in the section on Synonyms.

Now for a few general rules. Each verb that involves touching an object should check that the object does not have the attribute OUT_OF_REACH before allowing the player to manipulate it. This, of course, does not apply to verbs that can only be performed on objects that are being held. The following line of code is an example from the top of the +take function:

{+take
break +reach noun1

This above line tells the JACL interpreter to break true if the function +reach breaks true. In practice, this means that if the the object is unreachable by the player, nothing beyond this line will be executed. Below is the content of the +reach function from verbs.library:

{+reach
if noun4 has OUT_OF_REACH
  write <p> noun4{The} " " noun4{is} " out of reach.^"
  set TIME = false
  break
endif
}

This function simply tests if the specified object has the attribute OUT_OF_REACH. If so, an appropriate message is displayed and the variable TIME is set to false. Finally, the break command says that execution should not continue after calling break command. If the object does not have the attribute OUT_OF_REACH, the function will terminate normally (an implicit break true), and executing will then continue from the line after the calling break command.

Most verbs will also make a similar call to the function +darkness. This function tests whether the player is currently in darkness or not. Actions that require the player to see should have the line:

break +darkness

If your verb causes the object to be moved, such as the take verb, you must also ensure that the object is given the attribute TOUCHED if the move was successful. This will ensure that any tests as to whether the object has been moved from its initial position or not will be accurate. This time an example from the end of the +take function:

...
override
write "<p>You take " noun1{the} .^
move noun1 to player
ensure noun1 has TOUCHED
}

If the verb performs any tests to check whether the move should be successfully completed under the current circumstances or not, an override command should be added directly before any effects are coded, such as in the example above. This allows override functions to be associated with objects in order to change the default outcome while still taking advantage of the tests you have coded. Below is an example of the types of tests that the default action for a verb should perform. Of course, the exacts tests you will require are specific to the nature of the verb you are coding.

grammar ask *present for *carried             >ask_for

{+ask_for
if here has UNDER_WATER
   write "<p>Talking under water isn't very easy.^"
   set TIME = false
   break     
endif 
if noun1 hasnt ANIMATE
   write <p> noun1{The} " seem" noun1{s} " to be ignoring "
   write "your request.^"
   break     
endif 
if noun1 has DEAD
   write <p> noun1{The} " " noun1{is} " a bit too dead to "
   write "respond.^"
   set TIME = false
   break     
endif 
if noun1 = player
   write "<p>I think it might be time to take a break and "
   write "get a cup of tea.^"
   set TIME = false
   break     
endif

As you will have seen above, it is also important that the variable TIME is set to false if the move typed by the player could not be performed. Setting TIME to false tells the interpreter that the eachturn functions should not be executed. In other words, if the player's move is not possible, time should not pass.

One last thing to keep in mind when adding grammar statements is whether its scope indicators are going to clash with any other existing grammar statements. For example, consider the following two lines:

grammar give *present grief >hassle

grammar give *held to *present >give_to

The vocabulary for each game is stored as a tree. The following tree is a representation of the two grammar statements above.

Given the two grammar statements above, the command

give sword to troll

would produce the message

You can't use the word "to" in that context.

This is because sword is a match for *present, therefore causing the parser to branch down a path that only allows to word grief to be used next. This is obviously not the desired effect. Using $text in a grammar statement is equally dangerous. As an exteme example, using $text as the first word of the first grammar statement will prevent any others from ever matching. It is therefore important to bare in mind that grammar statements are tested in the order they appear in the game file, starting from the top. To have a grammar statement checked last, add it to your game file after the line including verbs.library.