Dirty Tricks ============ This document is an effort to describe some useful techniques for general purpose building. It's a series of very clever or at least cute ideas thought up mostly by me or Moira. The really smart ideas may be freely attributed to the latter party, though I like to think I've had an idea or two worthy of merit. This document is written largely from the perpective of a user of v1.11. v1.12 allows more powerful contructs, but should not break any of what follows. First off, a word about permissions. Macros are not run as the player, they are run as the object that the macro is attached to. Therefore, you never have to worry about a macro somewhere changing your description for you; it can't, unless it happens to own you (or it has a wizard bit). This also means that anything the macro does that requires a 'set' or 'teleport' command will require that the object the macro is on own whatever it is it's trying to fiddle with. If you have an exit that has a macro as its fail that changes the room's description, that exit must own the room. If you have a room that has a macro that teleports an object in, that room must either own the object, or the object must be linkok. (See the teleport rules). Macros with Arguments ===================== Generally, when you want a 'kick dog' command, you should use an exit with that name. This also allows several aliases for the command, and so on. For instance, either: ]str fail=You kick the dog savagely. ]str ofail=kicks the dog savagely. or: ]cmd fail=@do kicks the dog savagely. on a locked exit named "kick dog" would work. At times, this is not quite practical, as in the case of a system macro or macros tied to objects intended to be used. UnterMud does not, at present, support exits attached to things, nor exit-style command matching on anything other than exits. Thus, it is sometimes necessary to resort to the following trick: ]cmd kick=X${1}z ]cmd Xdogz=@do kicks the dog savagely. When the user types 'kick <something>', the macro 'kick' is called with a single argument, '<something>'. This macro runs through the variable substitutor, and expands to X<something>z. In the case of 'kick dog', this is Xdogz. The Xdogz macro gets called, and the indicated @do command executed. In the case of 'kick lizard', well, the command 'Xlizardz' results, and a Huh? message gets displayed to the user. This cannot really be helped. Coding Strings on the Object ============================ This is a really simple idea, and really really useful. The problem here is that often a long complex macro has a bunch of strings hardcoded in it, and in the absence of good building tools, typos can be hard to correct. Further, the 512 character limit on commands sent to the server may make it hard to fit it all in. It is handy, then, to let the variable substitutor do some of the work for us. ]cmd summon=@tel <object ID> here;@_echo $me.summonstr ]str summonstr=The pot appears in a flash of light! The 'summon' macro teleports the specified object (a pot, in this case) to the user's location, and then _echo's the string found in the 'summonstr' attribute of whatever the above attributes are hooked to. The variable substitutor graciously converts '$me.summonstr' into 'The pot appears in a flash of light!' for us. Coding Booleans on the Object ============================= The easiest way to keep track of 'state' in UnterMud is by directly saving it on some convenient object (the object that needs to know what state it is in, generally). Again, the variable substitutor gets a workout. Imagine the following attached to a room which may or may not be, say, partially filled with water. ]cmd splash=@_if $me.wet "@do splashes around." else "@_echo 'splash?'" ]cmd drain=@_echo "You let the water out";@set me str wet F ]cmd fill=@_echo "You fill the room with water";@set me str wet T ]str wet=T The two macros 'drain' and 'fill' change the state of the room. In a more sophisticated example, they could re-write the room's description and so forth. Here, they just change the string coded in the 'wet' attribute, and echo something to the player. The 'splash' macro uses the variable substitutor to see if the room has water in it or not, and either executes a suitable @do, or asks the player what it thinks it's doing splashing in a dry room. Whatever these attributes are attached to needs to own itself, to be able to set the 'wet' attribute back and forth. Another way to do state changes that isn't quite as pretty to look at, is to let the macro rewrite itself to the opposite state every time it is run. For the above example with the water in the room, you could do: ]cmd splash=@do splashes around. ]cmd drain=@_echo "You let the water out";@set $me cmd splash "@_echo 'splash?'" ]cmd fill=@_echo "You fill the room with water";@set $me cmd splash "@do splashes around." This way also requires that the room (or whatever) owns itself, so it can set the 'splash' macro on itself. Re-writing Macros ================= Sometimes you want to run some text through the tokenizer/macro substitutor a couple of times. For example, you might want a 'dance' macro that references an attribute of the object it is attached to based on the name of the player calling the macro. Ideally, you'd just do $me.${actor.nam}dance to reference, say, a 'bobdance' attribute when bob calls the 'dance' macro. Alas, this will not quite work, since it requires two passes through the variable substitutor, which Unter does not provide. Thus: ]cmd dance=@set $me cmd Xdnc "@do dances: $$me.${actor.nam}dance";Xdnc ]str bobdance=bob hops around. If 'bob' calls the 'dance' macro, it will first set a new macro on the object it's attached to reading: ]cmd Xdnc=@do dances: $me.bobdance and then call said macro, with the obvious results. Alas, this will break for people with multiple word names: Jerry Cornelius would have, instead: ]cmd Xdnc=@do dances: $me.Jerry Corneliusdance which would fail unpleasantly. A better solution is: ]cmd dance=@set $me cmd Xdnc "@do dances: $${me.${actor.nam}dance}";Xdnc which would produce Xdnc macros: ]cmd Xdnc=@do dances: ${me.bobdance} ]cmd Xdnc=@do dances: ${me.Jerry Corneliusdance} which ought to work correctly. The latter will reference an attribute ]str Jerry Corneliusdance=<stuff> which may or may not be legal OIF, but works. Again, the object in question needs to own itself in order to write these macros on itself. Choosing Exits Based on Locks ============================= One of the significant ways UnterMud differs from the TinyMud derivatives is that it does NOT choose unlocked exits in preference to locked ones, when selecting randomly between exits with the same name. Luckily, _if provides a way around this. Indeed, there is a school of thought which maintains that the Unter method is cleaner and easier to maintain, but we shan't get into that here. To implement a pair of identical exits named, say, 'w;we;wes;west' which take you either to room A or room B depending on whether or not you are carrying some key, you make only *one* exit named 'w;we;wes;west', and lock it completely. Make two other, unlocked, exits with names that are hard (impossible) to guess, say XXXwestone and XXXwesttwo that go to the two possible destinations. Now set the fail of the 'w;we;wes;west' exit to be something like this: ]cmd fail=_if <key object ID> "@go XXXwestone" else "@go XXXwesttwo" The '@go's are actually superfluous. This takes advantage of the fact that any 'go' operation is applied to the player, so regardless of what this fail is attached to the player winds up taking the exit specified. In this case, if the player is carrying the key, it winds up walking through XXXwestone, otherwise through XXXwesttwo. A similar technique, using _rand instead of _if, can be used to build a set of equi-probable exits (the usual arrangement on UnterMud is to have the first exit taken 1/2 the time, the 2nd 1/4, the 3rd 1/8 and so on). Action at a Distance ==================== The basic technique for getting things done in UnterMud is to re-write objects on the fly. There is no particular reason to restrict this to objects nearby -- macros in 'this' room can change state anywhere in the MUD. For example, a working drawbridge, controlled from a control room, could be built by making an exit like this: ]str nam=flip switch;switch;flip ]boo lok=F ]cmd fail=_if $me.bridgeup XXXlower else XXXraise ]cmd XXXlower=@_echo $me.lowertext;@set $me str bridgeup F;@set <roomID> desc $me.downdesc;@set <exitID> lock T ]cmd XXXraise=@_echo $me.raisetext;@set $me str bridgeup T;@set <roomID> desc $me.updesc;@set <exitID> lock F ]str lowertext=You hear the drawbridge creak down. ]str raisetext=Creak! Bridge goes up. ]str updesc=The bridge is up. ]str downdesc=The bridge is down. ]str bridgeup=F This exit does a lot of stuff. Depending on the state of the Boolean coded in the 'bridgeup' attribute, it either raises or lowers the drawbridge. This consists of re-writing the description of the room to indicate the state of the bridge, echoing text to tell what the bridge is doing, locking or unlocking an exit (you can only get through the exit if the bridge is down) and finally re-writing the 'bridgeup' attribute to the other state. Note that this exit must own itself, the room and the exit it needs to lock/unlock -- luckily if you forget, Unter will give you many fine error messages :-) Have fun. bob & Moira