add_command |
Discworld creator help |
add_command |
Syntax
varargs int add_command(string verb, object ob, mixed pattern, function f, string desc);
Description
This function is a pattern matching input parser. It is designed to make input parsing from the user a lot easier.
It should be called on a living object that can use the command. See add_command_examples for standard use.
The first parameter is the verb. The add_command function does NOT handle short verbs as the commands in /cmds do (eg, 'l' for 'look').
The second parameter is the object defining the verb and is the object the handler function is called on if the command is matched. It may be helpful to think of this as the object that provides the verb. This parameter is typically this_object(), but doesn't have to be. Note however that there is a relationship between this parameter and the <direct> pattern specifier decribed in the Patterns section.
The third parameter is the pattern. This is usually a string, but can be a string array if want to define multiple patterns at once (although this is rare). The pattern is the main part of the processing. If the pattern is not defined a pattern of "<direct:object>" is assumed. The format of the pattern will be defined in the Patterns section.
The fourth parameter is a function pointer. It is what is called if the pattern matches and a function needs to be called. If the function pointer is 0 then the function do_verb (eg, do_read, do_light) is called if it exists, or the function command_control as a last resort. The parameters and how these are called are defined below, starting in the section on The Handler Function.
The fifth parameter is a description of what the command does, shown to a player if they use the 'syntax' command for the verb.
The return value is 0 if the 'ob' or 'pattern' parameter is 0, or if whatever was passed for the 'f' parameter is not a function. Otherwise the return value is 1.
SEfun
There is also simul efun version of this function which can be slightly less cumbersome. Its syntax is:
varargs void add_command(string verb, mixed pattern, function f, string desc);
The 'ob' parameter is replaced with previous_object(), and the command is always added on this_player().
Patterns
The patterns are defined by using a series of special strings and regular text. The regular text must match the command input exactly (unless it is defined as optional — more on optional elements later).
The special strings define the type of thing that should be matched in what the player entered (eg, is it an object, a number, etc). Some special strings have additional options that can be added on as a suffix. All of the special strings will cause an argument to be captured from the command given by the player and passed to the command handler function (this will be described in more detail later).
The special strings and their options are described below.
<direct>
The direct object reference. This may match more than one object (as in "light torches"). NOTE: A direct object must be one of the providers of the verb (see the 'ob' parameter above).
It is possible to have more than one "<direct>" specifier in your pattern, and each one must match an object or objects, but only the last one counts as far as getting the objects to call the handler function on. However, any earlier ones will still capture an argument.
Also note that this is not a direct object in the grammatical sense, but just a short name for "the object the handler function is called on".
There are two levels of options for <direct>. The first level defines the class of object to look for —
| :object | Any object, uses find_match directly (default) | |
| :specific-object | Any object, but forces a non-ambiguous match. If more than one object matches the input the command is rejected. | |
| :living | Any living object | |
| :distant-living | Uses find_living | |
| :any-living | A combination of living and distant-living | |
| :player | Will only match a player |
The second level options define the environment to search for the object —
| :me | The player's inventory. | |
| :here | The inventory of the room. | |
| :here-me | The inventory of the room, then the inventory of the player. | |
| :me-here | The inventory of the player, then the inventory of the room. (default) |
Be careful when using "<direct>" in objects that are easy for players to create in large numbers. Doing so can cause over-eval errors due to how direct objects are handled when trying to execute a command (see the section on Calling the Handler Function).
An example of a full direct object specifier might look like "<direct:object:me>".
The captured argument for <direct> is the string used to match the object(s).
<indirect>
An indirect object reference. This may match more than one object (as in "get swords"). The handler function is not called on these, but the player's input must match an object or objects. You can have multiple "<indirect>" specifiers in your pattern. As with <direct>, this is not an indirect object in grammatical terms, but a name for objects that are passed to the handler function via a particular argument
The options for <indirect> are very similar to those for <direct>. There are also two levels, and the first level defines the class of object to look for —
| :object | Any object, uses find_match directly (default) | |
| :specific-object | Any object, but forces a non-ambiguous match. If more than one object matches the input the command is rejected. | |
| :living | Any living object | |
| :distant-living | Uses find_living | |
| :any-living | A combination of living and distant-living | |
| :player | Will only match a player | |
| :wiz-present | Uses wiz_present to do a match |
The second level options define the environment to search for the object —
| :me | The player's inventory. | |
| :here | The inventory of the room. | |
| :here-me | The inventory of the room, then the inventory of the player. | |
| :me-here | The inventory of the player, then the inventory of the room. (default) | |
| :direct-obs | The inventory of the direct object. This allows manipulation like "unload womble from camel" to work. |
Note that all of the 'here' options can still match objects in or on other objects if a player uses a syntax like "frob thing on table". This means you can't rely on these options to prevent accessing objects in another player's inventory. If your command shouldn't work on such things then you MUST do the environment check yourself.
An example of a full indirect object specifier might look like "<indirect:living:here>".
The captured argument for <indirect> is the string used to match the object(s). The indirect objects and the strings that they matched against are also passed to the handler function in separate arguments.
<string>
Matches at least one word and possibly more. There are three options for <string>. The two main options are ":long" (the default) and ":short". These determine whether the matching will be 'greedy' and match the longest possible string based on the rest of the pattern, or 'non-greedy' and match the shortest.
For example, given the pattern "<string:short> rabbit <string:short>" and the input "womble rabbit green rabbit home", the first "<string:short>" would match "womble" and the second "green rabbit home".
If instead we change the pattern to "<string:long> rabbit <string:short>", then the first match will be "womble rabbit green" and the second will be "home".
The third option for strings is ":quoted". This means that in order to match, the input string must be inside some kind of quotation marks. If your pattern is "frog <string:quoted> green", then it will match "frog 'gumboot' green" but not "frog gumboot green". Single ('), double ("), and backquotes (`) are accepted.
The captured argument for <string> is the string (surprise!). If using the ":quoted" option the quotation marks are removed.
<word>
This matches a single word. The captured argument is the word.
<number>
This matches any integer. It only matches numbers which are typed as numbers, eg "12". It does not match text numbers (eg "twelve"). There are four options, which are all pretty self-explanatory —
| :positive | Match an integer > 0 | |
| :negative | Match an integer < 0 | |
| :positive-or-zero | Match an integer >= 0 | |
| :negative-or-zero | Match an integer <= 0 |
The captured argument for <number> is the actual number as an int type.
<decimal>
This matches a decimal number such as 1.25 or -.4. Integers are also accepted. The captured argument is the actual number as a float type.
<ordinal>
Matches an ordinal number (like "third" or "42nd"). This is limited to a single word, so the maximum is "100th" / "hundredth". Note that this should be used if you want to match a large set of ordinals. If you only want to match something like "first", "second", or "third", then you are probably better off using the string list pattern (see below) and handling the string yourself in your command handling function using the ordinal_to_num sefun.
The captured argument is the int value corresponding to the ordinal ("third" becomes 3, etc).
<fraction>
Matches a fraction, such as 1/2 or 3/4. Both the numerator and denominator must be integers, and the denominator can't be 0. This captures *two* arguments, first the numerator, then the denominator, both as int types.
<preposition>
Matches a preposition, like "from", "in", or "under". This is just shorthand for a string list pattern (see below) that includes a group of prepositions. The complete list can be seen by using the command 'exec return master()->query_word_list("preposition");'
Honestly I don't know why this exists. I'm documenting it for completeness, but you should avoid using it because there's no way for players to see the list of words.
The captured argument is the proposition that matched.
{string1|string2|string3|...}
Matches one of the strings in the list. The captured argument is the string that matched. A list may consist of just one string.
Optional Elements
It's possible to make parts of a pattern optional by enclosing those parts in [square brackets].
To begin with, you can enclose any plain word or words in your pattern, which means if the player enters that text in their command, at that pattern position, then the text is ignored. This is typically used for short words that the player can use to make the command read more naturally but that aren't important to actually handle the command. For example, if the pattern for your "frotz" command is "wombat [with] gizmo", then the player can enter either "frotz wombat with gizmo" or "frotz wombat gizmo".
The text inside the brackets must match exactly for it to be ignored, including if it consists of more than one word. So if your "frotz" pattern is "wombat [with the] gizmo", then the player can enter either "frotz wombat with the gizmo" or "frotz wombat gizmo". But any other combination of "with" and "the" will result in a pattern mismatch.
You can have multiple optional words in a row, as in "[in] [the] bucket", but be aware that even though the words are optional, the order still matters. So "the in bucket" won't work.
You can also include alternate strings within the brackets, separated with the '|' character. In this case if any of the alternates match the input it will be ignored. If you change your "frotz" pattern to "wombat [with|using] gizmo" then the player can enter "frotz wombat with gizmo", "frotz wombat using gizmo" or "frotz wombat gizmo".
The <string>, <word>, <number>, <decimal>, <ordinal>, and <preposition> specifiers and string lists (ie, "{string1|string2}") all may be optional. Note that except for "[<preposition>]", a default argument is still captured even if the player omits the optional element from their input. The default values (defined in <user_parser.h>) are:
| [<string>] | PARSER_UNSET_STRING | |
| [<word>] | PARSER_UNSET_STRING | |
| [<number>] | PARSER_UNSET_NUMBER | |
| [<decimal>] | PARSER_UNSET_DECIMAL | |
| [<ordinal>] | PARSER_UNSET_ORDINAL | |
| [{string list}] | PARSER_UNSET_STRING |
Syntax Helpers
All of the special command pattern strings will automatically have a short description in the command's syntax message. You can override the default description by putting your own inside single quote marks just inside the closing angle bracket (or curly brace for string lists). For example if you have a "<string>" in your pattern and you would like the syntax message to identify it as a player name, you can change it to "<string'player name'>"
The Handler Function
The signature for the command handler function (assuming you don't specify your own with a function pointer) will be one of:
int func(object *indirect_obs, string dir_match, string indir_match, mixed *args, string pattern);
int func(mixed *indirect_obs, string dir_match, string *indir_match, mixed *args, string pattern);
The reason for this is those wonderful words, 'backwards compatibility'.
Note: If the command is added though an add_item the verb is passed as the first argument and then the other arguments, so the two possible signatures become:
int func(string verb, object *indirect_obs, string *dir_match, string indir_match, mixed *args, string pattern);
int func(string verb, mixed *indirect_obs, string *dir_match, string* indir_match, mixed *args, string pattern);
Standard Arguments
The indirect_obs and indir_match parameters are variable. If there is only one "<indirect>" specifier in the pattern then indirect_obs will be an array of objects that matched the input and indir_match will be the input string they matched against.
So if you have a "dump" command with the pattern "<indirect:object:me>" and you entered the command "dump swords" then (assuming you are carrying swords) indir_match would be "swords" and indirect_obs would be ({ all the swords in your inventory }).
If there are multiple "<indirect>" specifiers then indirect_obs will be an array of arrays, with each inner array being the objects that matched the input for each corresponding "<indirect>" specifier. And indir_match will be an array of strings from the input that each inner indirect_obs array matched against.
If you change your "dump" command pattern to "<indirect:object:me> into <indirect:object:here>", and you enter the command "dump swords into bins", then indir_match would be ({ "swords", "bins" }) and indirect_obs would be ({ ({ all your swords }), ({ all the bins in the room }) }).
The args parameter is an array of all the arguments captured by the special pattern strings described above, in the order they appear in the pattern.
The pattern parameter is the pattern which was matched. This is needed because sometimes multiple patterns are defined for the same verb, and you may need to know which was used.
The dir_match parameter is currently unused.
Using a Function Pointer
If you supply your own function pointer for the handler, then you can choose which of the parameters you want. The standard arguments described above can be referenced using the placeholder parameters $1 through $5 (matching the arguments in the default handler signature; $1 is replaced with indirect_obs, $3 with indir_match, etc). Thus if you just want to know the first indirect object and the second argument string you would use:
add_command("bing", this_object(), "<indirect:player> {up|down}", (: my_fun($1[0], $4[1]) :));
and declare your function as:
int my_fun(object ob, string param);
Calling the Handler Function (The Gritty Details)
When we're trying to handle a command, we start by going through all the patterns in the order of most complicated to least. Each pattern is checked against wherever the player entered. If there's a match, then we proceed down one of two possible routes.
Case 1: There is NOT a "<direct>" specifier in the pattern
—————————————————————————————
In the situation where there are no direct objects, first any "<indirect>" specifiers are checked to make sure there are objects that match. Any ":direc-obs" options are ignored. If no objects match we go back and try the next pattern. Otherwise we continue.
Next we go through each object that provides the verb, in the order of most-recently added to the oldest, and attempt to call the handler function on each one. If a function was given in the add_command call, then we try to evaluate it with the standard arguments. Otherwise if a "do_verb" function (eg, "do_read") exists in the object we call that with the standard arguments. If all else fails we try to call the function command_control on the object, passing the verb as the first argument and then the standard arguments.
This loop continues until we run out of objects to try or one of the function calls returns a value that tells us to stop (more on this in the Return Values section below).
No success or failure messages are generated automatically. It's up to the handler functions to add an appropriate message.
Case 2: There IS a "<direct>" specifier in the pattern
———————————————————————————
In this situation, first the "<direct>" specifier is checked to make sure that there are matching objects, then any "<indirect>" specifiers are checked for object matches — except those using the ":direct-obs" environment option. If either of these fail then the pattern fails and we try the next.
Next we consider each direct object one at a time. First we check if the object is one of the providers of the verb. If not, we skip it and go onto the next one. If there are any "<indirect>" specifiers with the ":direct-obs" option then those are checked for matches against the inventory of the direct object. If this fails then we skip to the next direct object.
Next we try to call the handler function on the direct object. This is very similar to Case 1 — if there's a function pointer we try to call that with the standard arguments, otherwise if there's a "do_verb" function we try to call that with the standard arguments, otherwise we try to call command_control with the verb and the standard arguments.
This loop continues until we've considered all the direct objects, with one exception which I will talk about in the Return Values section.
Success and failure messages are generated automatically, but you can of course provide your own.
Return Values
The return value of the handler function determines whether more patterns will be checked or not. It might help to think of the return value as a judgment on whether the pattern was handled properly or not.
In the no-direct-object case, the possible return values are:
| 0 | Continue checking further verb providers. If all of their handler functions return 0 then check the next pattern. | |
| -1 | Stop checking verb providers. Consider the pattern a failure and do not check other patterns. | |
| other | Stop checking verb providers. Consider the pattern a success and do not check other patterns. |
In the have-direct-objects case, the possible return values are:
| 0 | Continue checking further direct objects. If all of their handler functions return 0 then check the next pattern. | |
| -1 | Continue checking further direct objects. If all of their handler functions return 0 or -1 then consider the pattern a failure and do not check other patterns. | |
| PARSER_SKIP_OTHER_OBJECTS | Stop checking direct objects. Consider the pattern a success and do not check other patterns. (Defined in <parser.h>) | |
| other | Continue checking further direct objects. If *any* handler function returns something other than the above values then consider the pattern a success and do not check other patterns. |
