Creating New Verbs

Although the implementation of a large number of standard verbs is available in verbs.library, almost all large games will find the need to define custom verbs. New verbs are defined using grammar statements and their default implementation is provided in a global function. This chapter contains information on making your custom verbs 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 
if +reach<noun2 = true
return true
endif
...

Another possibility is to define "put" as an actual 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
...
if +reach<noun1 = true
   return true
endif
...
}

These above lines tell the JACL interpreter to return true if the function +reach returns true. In practice, this means that if 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 arg[0] has OUT_OF_REACH
   write  arg[0]{The} " " arg[0]{is} " out of reach.^"
   set time = false
   return true
endif
return false
}

This function simply tests if the specified object has the attribute OUT_OF_REACH. If so, an appropriate message is displayed, the variable time is set to false and the function returns true. If the object does not have the attribute OUT_OF_REACH, the function will return false.

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 lines:

if +darkness = true
   return true
endif

Finally, all verbs should contain a code block that calls the function +important. This function tests whether the passed object has the attribute NOT_IMPORTANT and displays a suitable You don't have to worry about that. type message. If the verb you are adding refers to two objects, you may need to call this function twice, one for each object. If one of the objects in the command needs to be an object that is *held, you will only need to call +important for the other object, as objects with the attribute NOT_IMPORTANT can't be taken due to the take verb stopping when it reaches this same test. Below is an example of a call to +important:

if +important<noun1 = true
   return true
endif

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

A tree representing 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 extreme 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.

Back to Contents