Foxen's MPI Tutorial

From Redwall MUCK
Jump to navigation Jump to search

An Introduction to MPI in the fb5.xx Muck Game.

Written by Foxen, October 12, 1993

SECTION I: "What is MPI, and why should I use it?"

MPI the language, and this document are CopyLeft 1993, by Fuzzball Software and Graphic Arts.

So, you've heard about this thing called MPI and you've been told that it can do all sorts of neat things, but you still wonder what it is? Well, I'll tell you..

MPI is My Personal Insanity. That's my unofficial name for it. I must have been crazy for having written 3500 lines of C code to implement it. The official name for it, is Message Parsing Interpreter. That sounds a lot more ostentatious, and looks better on a resume'. =)

MPI is a script language that lets you embed commands within plain text, to do things like substitute in the text value of a property. It's used in messages like @descs, @succs, @fails, @drops, @osuccs, @ofails, @odrops, and your sex property. Those of you who use the $desc, $wf-desc,or @6800 programs are likely familiar with this sort of idea. MPI works like those programs, only much faster, and with many more possibilities.

So, what's in it for you to use MPI? Well, you can make descriptions longer than the 512 character input limit, by splitting them up into sections that are in different properties, and substituting them into your main @desc. This also makes it easier to update only one part of the description, such as what you are wearing, by changing only the property that has your clothes described. You can also make multiple line descriptions, by making a list with lsedit, or another list editor, and using the MPI command to display a list in your @desc. For those of you who want to get fancy, you can make your description depend on what you are carrying. For example, you can make your description mention that you are wearing sunglasses, only if you are actually carrying them. The possibilities are endless.

Because the commands are read and interpreted by the game itself, they run quickly, producing less lag for the game than the MUF description programs like @6800. Also, you can port your descriptions for objects between fb5.0 MUCKs, without having to worry if the other muck has the right MUF programs, or uses the same list styles. You can also get much fancier with MPI than you can with @6800, since there are many more commands available to you.

Okay, okay, that's enough evangelizing.. Lets get onto the next section.

SECTION II: "So what does MPI look like?"

MPI has a fairly simple structure, mostly. It goes like this:

{command:arg1,arg2,arg3...}

The open-brace { says that the text until the matching close-brace } is going to be an MPI command that the game should interpret. The first word inside the braces, before the colon, is the name of the MPI command that you want it to run. The rest, after the colon, are the arguments, seperated by commas. MPI commands often take two or three arguments, though some take many more, one, or none at all. In fact, a lot of MPI commands can each take multiple different numbers of arguments, using default values for any arguments that aren't given. If an MPI command takes no arguments, you can omit the colon. Here's some examples of MPI commands:

{prop:_clothes,me}

This example runs the MPI command "prop" with the two arguments "_clothes", and "me".

{nl}

This runs the MPI command named "nl" with no arguments.

If you want to pass a comma as part of an argument, you have to mark it to say that you really mean for the comma to get passed to the function, and that it isn't there to seperate arguments. To mark it this way, you put a backslash \ in front of it. The backslash means that the next character following it has no special meaning, and that it is to be copied literally. This makes a comma look like part of an argument, instead of like an argument seperator. This is called escaping a character. You can also escape an open-brace { to keep it from trying to interpret the text after it as an MPI command. Also, you can escape backslashes. In fact, to include a backslash in text, you HAVE to escape it. Almost any character after a backslash will be copied literally.

The only exception is \r which is replaced by a carriage return character. A carriage return character tells the game to split the text into two lines at that place, when the text is displayed to the player.

Example:

{store:\{Here\\now\}\, she thought.,_when}

This will run "store" with the two arguments "{Here\now}, she thought.", and "_when". The first backslash \ escapes the open-brace {, copying it into the argument literally. The next backslash escapes the backslash that follows it, making the backslash copy into the argument literally. The close-brace has to be escaped, also, or else the game would think that you are trying to run the "store" command with only the argument "{Here\now". The comma is escaped to say that it is part of the argument, and not an argument seperator. The NEXT comma, however, is not escaped. The game sees this and goes, "Aha! A second argument follows!" Then it gets to the last close brace }, and sees that it has gotten all the arguments. At this point, it calls "store" with the two arguments that you passed.

Note that several backslashes in a row will escape each other, alternating between escaping and escaped characters. What this means is that the game will take a look at "\\\{" and see an escaped backslash and an escaped open-brace. Literally: "\{". However, it will see "\\\\{" as two escaped backslashes and a NON-escaped open-brace. It would try parsing anything after the non-escaped open-brace as an MPI command.

MPI commands can have MPI commands nested within them, so they can take the output of other commands and use them as inputs. Example:

{count:{contents:here}}

This would first run the MPI command "contents" with the argument "here". Then the "count" command would be run with the string returned from the "contents" command as its single argument. Commas and other characters inside strings returned from nested commands will all act like they have been escaped. If they didn't, they would cause unexpected problems, as your commands would find more arguments than they expect! Luckily, you won't have to worry about that problem.

SECTION III: "Now that I know what MPI looks like, how do I use it?"

MPI commands are run from within messages like your @description, your @success, or your @ofail, to name just three. You just set the message like you always used to, but you also include some commands within the text of the message. When the message is displayed by the game, it sees the commands in the text, and runs them. It takes whatever text they may return, and puts that in the text to be displayed, in the place where the command was.

Example:

@desc me=You see a pretty young girl with {prop:_haircolor) hair.

When someone looks at you, with that description set, the game will run the "prop" command with the argument "_haircolor". The "prop" command will then look for a property named "_haircolor" on the object the command is on. It then replaces the call to itself, in the output text, with the string value of the property. If the "_haircolor" property was set to "golden", then the looking player will see "You see a pretty young girl with golden hair."

Unlike the various MUF description programs such as @6800 and $wf-desc, MPI commands can be inside of omessages, such as @ofail, @osucc, and @odrop. This lets you do things like make an @osuccess message properly reflect a varying @succ message.

SECTION IV: "Right, so what are the commands?"

There are a lot of MPI commands that you can use. Over a hundred of them, in fact, so I'm not going to list them all right now. I'll just give you a run-through of a few of the most useful and popular ones.

{prop:propname,object}

This command looks for a property with the name propname on the given object. If it doesn't find it there, it looks down the environment from the given object. If no property by than name exists on that object, or in its environment, then this returns an empty string. Otherwise, it returns the string value of the property. If no object argument is give, then it assumes that the given object is the trigger object. The trigger object is the object that has the MPI commands in a message on it. An example of the use of {prop} is: @desc me=You see a young woman who is wearing {prop:_clothes}. @set me=_clothes:a blue blouse and a short green skirt

{exec:propname,object}

This command is almost exactly like {prop}, except that any MPI commands within the string value of the property are executed, and for their running, the trigger object will be the object that the property is on. This lets you have MPI commands embedded within the properties that store various parts of your @desc, if your @desc is split up into multiple properties. An example of the use of {exec} is: @desc me=You see a young woman who is wearing {exec:_clothes}. @set me=_clothes:{prop:_clothes/top} and {prop:_clothes/bottom} @set me=_clothes/top:a blue blouse @set me=_clothes/bottom:a short green skirt

{list:listname,object}

This will load in a property list with the given listname, from the given object. It loads the list as a single string, with each list item seperated from the next by a carriage return character. These carriage return chars will each start a new line, seperating the text into multple lines, when they are displayed to the user. A property list is a set of numbered properties with string values. For example, a set of properties named "list1", "list2", "list3", etc is a list named "list". The {list} command understands several styles of lists, so "list#/1", "list#/2", etc, and "list/1", "list/2", etc, are both also recognized by the listname "list". This means that almost every known MUF list editor should be able to make a list that {list} can read. If no object argument is given, this command will assume that the given object is the trigger object. The environmental searching for the list is the same as for {prop}.

{concat:listname,object}

This is almost exactly like {list}, except that instead of seperating list items with carriage returns, it seperates them with spaces. The number of spaces depends on how the previous line ends. If it ends in a period, question mark, or exclamation mark, it seperates the lines with two spaces, otherwise, it uses only one.

{rand:listname,object}

This is sort of like {list}, except that it will randomly pick a line from the given named property list and return it, instead of returning the whole property list.

{timesub:period,offset,listname,object}

This is sort of like {list}, except that it will only return one line of the given named property list. The line it chooses depends on the time. The period is the length of time, in seconds, that it takes for {timesub} to cycle through the entire list. The offset is the number of seconds to offset into the time period, if you actually need to synchronize the {timesub} with something. The offset usually is just left at zero. What this all means, is that if you have, for example, a period of 3600 (one hour), an offset of zero, and a property list that has six items in it, then {timesub} will return the first line of the property list during the first ten minutes of the hour, the second line during the next ten minutes, and so on, until it returns the last line during the last ten minutes of the hour. Then it returns the first line for the beginning of the next hour. Here's an example: {timesub:86400,0,_sunmoon} This example will show different property list lines, depending on the time of day. The period is 86400 seconds, which is one day. If the property list has 24 items in it, then a different line will be returned for each hour of the day.

SECTION V: "And just how do I refer to objects?"

Okay, you can reference objects in basically the same ways that you can in user typed commands such as @desc, or @lock. You can refer to obj- ects by name, if they are in the same room as you, or in your inventory. You can refer to objects by dbref or registered name, if you need to be able to refer to the object if it is not in your vicinity. And you can refer to players by name, with a * in front, no matter where they are.

The following are examples of the accepted ways to refer to objects. object Referencing by name. #1234 Referencing by dbref. $regname Referencing by registered name. *playername Referencing by player name.

When an MPI command returns a reference to an object, (we'll discuss some of these commands in the intermediate guide) it will refer to nonplayers by dbref, and to players by starred name. ("*playername")

SECTION VI: "Cool. So how what stuff can I run mpi from?"

There's lots of things that'll parse MPI commands. To start with, the @desc, @succ, @osucc, @fail, @ofail, @drop, and @odrop messages will all parse whatever MPI code you put in them. Also, when a player connects to the muck, or disconnects, you can run MPI. Also, when a player moves from room to room. You can even have MPI code that hears whatever is going on in the room.

To run MPI commands when a player logs in, you need to make a special property on the player, or on the room where the player resides. To do this, just simply do:

@set =_connect/XXX:&

The XXX can be anything. The properties will be evaluated in alphabetical order. The result of the mpi code is displayed to the connecting user. The ampersand (&) that starts the property value tells the server that it has MPI commands in that property that it should evaluate. As an example:

@set here=_connect/room_motd:This example will show the room's message of the day, that is stored in the _room_motd property list on the room, to the player logging in. There is a different set of properties, with names starting with "_oconnect/", that also runs when a player connects, but the results of the MPI code will be shown to the other people in the room, and not the player. For example:

@set here=_oconnect/herald:&Hey! {name:me} has connected!

Will tell everyone in the room that someone just connected in the room, in a somewhat heraldic fashion. =)

When a player logs off from the muck, the properties under the _disconnect and _odisconnect propdirs are evaluated in a similar fashion. When a player leaves a room, the properties under the _depart and _odepart propdirs are evaluated. When a player enters a room, the properties under the _arrive and _oarrive propdirs are evaluated. Here's some examples:

@set here=_odisconnect/elvis:has left the muck!

@set here=_arrive/enter-details:@set here=_odepart/cabbie:&The cabbie watches {name:me} storm out.

Note: Props beginning with ~ require a wizbit to set.

When something is heard in the room, properties in the ~listen and ~olisten propdirs are run, from all things in the room. The {&arg} variable holds the message that was heard.

The syntax is: @set =~listen/XXX:=&

For example:

@set here=~listen/echo:&You hear an echo: {&arg}

That will echo whatever it hears in that room, to the player who made the noise. You can also make listeners be restricted to only hearing certain patterns of words. To do this, you need to put in a matching pattern at the beginning of the property value.

For example:

@set here=~listen/aod:Klaatu barada nicto=&The sky rumbles at You!

@set here=~olisten/aod:Klaatu barada nicto=&The sky rumbles at {name:me}!

This example would have the skies rumbling at any player who said or posed the words "Klaatu barada nicto" in the room.

You can use MPI in locks, too, but only indirectly. The way you do it, is you lock to a property and the result you want, then you make that property contain the MPI code. For example, to have a room display its fail message when there are more than ten people in the room, do this:

@fail here=The room's rather crowded at the moment.

@succ here=The room's not so crowded at the moment.

@lock here=_crowdedroom?:no

@set here=_crowdedroom?:{if:{gt:{count:{contents:here}},10},yes,no}

In this example, the MPI code in the _crowdedroom? property will return "yes" if there are more than ten items in the room. Otherwise, it will return "no". The lock on the room evaluates that MPI code, and checks to see if it returned "no". If it did, then the success is shown to the player looking at the room. Otherwise, the @fail is shown to the looker.

Foxen's Intermediate Guide to MPI Programming

SECTION I: "Okay, I know the basic stuff in MPI. What else can I do?"

MPI can do a lot more than just substitute in lists or properties into a description. It can be conditional in what it substitutes, based on all sorts of criteria. It can process information, and do full floating point math. It can set the values of properties, and force puppets around. In fact, MPI can do a LOT of things. In the next few sections, we'll cover several of the main command types that MPI has, including conditionals, variable handling, stringlist handling, and loops.

SECTION II: "And what, pray tell, is a conditional command?"

A conditional command is a command that evaluates an expression, and, depending on the result, decides whether to run one set of commands, or another. In MPI, there is only one current conditional command: {if}.

The {if} command has the syntax of:

{if:1,Yes,No} This will return "Yes". {if:0,Yes,No} This will return "No". {if:1,Yes} This returns "Yes". {if:0,Yes} This returns a null (empty) string.

{if:{prop:test},Yes,No}

This returns "No", if the property "test" has the value "0", or if it does not exist. Otherwise, this returns "Yes".

{if:{prop:_clothes},{prop:_clothes},{prop:_nude}}

If the property "_clothes" is set, and is not "0", then this command set will return its value. Otherwise, this will return the value of the "_nude" property.

{if:expression,truebranch,falsebranch}.

It evaluates the expression, running any MPI commands embedded within it, and if the expression evaluates out to being a null (empty) string, or a zero (0), then {if} will evaluate the falsebranch, executing any MPI commands inside of it, and returns the result. If any other string was returned by the expression, then the truebranch is evaluated in a similar way, and the result is returned. Only one of the truebranch or falsebranch options is evaluated, and {if} will return the result of that option's evaluation. If no falsebranch is given, then if expression evaluates false (as a null string, or "0"), {if} will return a null string. Here, let me give you a few examples:

"Aha!", you say, "I see what {if} is good for, now! Choosing default values if a property doesn't exist!" Well, that's just one thing that {if} is good for. But to be really useful, you need to have some operators to put in that test expression. Here are a few of them now:

{if:{eq:{prop:_clothed?},yes},{prop:_clothes},{prop:_nude}}

This will check if the value of the "_clothed?" property is "yes", and, if it is, it returns the value of the "_clothes" property. Otherwise, it returns the value of the "_nude" property.

{eq:expression1,expression2} will evaluate both expressions, and test to see if they return the same result string. If the expressions both result in numbers, then the numbers are compared by value. That means that {eq:1,01} with return true ("1"). You can test strings against numbers, and it will check them as strings, and not numbers. Also, null (empty) strings are not considered to be numbers. {eq} will return true ("1") if the result of both expressions is the same string, or number value. Otherwise, it will return false ("0"). When it is comparing strings, it is case insensitive, so an "A" will compare the same as an "a". There is currently no way to do case sensitive compares. {eq} is good for testing the values of things like properties.

Example:

{neq:expression1,expression2} is almost exactly like {eq}, except that it returns FALSE ("0") if the results of the two expressions are the same, and RUE ("1"), if the results DON'T match.

Okay, so now you know how to test the value of something, but what if you want to check the value of more than one property? Well, I've provided a few operators to help there, too:

{and:expression1,expression2} will evaluate the first expression, and check if it's false. If it is, then {and} immediately returns FALSE ("0"), with-out even bothering to evaluate the second expression. Otherwise, it tests the second expression, and if that is false, then {and} returns FALSE ("0"). Only if BOTH the expressions evaluate as true, does {and} return true ("1").

This will check if the "_clothed?" property is set to "yes", and the "sex" property is set to "male", and if both tests are true, then it returns the value of the "_ballcap" property. Otherwise, it returns a null (empty) string.

Example:

{if:{and:{eq:{prop:_clothed?},yes},{eq:{prop:sex},male}}, {prop:_ballcap}}

As you can see in the above example, expressions can get hard to read, when there are a lot of levels of braces {}. From now on, I'll format complex examples for readability. When using them in practice, however, you'll be putting them on one line. The above example, formatted nicely, looks like:

{if: {and: {eq:{prop:_clothed?},yes}, {eq:{prop:sex},male} }, {prop:_ballcap} }

Another useful operator is {or:expression1,expression2}. This operator is a lot like {and}, except it returns true ("1") if EITHER expression evaluates as true. Also, if the first expression evaluates as true, then it doesn't even bother to evaluate the second expression. If BOTH expressions evaluate as being false, then {or} returns FALSE ("0");

{xor:expression1,expression2} will always evaluate both expressions, and if one, but not both of them evaluates as true, then {xor} returns TRUE ("1"). If both expressions return true, or neither expression returns true, then {xor} will return FALSE ("0").

{not:expression} will evaluate an expression, and if the result is true, then {not} returns FALSE ("0"). If the expression's result is false, then {not} will return TRUE ("1"). Basically, this just reverses the true/false value of the expression.

Now, all these operators have only limited uses, when all they can check is what properties exist, and what values they have. There are a lot of MPI operators, though, to check for a lot of other circumstances..

{holding:what,who} checks to see if object what is located in 'who's inventory. If the given player who is holding the given object what, for example, then this command returns true, otherwise, it returns false. If no who argument is given, then it assumes that who is the player running the MPI code. You can check if a player or object is in a room, by testing if the room is {holding} them.

Here's an example of how this is useful:

@create Sunglasses=10=shades

@desc me=You see a man{if:{holding:$shades,this}, wearing shades}.

In the above example, I first @create a thing object called Sunglasses, and personally register it with the name $shades, so I can refer to it later by that name, whether or not I happen to be carrying it at the moment. The object "this" always means the trigger object; the object that the MPI code is executed from. In this case, {holding} checks to see if I'm holding my sunglasses when you look at me. If I am, then my @desc reflects that fact.

{contains:what,who} is almost exactly like {holding}, except that it checks to see if what is either held by who, or is inside of something that who is holding, or is inside something that is inside of something that who is holding, and so on and so forth. If what is somewhere inside of something that who is holding, then {contains} returns true, otherwise it returns false. An unexpected side effect of this command, is that you can see if a player is inside of a room environment by checking if the environ- ment {contains} them.

{awake:player} will test if a player is awake. If the given player happens to be a puppet with the Zombie flag set on it, then this will test if the owner of the puppet is awake. This will return true if the player or puppet owner is awake. Otherwise, this will return false.

Example: @desc me={if:{awake:this},{prop:_awakedesc},{prop:_sleepdesc}} If I'm awake when you look at me, then you will see the value of my _awakedesc property. Otherwise, you'll see the value of my _sleepdesc property.

SECTION III: "And what's that variable handling stuff you mentioned?"

There are a lot more operators in MPI, but I'll not list them here. For a full list of operators and MPI commands, see the mpidocs and mpidocs2 files.

{with:varname,value,commands} The {with} command says to the game, "Hey! I'm making a new variable, and I'm going to name it varname. I want it to start out with value stored in it, and I want you to run all these commands with it defined." The {with} command will return the results of the commands it executes. It is important to note is that the declared variable only exists while running the commands, and it is thrown away afterwards. This may seem silly, but it's actually a way of saying that only those given commands need to use the variable, and the game can free up the memory the variable uses after they are done executing. If you tried to use that variable outside of the {with} block, the game will complain that the variable is not defined.

Sometimes you'll want to evaluate something, and use the results it gives you more than once, without having to recalculate it each time. Well, a variable is very useful for that. A variable is a holder for a string. You store a string in it, and you can use that string over and over, until you are done with the variable, or you store something new in it. Variables are referenced by name, meaning that you have to declare the name of it before you first use it. Here's how you declare a variable:

Now, being able to declare and store a value in a string is all fine and good, but what use is it, if you can't get the data back from the variable? Well, the way you get the value of a variable is with {x:varname}. This will return the value that is stored in the variable varname. A shorthand way of doing this is {&varname}. {&varname} and {x:varname} are effectively identical, except that {&varname} is a little faster, and you can use {x:} if you need to calculate the variable name. (Why you would want to do that is beyond me, though. If you want to do that sort of thing, it's easier to just use properties.)

But what if you want to change the value of a variable after it has already been defined? To do that, you use the {set:varname,value} command. That will store the given evaluated value in the previously declared variable.

Here's an example of how to use this stuff: @desc me=You see a young {with:sexmale,{eq:{prop:sex},male}, {if:{&sexmale},man,woman} who is wearing {if:{&sexmale}, a pair of faded denim jeans., a short green skirt, and blue blouse. } }

In the above example, the variable is used to remember the result of a test that checks if the "sex" property on the player was set to "male". Then the variable is checked in two separate places in the text to choose appropriate text to match the player's current sex.

There are some variables that are standard, that have special meanings. The {&how} variable is a short string telling what ran the MPI commands. It can have the values "(@desc)", "(@succ)", "(@osucc)", etc. for when it is run from an @desc, an @succ, an @osucc, or whatever. It can also have the value "(@lock)" for when it is run from a lock test.

The standard {&cmd} variable contains the command name the user used, that caused the MPI to run. This is generally the exit name that the player triggered. For example, if the player typed east, and triggered the exit named east;e;out, which ran some MPI commands, the {&cmd} variable would have a value of "east".

SECTION IV: "What in blue blazes is a stringlist?"

The standard {&arg} variable contains the command line arguments the user entered. This is so that you can have stuff like MPI in the fail of an exit, and when the user triggers the exit, and has some extra text on the line they entered, other than the exitname, the MPI can take that extra stuff as arguments for use. I'll talk about this more, in the advanced MPI guide.

Think of a catalog of fine yarnballs of the world. That's not what a string list is. =) A string list is also NOT the same as the property lists we discussed back in the Introductory Guide to MPI. However, I'll repeat some of our discussion from there. I mentioned that the {list} command reads in the values of all the properties in a property list, and returns them as a single string, with each item seperated from the next by a carriage-return character. THAT is a string list.

To put it in the general form, a stringlist is a string containing one or more substrings, seperated by delimiter characters. In the default case, the delimiter character is the carriage-return character.

So what are they good for, you ask? Well, a bunch of MPI commands return stringlists, with lists of strings, or objects, or other things. As an example, the {contents:object} command returns a stringlist of references to the contents of the given object. This is actually a very useful thing, and I'll explain the uses of this more in the next section.

You can do a fair bit with stringlists. There are commands to sort them, remove duplicate items from them, get the union of two lists, get the remove the items of one list from another, and a lot more.

{luniq:stringlist} will return a copy of the given stringlist, with all duplicate items removed. In all cases, only the first of two duplicate items is kept, and all other duplicates of it are removed.

{lsort:stringlist} will return a sorted copy of a stringlist, sorted in ascending alphabetical order.

{lcommon:stringlist1,stringlist2} will return a stringlist that contains all of the list items that were found in BOTH of the stringlists.

{lremove:stringlist1,stringlist2} returns a copy of the first stringlist, with any and all items that were found in both of the lists, removed.

SECTION V: "What are loops, and what do they have to do with stringlists?"

The {count:stringlist} command will tell you how many items are in the given string list. A count of zero ("0") will mean that the list was completely empty.


{filter:varname,stringlist,testexpression}

Since stringlists contain several items, wouldn't it be nice if there were some way to do something with each item in the list, no matter how many there are? Sure it would be. That's the main thing that loops are for. Here, let me give you an example:

The {filter} command will take each item in a list, and decides whether or not to keep it, based on a test you give it. It returns a list of those items that the test decided it should keep. It has the following syntax:

Remember variables? Well, the {filter} command, and many other of the loop type commands, use variables to pass the list item under scrutiny to the test expression. But you don't have to define the variable yourself; the {filter} command does that for you. All you have to do is give it the name of the variable you want it to define. For every item in the stringlist, the {filter} command evaluates the test expression, with the current list item in the variable you had it define. If the test expression evaluates with a true result, then that list item is put in the output stringlist. Otherwise, the game just discards that item.

Example: {filter:what,{contents:here},{awake:{&what}}}

{filter:line,{list:mylist},{neq:{&line}, }}

The above example uses the {contents} command that I described earlier, to get a list of the objects in the room where the user is. The {filter} command defines a variable named what, and stores the reference to the first object from {contents} in it. The test expression is then evaluated. The object is tested to see if it is an awake player, or a zombie whose owner is awake. If it is, then the object reference is remembered. Otherwise, the object is forgotten about. This test process is repeated for each and every object reference returned from {contents}. When all of the tests have been done, then {filter} will return a list of all the items that passed the test. So, what the above example does is return a stringlist of references to all the connected players in the room, and to all the zombies in the room, whose owners are awake.

Another example:

{parse:item,{contents:here},{name:{&item}}} This will take the stringlist of references to the contents of the room, that the {contents} command gives, and gets the name of them (since the reference is usually a dbref) to put in the returned list. You can simulate the Contents: listing of a room look with just the following:

{with:cont,{contents:here}, {if:{count:{&cont}}, Contents:{nl}{parse:item,{&cont},{name:{&item}}} } }

The above example will take the contents of the mylist property list, and return them as a stringlist, with all the items that are " " (2 spaces) removed.

Okay, lets try another looping command: {parse} has the syntax {parse:varname,stringlist,expression}. The varname argument is just so you can supply a variable name for it to define, like back in the {filter} command. The stringlist is the list of items that you want it to work on. The difference from {filter}, here, is that instead of using the expression to decide whether or not to keep the item, the {parse} command keeps every item, but REPLACES it with the output of the expression. Umm, to put that clearer, the expression is run on every item in the stringlist, and the result of the expression is put into the string list that the {parse} command will return. So the output list will have the same number of items as the input list, but every item in the input list will have been processed by the expression before being put in the output list. Oh hell, let me just give you an example.

Since each list item is separated from the next by a carriage return, each item's name will be shown to the user on a separate line.

Another really useful looping command is the {commas} command. This is one of the more complex commands in MPI. But what it does is actually fairly simple, once you understand it. It takes a list and puts commas between each item, and an "and" or an "or" between the last pair. This lets you take a list with the items "Tom", "Dick" and "Harry", and get back a string like "Tom, Dick and Harry".

The syntax is {commas:list,andtext} or {commas:list,andtext,var,expression}. The first syntax simply takes a stringlist, and puts it in the comma format. The second syntax lets you do a bit of processing on each list item before it is put in the comma formatted string. The andtext argument in both cases is the "and" or "or" text that you want in the comma seperated list. For example, it lets you make strings like "Tom, Dick and Harry", or else "Tom, Dick or Harry".

The second syntax is the looping one. Basically, it does what the {parse} looping command does, in that it replaces the input list item with the result of the given expression, before putting it in the comma string.

Example:

In this room, you see {commas:{contents:here}, and ,item,{name:{&item}}}

The above will take the stringlist of references to the room's contents, from the {contents} command, and will get the name of each item, before adding it to the comma formatted string. The above might produce: "In this room, you see Tom, Dick, Harry and Bulletin Board."

It should be noted that the spaces around the and in the arguments list, are important, since without them, it would have said: "In this room, you see Tom, Dick, HarryandBulletin Board."

You can get fancy with this stuff, if you really want to. Imagine, if you will, making a room that when you look at it, will list who all is awake in the room, what puppets are in the room who's owners are awake, Who's sitting where, and what the obvious exits are. All done automatically.

See Also