******** Please note this is a rough draft of how verbs work. ************ ******** Until this has beeks and the rest of the lima admin ************ ******** approval it will be considered just that a rough ************ ******** draft. The purpose of this is to give someone a very ************ ******** high level picture of what is happening in the verbs. ************ ******** Updated by Loriel 2003 ************ One of the most often asked questions is what is the difference between a verb and a command. The "major" difference between verbs and commands is the way in which they are parsed and the 'tokens' the recognize. Commands use tokens like cfile etc which parse filenames etc. Verbs use tokens like OBJ, LIV and are suited for game like stuff. Verbs are used for game type stuff ( i.e. look, get, drop etc.), and most verbs have heavy interaction with other objects. The first part of this document is a post on Lima Bean from Beek regarding the parser. ___________________________________________________________________________________ .... [ snipped for the reason that it wasnt necessary ] .... First of all, I'm not guaranteeing that the current interface will be the final one, so the given examples are a good indication of what things will probably look like, but not necessarily. [ more snipped ]. Basically, if you are thinking of writing 100s of verbs, you're jumping the gun a bit as the parser isn't finished yet. If you don't mind making small tweaks to them as design changes roll through, though, you'll be fine. _____________________________________________________________________________________ The following is the spec that Beek supplied: The efun interface: void parse_init() This efun alerts the driver that this_object() is an object that the parser should recognize as a 'game' object. All objects that do not call parse_init() are ignored by the parser. Nothing is actually done at this time; the internal parser structures for the object are set up, and the object is marked as being a parseable object, and is marked as being in a non-setup state. Objects that are not setup will have the parse_* functions called on them *once* at a later point when the driver decides it needs the information for that object. After that, the driver remembers what the functions returned. See parse_refresh(). void parse_refresh() Notifies the driver that the result of the parse_* functions may have changed. Basically, the driver throws away it's saved information and marks the object as not being set up. void parse_add_rule(string verb, string rule, int flags) Adds rule 'rule' for verb 'verb' to the internal parse tables. Rule is in the form 'OBJ in OBJ'. 'flags' is reserved for future use. mixed parse_sentence(string input) Asks the driver to handle the input string 'input'. Return values are 1 for everything went spiffy, 0 for 'I haven't a clue what the hell that is' and a string error message if 'input' doesn't make sense, but the parser has a good idea what was intended. parse_sentence("put belboz in trashcan") -> "Belboz vehemently refuses to be put anywhere."; many of these messages are the result of negotiation with the mudlib, altough there are some canned ones (parse_sentence("get foo") -> "There is no foo here."). This is by far the most complex one. The others are bookkeeping/interface more or less. The algorithm is more or less as follows: 1. Find the first word and lookup the appropriate verb. 2. For each rule for the verb, match it to the remaining input string and attempt to fit parse the sub parts (i.e. for 'belboz in bag' and 'OBJ in OBJ' try to parse 'belboz' and 'bag' as OBJs) -> at this moment, support for OBJ, OBS, LIV, STR, WRD is planned. Probably more to specified stuff like 'an object I am holding' etc. 3. The basic algorithm for parsing an object is to first check if the object table has been loaded (from a previous attempt to parse an OBJ rule). If not, parsing information is loaded from the objects in environment(this_player()). 4. The substring is then iterated over, checking the grammatical types of the words, and seeing if it makes sense/matches an object 5. Assuming a rule is properly matched (there is a non-ambiguous object that matches each OBJ rule, etc) the parser then attempts to check if the rule make sense. It calls the following: -> can_verb() in the zorker object e.g. can_look() { if (!light) return "It is dark"; } 6. Actually execute the verb. verb_obj->do_verb(object io, object do) There are some details about imprecise matches, error recovery, and error priorities which have been left out here for simplicity. They only improve the error messages, not the functionality. -Beek ___________________________________________________________________________________ One of the most important things to remember to do in your verb handler ( the file in /cmds/verbs/) is to inherit VERB_OB the important functions are as follows: do_(verb)() -- This is where the functionality of the verb takes place. add_rules() this function tells the driver what 'rules' can be associated with the verb i.e. OBJ, LIV, STR etc. Also this is where you set the aliases. e.g. get and take will be aliases so that there is only one verb for those. can_(verb)() -- This function should do nothing other than tell the driver if it is possible for this to happen. By default it returns 1, meaning "yes, it can happen" The following 2 functions are used with direct/indirect object respectively, to check whether the action is allowed. direct_<verb>() called when there is a direct object. Should return 1 for "Yes it can happen", with 0 or a string meaning "No, it can't happen" 0 will result in a generic error message. eg "You can't do that", whilst a returned string will be used as the message for why the verb can't be executed (eg "You need to have a spade to dig") indirect_<verb>() indirect called when there is a indirect object. Works in a similar fashio to direct_<verb>() ______________________________________________________________________________ Lets look at a very simple verb: xyzzy (from the original ZORK adventures). inherit VERB_OB; mixed do_xyzzy() { write("A hollow voice says: Fool!\n"); } void create() { add_rules( ({ "" }), ({ "plugh", "plover" }) ); } This is a very simple verb. the do_xyzzy does the action for the verb. There is no can_xyzzy, so the default tells the driver "OK, this action can happen". the add_rules sets the optional arguments for the verb (in this case only the verb xyzzy by itself is valid). plugh and plover are aliases. ______________________________________________________________________________ Lets look at another simple example of a verb - extinguish. inherit VERB_OB; void do_extinguish_obj(object ob) { ob->do_extinguish(); } void create() { add_rules( ({ "OBJ" }) ); } This means that the only syntax is "extinguish <object>", that the can_ and direct_ checks will be in the object rather than the verb, and that the action will call do_extinguish() in the object - so that different objects can implement "extinguish" in different ways. In fact, the do_extinguish() function here is unnecessary, as this is the default action for a verb, but is retained for clarity. ______________________________________________________________________________ A simpler form of the "give" verb could be implemented as : inherit VERB_OB; mixed do_give_obj_to_liv(object ob, object liv) { this_body()->targetted_action("$N $vgive $o to $t.\n", liv, ob); ob->move(liv); } void create() { add_rules( ({ "OBJ to LIV" }), ({ "hand" }) ); } In the above example, notice the difference in the function names. They correspond to the rule/rules in query_verb_info(). please note the existence of the following in /std/living.c: indirect_give_obj_to_liv() { ...... } direct_give_obj_to_liv is in /std/object/vsupport.c: (which is where things go that dont really have a home yet) The actual implementation of the "give" verb is somewhat more complicated than the simple version above, adding error checking for the do_give_obj_to_liv() function, and implementing other cases to handle giving multiple objects (ie the "OBS" rule) and to handle money via the "WRD STR to LIV" rule and do_give_wrd_str_to_liv() function ("give 2 gold coins to fred" etc).