Macros and Stuff in General --------------------------- When the player types a command, the following, rather lengthy sequence occurs: 1) If the command starts with a " or a :, a suitable call to say or do, with the remaineer of the line passed as the single parameter, is generated. Thus, anything whatsoever in a " type say, or : type pose is said or posed without further mashing. If this case occurs, no other processing is done. Otherwise we proceed as follows: 2) If 1) is not the case, the line of text is hacked up into a sequence of sets of tokens. A token is either a word, or a quoted phrase. Both " and ' are allowable quotes, and one type of quote may be used inside a phrase quoted with the other. \ escapes also work. The sets of tokens are delimited by semicolons. Thus: foo "bar baz";frob "thing's; and other stuff" bop breaks up into sets of tokens: (foo, bar baz) (frob, things's; and other stuff, bop) In addition, the appearance of a equals sign (not in quoted text) forces the remainder of the line to be treated as a single token. Variable substitution also takes place at this stage, see below. 3) Each set of tokens is examined in turn, and an attempt made to match the first against a 'command' of some sort. When a matching command (or exit, or what have you -- see below) is found, it is invoked with the remaining tokens in the set as an argument list. How Commands Are Matched ------------------------ If the first token (referred to hereafter as the 'command') starts with an @, the @ is stripped off, and the remainder of the token is used to search the server-internal command tables, and then the system macro table for a match. In event of a match, the matched command is run, and no more processing occurs on this set of tokens. In the event of no match, an error is displayed to the player invoking all this, and again, no further processing is applied to this set of tokens. (Therefore, any command starting with an '@' *will* match either a system command or a system macro, and not any other macro in the area.) Next, the command is matched against the list of exits in the room where the player responsible for invoking all this is. If a match is found, a suitable call to 'go' is generated. As always, this completes processing on this set of tokens. If all this has failed so far, the command is matched against macros (attributes of type cmd) on the player, room and the object the player is holding, in that order. In 1.12 and above, you can define a compile-time option to make it search the player's entire inventory, after searching the thing they are holding. If a matching macro is found, then *all* of the processing described above, starting from the breaking up into tokens, is applied to the text of the macro. The tokenizer will, at this point, use the arguments to command (the remaining tokens in the set) to substitute into the tokens generated by tokenizing the text of the macro (see below, again). The first matching macro, if any, is the only one called. Note that at this stage, we are running recursively. Macros can certainly invoke other macros and so forth. Finally, if no matching macro has been located, the server internal command table and then the system macro table are searched for a match. Variable Expansion ------------------ When a macro is matched, we refer to all the tokens in the set whose first token matched as the arguments of the macro. As the macro text itself is tokenized, tokens beginning with dollar signs are replaced according to the following rules: $$<text> Is turned into $<text>. Note *double* dollar signs. $n where n is a number, is replaced with the nth argument to the macro. The 0th argument is the first token of the original token set, i.e. was the token matched against the macro name. $* is replaced by a *single* token made up of all the arguments to the macro, including the macro name itself (the 0th argument), separated by spaces. $+n where n is a number, is replaced by the nth and succeeding arguments to the macro, again as a single token separated by spaces. $# is replaced by a number signifying the number of arguments given. $actor.xxx (or, equivalently, $xxx) is replaced with the xxx attribute of the player whose actions led to this macro being run. $me.xxx (or, equivalently, $self.xxx) is replaced by the xxx attribute of the object which is actually running the macro. $here.xxx (or, equivalently, $locale.xxx) is replaced by the xxx attribute of the location of the object which is actually running the macro. $subv (or $Subv, $objv, $Objv, $posv, $Posv) is replaced by the subv (or etc) attribute of the player. If the player has no such attribute, defaults are used (it/it/its and captilized varieties). In all above cases, the text following a dollar sign may, optionally, be enclosed in { and }, to provide useful blocking. I.E. $me.nam and ${me.nam} are equivalent. This allows an expansion to be concatenated with the surrounding text. Variable substituition is done inside strings quoted with ", but NOT inside strings quoted with '. This allows variable evaluation within a macro to be defered. ${@thing} is replaced with the object id of the matched thing (or nothing if no match can be made. Exact matches are taken first, then the first partial match is taken). Numeric values are permitted. For example: ${@Mol.nam} will match anything in the room that matches "Mol", and will return its name. ${@4@MyMUD.name} will return the name of the object 4@MyMUD, assuming such an object exists in the database. ${!number} is replaced with the object id of the matched parameter number (or nothing if no match can be made. As always, exact matches are preferred, but first partial match will be taken if there is no exact match). The numeric value must be within provided parameter count. For example: ${!1.nam} will return the name of the object matching $1. If there is an object in the room named the large stripy cat and there is a command on the player cmd pet=@do pets ${!1.nam}. which is invoked as pet stripy the following occurs: So-and-so pets the large stripy cat. Chaining of attributes is performed as follows: Owners (and wizards) can always see all attributes, regardless of the objects location. The attribute "name" is defined as a PUBLIC attribute, meaning anyone can get it, regardless of ownership, and regardless of location. The attributes "description", "text", "subv", "Subv", "objv", "Objv", "posv", and "Posv" are defined as LOCAL_PUBLIC, meaning anyone can get those attributes, regardless of ownership, IF the object is in the same room as the player. All other attributes default to PRIVATE, meaning only the owner can get them, regardless of location. However, any attributes with "+" as the first character in their name are defined as LOCAL_PUBLIC. As long as an attribute continues to resolve to a valid and reasonable object, the attribute names are looked up iteratively. For example: _echo ${me.loc.nam} will first grab "me" (which resolves to an object id), and then look up the location (which is also an object id), and then will return the name of that. Similarly: _echo ${@45@MyMUD.loc.nam} will return the name of the location of the object 45@MyMUD, assuming the player does own that object. It is not necessary for the player to be able to own the location of 45@MyMUD, because it is returning the name attribute, which is defined as PUBLIC. It is also now possible to get non-strings returned (as strings). For instance, $here.con will return a list of object ids that are in the room's contents (assuming the player owns the room). Examples -------- A simple macro: cmd bonk=@do "BONKs $1" If this is an attribute of your player object, typing 'bonk foo' is entirely equivalent to typing '@do "BONKs foo"'. In detail, 'bonk foo' tokenizes to (bonk, foo) Since 'bonk' matches the attribute, the macro text is tokenized in to (@do, "BONKS foo") which is the same token set you'd get by typing '@do "BONKs foo"'. --- Another simple (and popular) macro: cmd say=@do "purrs, \"$+1\"" Now, typing 'say hello, there' tokenizes out nicely to (@do, "purrs, \"hello, there\"") which comes out as so-and-so purrs, "hello, there" Notice the quotes. They had to be escaped in the macro; otherwise they would have been stripped. Using '"' instead of 'say' will also trigger the 'say' macro. --- (thanks to Tarrant for inspiring this) Suppose you wish to alias 'look' to 'study' with a macro. You want to be able to use both 'study' and 'study <thing>' to look at the room, or at a thing. The naive approach is to attach an attribute to yourself: cmd study=@look $1 Then typing study foo will work fine, generating a token set (study, foo) which matches the attribute. Running the macro through the tokenizer will give you (@look, foo) which will do the right thing. Alas: study will NOT work. This will tokenize to (study), which will match the macro. Tokenizing the macro, and substituting for the $1 will give (@look,""). The look command will cheerfully try to match the empty 2nd token against things here and there, and not find anything. You will NOT see the room, in particular. To make it work right, you get to do this: cmd study=@set me cmd xxxstudy "look $1";xxxstudy;@unset me xxxstudy Now. What happens when you type 'study foo'? First you get the token list (study, foo). This matches the macro above, and expands into token sets: (@set, me, cmd, xxxstudy, "look foo") (xxxstudy) (@unset, me, xxxstudy) The first is run, and generates a NEW attribute on you: cmd xxxstudy=study foo Then the 2nd is run, simply running this new macro, and the 3rd set tidies up. If you type 'study', this happens. The token list (study) is matched against the study macro, which expands: (@set, me, cmd, xxxstudy, "look ") (xxxstudy) (@unset, me, xxxstudy) Which makes a macro cmd xxxstudy=look runs it, and tidies up. --- For more macro examples for neat building tricks, see the files "ferret.unter" and "Macro.tricks" (the latter for lots of dirty macro tricks).