[Amberdrake's picture of Dina goes here.]

Auction II

Dina's MPI Page

Hi, folks! A lot of what I've done isn't really directly relevant to the Auction II system but is neat or potentially useful anyway, so this is the page where I'm going to deposit little tidbits of MPI code that I find neat, or useful, or just want to post for the heck of it. The building page is related, for everything that's not MPI.

The only downside is that if you aren't an MPI coder, you many find some of these things a bit arcane. I'm not going to try to confuse anyone, but I'm assuming that the readers will have a basic knowledge of MPI and are willing to read the code to adapt it to their own needs.

I'm randomly adding a few coder tips here occasionally for other MPI programmers... It's not clear what would be most useful, though, or if anyone would care, so I'm just tossing in what happens to occur to me at the moment; I know I learned a lot by looking at other people's code. That's at the bottom under the miscellaneous header, and I plan to add some MPI links as I notice them.

A simple time readout
Some of the {ftime} codes prepend a space when you don't need one. This code will give the time with one and only one prepended space.

{if:{gt:{ftime:%l},9}, }{ftime:%l:%M}

A simple look-notify
Lots of people have asked how to implement look notifies. This will do it no matter what the muck's system does, by duplicating the look notifies. You put this MPI into your @desc, replacing MY_DBREF with your dbref (duh).

{null:{tell:[ {name:me}\, a {prop:sex,me} {prop:species,me}\, looks at you! ],#MY_DBREF}}

Temperature conversion
I really, really love the weather.muf program that other mucks have (and which Furry really should have), but the temperature output is in Farenheit. That's fine for Americans, but what of muckers who live in the rest of the world? So I wrote some MPI to do the conversion between Farenheit and Celsius; it's only approximate because of MPI's integer quirk but that's good enough for muck purposes. This is no big deal but it might save someone else from having to reinvent the wheel later.

F-to-C:   {div:{mult:5,{subt:{&FARENHEIT},32}},9}
C-to-F:   {add:{div:{mult:9,{&CELSIUS}},5},32}

If you like that, you should also check out Amberdrake's inches-to-feet converter.

See-through exits
One thing I like a lot is see-through exits; in other words, when you look at an exit it's nice to see who's in the room to which it leads. This is how you get that list of people:

{commas:{contents:{links:this},player}, and ,n,{name:{&n}}}

As you can see, it doesn't check for puppets, it doesn't check if they're awake, and it will return a blank string if the room is empty. The first two are okay, but the last is a real problem in your descs. So I do this:

{if:{eq:{contents:{links:this},player},},nobody,{commas:{contents:{links:this},player}, and ,n,{name:{&n}}}}

[Picture of Dina at the computer, by Dobbs @ FurryMuck] This returns 'nobody' if the room is empty, preserving the grammer of the desc. Obviously, what is said can be re-arranged in infinite patterns; for example, it's also good for making large open areas made up of different rooms -- you could see who's at the bottom of a cliff while you're at the top, for example. The exit and the destination room do have to be owned by the same player, however; otherwise it will fail with a Permission Denied error.

The same code is useful in room descriptions too, in making large open areas, balconies overlooking other rooms, and so on. For example, on a street players might be able to see who (if anyone) was in the blocks to the north and south of them.

Multi-purpose actions
I didn't put this in for a while, but then it occured to me that not everybody knew about it. It's pretty simple in execution but it takes a little mental leap before people realize it's possible, and even this guide isn't for new players; I've had to assume you're comfortable creating and editing actions. Lots of muckers have actions that do...whatever. This trick works for all of your actions that don't take you anywhere, i.e. the ones that are linked to $do_nothing/$null/$whatever-it's-called-on-your-muck. Puppet control actions, pie throwing actions, whatever.

Suppose we have two simple actions, sing and puppet. One emits the {&arg} in a 'singing' format and the other one controls a puppet. These are taking up two db objects and filling up your quota. Let's combine them into one.

Start by renaming the original two actions something else, like foo and bar; we'll be using the names sing and puppet, and having two identically named actions is just begging for trouble. Then create and outfit a third action called sing;puppet on yourself: [One of four pictures of Dina done by Millia in Aug '02.]

@action sing;puppet=me
@desc sing={list:desc.{&arg},this}
@succ sing={eval:func.{&cmd},this}
@link sing=$do_nothing

See what we've done here? When you type 'sing' it will run the MPI you've got in the func.sing: property, and so on for as many names as you feel like giving your action. Likewise, you can go in with lsedit and give the action as many different descriptions as you want. By now you get the idea, right?

Of course, you don't have to use {list} and {eval}. I use {lexec} in both and get pretty fancy; you should do whatever is most in keeping with your own style.

To finish up, transfer all of the stuff that made your original actions interesting and useful over to the appropriate places on the new action. If you have failure settings for any of the old actions, you may have to do some fancy coding on the new one; I'll have to leave that to you, since I can't know what you've done! It is possible to make the locks contingent on what name is used, though; MPI can always use {&cmd}. Once you have the new sing;puppet done and tested you can recycle the old ones. Your quota will thank you for it.

Randomly selecting a player in the room
I didn't have this on the page until the end of 2003 because I didn't see any need for it, even though I've been asked for MPI that will do this; however, I saw a
whole page devoted to that function, so I decided I might as well mention it -- and it's going here because the code logically preceeds the next thing, custom room contents. The first version is easy:

{name:{lrand:{contents:here,player}}}

This is simple but has the drawback (for some applications) that it can choose the player triggering the code; often that's okay, but there are times when you want a player to do something with a random other player. To get that, we ammend the code like this:

{name:{lrand:{lremove:{contents:here,player},*{name:me}}}}

And there you are; it returns a random player. Remember that this doesn't look for puppets, though -- if you want to do that, you can check out the page that started me writing this here, which has code that does see puppets. The prepared student should now be able to construct the code that sees puppets but not the triggering player, right? ;-)

Custom room contents
This is neat; rooms that are often crowded, like the West Corner of the Park, are really spammy to enter. Wouldn't it be nice to have all of that information but taking up less of the screen? Here's how.

This code is taken from the room contents of the Auction Hall. You'll probably want to do it a bit differently; I'm advising you use lsedit ('lsedit here=contents') to make a file so that you can edit and debug it easily. Sorry about the long line...

  Players: {commas:{lsort:{contents:here,player}},\, and ,p,{name:{&p}}{if:{awake:{&p}},,[asleep]}}
{if:{contents:here,thing},{nl}   Things: {commas:{lsort:{parse:t,{contents:here,thing},{name:{&t}}{if:{smatch:{flags:{&t}},*Z*}, [{name:{owner:{&t}}}'s puppet{if:{lt:{idle:{owner:{&t}}},0},\, asleep}]}}},\, and }}
    Exits: Foo, Bar, and Baz

You'll finish off the project by turning off the muck's default room contents listing; on FurryMuck this can be as simple as putting your own code in the _contents: property, and I recommend using that property to hold a quick {lexec} pointing to your custom list code ('@set here=_contents:{lexec:contents,this}'). Unfortunately, I can't tell you an absolutely sure way to turn off contents listing for your room, because that depends on your look code; type 'look #help' and see what's there.

The Things list would be simpler if it didn't identify puppets, but you've got the MPI so you might as well use it. The sleep awareness also adds a little computation to the system, but it's nice to know. Don't forget to put in the exits that really exist in your room!

People who get fancy with this can include objects that aren't really in the room, idle times for idlers, etc. I do this in the Auction Hall on FurryMuck to show who's an auctioneer. In August 2002 I patched the second line to not appear if there are no things in the room, since you don't need a listing of things that aren't there (I'm assuming puppets won't be alone in the room often enough to be worth coding a trap to accomodate them).

Improved versions:
This version does all of the above, but doesn't list Things if there aren't any in the room, and will show who's idle.

{nl}  Players: {commas:{lsort:{contents:here,player}},\, and ,p,{name:{&p}}{if:{awake:{&p}},{if:{gt:{idle:{&p}},180},[idle {stimestr:{idle:{&p}}}]},[asleep]}}
{if:{contents:here,thing},{nl}   Things: {commas:{lsort:{parse:t,{contents:here,thing},{name:{&t}}{if:{smatch:{flags:{&t}},*Z*}, [{name:{owner:{&t}}}'s puppet{if:{lt:{idle:{owner:{&t}}},0},\, asleep}]}}},\, and }}
{nl}    Exits: Foo, Bar, and Baz

Is even that not enough for you? Here's a version of the Players: line that doesn't list you in it. After all, you already know that you're in the room. Also, if you're alone in the room it doesn't display the line at all. Credit for these upgrades goes to Threnody at le Jardin Mystique who suggested them (24 Aug 2002). It will still have the empty line quirk if a puppet is alone in the room.

{if:{ne:{contents:here,player},*{name:me}},Players: {commas:{lremove:{lsort:{contents:here,player}},*{name:me}},\, and ,p,{name:{&p}}{if:{awake:{&p}},{if:{gt:{idle:{&p}},180},[idle {stimestr:{idle:{&p}}}]},[asleep]}}}

Here's what I did for the Truth or Dare pools on FurryMuck (the TLC pools, in August of 2003; the Palace of Dragons was coded long, long before!) Note the nonexistant object listed on the Things: line. If the room is always going to have at least one real thing in it, such as the bulletin board that's in the Auction Hall, you don't need the {if} that checks for contents -- but the output will look funny if you omit it and then don't have at least one real thing. It's yet another really long single line, but it evaluates and formats properly this way even with {lexec}.

{if:{ne:{contents:here,player},*{name:me}},  Players: {commas:{lremove:{lsort:{contents:here,player}},*{name:me}},\, and ,p,{name:{&p}}{if:{awake:{&p}},{if:{gt:{idle:{&p}},180},[idle {stimestr:{idle:{&p}}}]},[asleep]}}{nl}}   Things: {if:{contents:here,thing},{commas:{lsort:{mklist:{parse:t,{contents:here,thing},{name:{&t}}{if:{smatch:{flags:{&t}},*Z*}, [{name:{owner:{&t}}}'s puppet{if:{lt:{idle:{owner:{&t}}},0},\, asleep}]}},Sign: type 'TDhelp' for help}},\, and },Sign: type 'TDhelp' for help}
{nl}    Exits: Out
[One of four pictures of Dina done by Millia in Aug '02.]
The Aquarium
This whole thing got started over on
Le Jardin Mystique when another player left a huge fish tank in the public square. It occured to me that I should be able to make a better object than that... At first glance it's a little ascii picture of an aquarium and no big deal; only when you look at it a second or third time does the magic appear. You see, the fish actually swim around within the aquarium! Frankly, I think this is neat.

It's not a very big piece of code but it's cute and does a few interesting things, so I've included the whole thing here; you can copy this & paste it to your muck so you have your own aquarium to look at while you read my comments. I'll talk about what I did below.

@create Aquarium=10
@set aquarium=/_/de:{null:{lexec:desc,this}}
@set aquarium=/_/dr:You gently set down the aquarium.
@set aquarium=/_/odr:gently sets down the aquarium.
@set aquarium=/_/osc:carefully picks up the aquarium.
@set aquarium=/_/sc:You carefully pick up the aquarium.
@set aquarium=/desc#:12
@set aquarium=/desc#/1:{store:{add:{prop:looks/{name:me},this},1},looks/{name:me},this}
@set aquarium=/desc#/2:{tell:+~~~~~~~~~~~~~~~~~~~~~~~~~+,me}
@set aquarium=/desc#/3:{with:stock,1\r2\r3\r4,
@set aquarium=/desc#/4:{for:a,1,4,1,
@set aquarium=/desc#/5:{with:fishnum,{lrand:{&stock}},
@set aquarium=/desc#/6:  {set:stock,{lremove:{&stock},{&fishnum}}}
@set aquarium=/desc#/7:  {with:n,{dice:21},
@set aquarium=/desc#/8:  {with:fish,{prop:fish{&fishnum}{if:{subt:{dice:2},1},a,b},this},
@set aquarium=/desc#/9:    {tell:|{left: ,{&n}}{&fish}{left: ,{subt:25,{add:{&n},{strlen:{&fish}}}}}|,me}
@set aquarium=/desc#/10:  }}
@set aquarium=/desc#/11:}}}
@set aquarium=/desc#/12:{tell:|./\\.\,\,\,.\,(o).$.\,.\,&.[].a\,|{nl}{right:[{prop:looks/{name:me},this}],27,=}{nl}##### Dina's  Aquarium #####,me}}
@set aquarium=/fish1a:>o
@set aquarium=/fish1b:o<
@set aquarium=/fish2a:><>
@set aquarium=/fish2b:<><
@set aquarium=/fish3a:,o
@set aquarium=/fish3b:`o
@set aquarium=/fish4a:}=@
@set aquarium=/fish4b:@={
Okay, so we've made an aquarium. The drop and succ messages are just flavor text. The action is in the desc# proplist, starting at line two (I'll get back to what's up in line one later).

The basic scheme is pretty simple. The aquarium is four lines vertically and there are four 'fish'; one fish will be placed on each line. The stock variable starts off with the numbers one through four and one of those entries is randomly withdrawn for every line so there will be no duplicates, as there would be if the selection was simply a random drawing from a list (this happens in lines 5 & 6). It's a bit more complex than that, though, since each fish has two versions, usually swimming left or right; that's what's going on in line 8. Line 9, obviously, shows the line to the triggering player.

I said I'd mention line one. That reads a property, increments the number, and stores the value back in that same property. Then later on that number gets read again, in line 12. As most of you have figured out by now, this is a look counter; the code in line 12 also justifies the number. There's no need to have a look counter on an aquarium...but what the heck.

Custom odors
You can set yourself up to have custom odors for different people. It turns out that on FurryMuck your _scent property will be checked for MPI, so, gee, you can go wild... This is how I did it:

@set me=_scent:{name:this} {if:{prop:_scents/{name:me},this},{prop:_scents/{name:me},this},smells of fresh clean skin and green pine trees; a hint of oiled metal is on her\, and the odor of libraries and leather...}

[Dina doing guerilla debugging, by Joseph Bonis AKA Harlin @ FurryMuck] As you can see, when Foo smells me the MPI checks for a property _scents/Foo: and if it's found, that's what Foo smells. Don't forget to backslash your commas or MPI errors will plague your smellers, who will only be confused. This could include a smell notification too, but the _smell_notify: property works well enough for me.

This works on FurryMuck (which uses Smell v2.01); there are no guarantees if you try this on a muck using different code. If the local smell program doesn't use the _scent: property you must find out where it keeps that information on your player object and try that; I've seen _prefs/scent: used, too. (Also remember that _room_scent: may store a different version of your odor.) If the program doesn't process MPI, you're out of luck; I understand that the common smell.muf can apparently be toggled to process or ignore MPI.

Miscellaneous MPI notes

Fun with {if} We all use {if}, but how many of us have really gotten to know it? In simplest form it's a Boolean check: 1 == true and 0 == false. But there are a few implications that you might not be familiar with yet.

Did you know that the true argument may be empty? We often leave the out the false argument but it can be done the other way around, too. Sometimes you just don't feel like reversing the logic of your test argument and {not} is too much work; it's much more elegant to do this: {if:{&foo},,{DO-STUFF}} This is shorter and logically the same as {if:{not:{&foo}},{DO-STUFF}}; cool, eh?

The {if} statement doesn't check for truth. Really. It looks for falsehood. What this means for you is that anything that isn't zero (either '0' or a null string) evaluates as true. So even something silly like {if:Harry Potter fan,yes,no} returns yes. This is useful in a number of ways, such as detecting the presence of properties on objects. You can cut down the lines like {if:{ne:{prop:species,me},},yes,no} to {if:{prop:species,me},yes,no} (and did you notice my use of the null string there in the first example?).

A {mklist} quirk Did you know that {mklist} doesn't like being passed strings? It turns out that typing @mpi {mklist:yes,no} works as you'd expect but @mpi {with:f,yes\,no,{mklist:{&f}}} does not. Go figure! So what can you do about it? Instead of {mklist:{&foo}} you can use {subst:{&foo},\,,\r} to do the same thing a little less elegantly. Revar gives the slightly clunkier {eval:\{mklist:{&foo}\}} alternative, where you must put in the backslashes to get eval and mklist to do what you want but don't have to remember nonsense like ,\,,\r to make it work.

The cause of this behavior is that MPI doesn't look at strings unless told to; I discovered it trying to do {mklist:{prop:{&foo}}} and failing. Now, mklist wants a list of values to make a list out of; one variable is seen as one entry, no matter how many commas it has in it. (You get away with Revar's approach above because you pass it the mklist string and the value string as one chunk of text and make MPI see that as something to be evaluated; if you omit the backslashes it tries to look at mklist before eval, mklist does nothing, and therefore so does eval.)

Inches to Feet by Amberdrake! He's made this a _msgmacs function (see here), which is very efficient; you might do it another way, but I can't offer an improvement. It looks long, but that's so that the returned result looks good for human reading; five feet is 5' not 5'0", for example.

i2f:{if:{ne:{div:{eval:{:1}},12},0},{if:{ne:{div:{eval:{:1}},12},1},{div:{eval:{:1}},12}',1'}}{if:{ne:{mod:{eval:{:1}},12},0},{if:{ne:{mod:{eval:{:1}},12},1},{mod:{eval:{:1}},12}",1"}}

Do you have a favorite MPI trick? Let me know!

Puppets are {awake} It's not in the documentation, but {awake} will treat a puppet as if it were the player who owns it.

Players and {istype} Here's another one that isn't in the documentation but really should be. The istype command is for returning true or false if the object is of the given type, and {istype:*Dina,player} returns true (on FurryMuck). But what if you forget the asterisk? What about {istype:Dina,player}? That's where it gets nifty, friends; that will return true if the player of that name is present! If the person has left, it returns false. It's a great way for your more complex MPI programs to see if someone they've recorded is in fact still around.

Links to other MPI pages

MPI for the Technically Impaired by Ginger; a decent introduction that still shows some of MPI's power

Muck Manual: 3.1 Overview: MPI at the MINK site

It's a bouncing bunny bar, what do you think it is?

Me: If you really insist, there's nothing to stop you from sending email to me at [email protected], but I don't check it all that often. If you're in a hurry, page-mail me on FurryMuck, okay? You can also go back to the main page or over to the building page from here.

The page was last updated 3 Dec 2003.

Hosted by www.Geocities.ws