Jason@Dragoness Home | MUDL documentation | MUDL tutorials
It appears that the first thing a god on Sloth MUD does when he gets access to MUDL is to make a piece of equipment that gives him various nifty powers he didn't have before. I managed to resist the temptation to do this for a while, but eventually I ran into something annoying: I couldn't walk through doors. I would be wandering around an area looking for places to put clues for a puzzle, and I couldn't get into certain rooms because they were behind locked doors. Of course, there are ways around this, but they are a bit tedious to do over and over. So I decided to make a cloak that would let me walk through closed/locked doors.
First I create the cloak object. Let's take that as read, and say I have it in my hands and it's called "cloak". I now need to attach a MUDL procedure to the cloak. I do that with
addproc cloak mudland Sloth responds
Setting procnum on index:0 Setting enabled=0 flag on new index. Proc Added Successfully. You must configure the proc and enable it.I will start out with a simpler goal: make the cloak let me walk north through closed doors. I thus need to add a MUDL function to the cloak that is triggered when someone walks north. I can do that easily enough:
setproc cloak 0 on_north ] msg_character( %a, 'You said to walk north.' ), ] return(true) ] @That's all that is needed to enter the code. Let me point out four things here:
setproc cloak 0 proc_enabled 1which tells the MUD that I am ready to compile this MUDL. For those of you who don't know setproc, this means "On the cloak's zeroth proc, set the KVP called 'proc_enabled' to the value '1'". If you are even more baffled by that statement, you should read the tutorial introductory warning.
I only ever have to issue this command once per object; I won't need to do it again for the remainder of the work on this cloak. (A tip: if you have a proc that is going dangerously wrong, a quick way to disable it is to set the proc_enabled to 0. A newly created monster or item won't compile its MUDL for a few seconds, which is your window to disable it before it kills you.) Finally, I must tell the MUD
compileto which it should respond
Attempted to compile 1 MUDL functions (0 on mobs and 1 on objects)If there are compile problems, the MUD would have told me at this point, but there were none, so let us continue. Now, I try out my new MUDL cloak by saying "north":
<999rm 41wz 0sv> north You said to walk north. <999rm 41wz 0sv>It indeed did something.
If you don't know Sloth MUD, those things with the 999's are my prompt. The first thing in my prompt is the number of the room I'm in, in this case 999, which as it happens has no exit to the north. Notice that not only did I not go anywhere, I also wasn't told that the room has no exit to the north! The only thing that happened was that my script sent me the message, like it was supposed to. If I didn't have the cloak, I would have seen
<999rm 41wz 0sv> north Alas, you cannot go that way... <999rm 41wz 0sv>So, my first function works: it prevents me from trying to go north and gives me a silly message. The first thing to fix is that I only want this cloak to function when someone is wearing it, and only on the wearer. I can use the location function to check that it's worn, and the bearer function to check that the person doing the movement, %a, is the person wearing the cloak. Like this:
setproc cloak 0 on_north ] if ( location( %o ) = 'equipment' AND bearer( %o ) = %a, ( ] msg_character( %a, 'You said to walk north.' ), ] return(true) ] ),( ] return(false) ] )) ] @Those %o's are references to the object that has the MUDL script, that is the cloak. Then I need to compile again, put on the cloak, and I'm ready to go, or rather not go. I have used the if statement here, which evaluates its first argument,
location( %o ) = 'equipment' AND bearer( %o ) = %athen executes its second argument if the first argument evaluated to true, or executes its third argument if the first argument evaluated to false. If that isn't clear you might want to read a more detailed explanation in the function reference for if. The first argument is true if and only if %o, the cloak, is located in 'equipment' (meaning it's being worn) and if its bearer is %a, that is, the person who said 'north'. If it's true, the MUDL does what it did earlier (silly message, no north). If it isn't true, it executes instead the code after the ),( and returns false. This tells the MUD to go ahead and handle the 'north' command itself.
Now I just need to make the cloak do what it's supposed to do. I need to check if there's an exit to the north, and if there is teleport myself there. This uses three new functions: exit_to (which tells what's out a certain exit from a room), room (which tells what room a person is in right now), and teleport (which teleports people to other rooms). Here's the script; from now on I'll leave off the ] at the beginning of each line; even though you can see those when you type in the script inside the MUD, they make it hard to cut and paste scripts from these pages into the game:
setproc cloak 0 on_north if ( location( %o ) = 'equipment' AND bearer( %o ) = %a, ( if ( exit_to( room( %a ), 'north' ), ( teleport( %a, exit_to( room( %a ), 'north' ) ), msg_character( %a, 'Ok.' ) ),( msg_character( %a, 'There~`s no exit in that direction.' ) )), return(true) ),( return(false) )) @
Notice the funky ~` in the string; since ' is used to delineate the end of the string, ~` is how I make a ' appear in the text output. I tried this cloak out, and while technically it works it isn't pretty. It does teleport me through doors, but it's missing two things: it doesn't show me the room when I arrive, and it doesn't tell people that I left and arrived. My first thought was that not telling people I had left was cool - it was like a perfectly effective 'sneak' skill - but I decided it would be cooler to have people see me phasing through doors.
So, first I will use the cmd function to force me to 'look' when I arrive in the new room:
setproc cloak 0 on_north if ( location( %o ) = 'equipment' AND bearer( %o ) = %a, ( if ( exit_to( room( %a ), 'north' ), ( teleport( %a, exit_to( room( %a ), 'north' ) ), cmd( %a, 'look' ) msg_character( %a, 'Ok.' ) ),( msg_character( %a, 'There~`s no exit in that direction.' ) )), return(true) ),( return(false) )) @and, WHOOPS! I get this:
<999rm 41wz 0sv> compile a traveling cloak intones 'MUDL proc 0 (north), line 5: syntax error' Attempted to compile 1 MUDL functions (0 on mobs and 1 on objects). <999rm 41wz 0sv>I look at line 5: counting from 1, that's the first msg_character line, which I didn't even touch, so why is that giving me an error? Ah, I forgot to put a , after the new line. Even after a year of MUDLing, that is the most common error I make in MUDL scripts. Any time you see "syntax error", look for a missing comma. I fix it and compile, and this time it's happy.
Now to add the messages. I need a message to the room I left before the teleport and a message to the room I arrived at after the teleport. While there is a lovely msg_room function to send messages to a room, what I really want to do is send one message to the people who aren't me and another message to me. For that, I need the msg_everyone_else function.
To make things even more complicated and wonderful, I would like to distinguish between the case where I just walk north, and the case where I actually pass through a closed door. Putting this sort of detail in a MUDL script shows that you have style. So, I need to be able to tell if there is a closed door in the way or not; I can check this by using the flag function. Here's the code that does all of that:
setproc cloak 0 on_north if ( location( %o ) = 'equipment' AND bearer( %o ) = %a, ( if ( exit_to( room( %a ), 'north' ), ( if ( flag( room( %a ), 'north', 'closed' ), ( msg_everyone_else( %a, '$n phases out of the room through the northern wall.' ), teleport( %a, exit_to( room( %a ), 'north' ) ), msg_everyone_else( %a, '$n phases into the room through the southern wall.' ) ),( msg_everyone_else( %a, '$n leaves north.' ), teleport( %a, exit_to( room( %a ), 'north' ) ), msg_everyone_else( %a, '$n arrives from the south.' ) )), cmd( %a, 'look' ), msg_character( %a, 'Ok.' ) ),( msg_character( %a, 'There~`s no exit in that direction.' ) )), return(true) ),( return(false) )) @
You might have noticed some unfamiliar $n symbols inside the messages. These are shorthand for the name of the first character argument, so instead of "$n leaves north." the people in the room will actually see "Kjartan leaves north." You will find a complete list of these symbols and their meanings is on the msg_everyone_else page. You can spot these special symbols because all of them start with $.
Now the magic cloak works great ... as long as I only ever want to go north. I could easily make five more of the functions for all six directions, but that seems inelegant. One general principle of programming that applies to all programming languages is that you should not have multiple copies of almost-identical code.
Instead, I will create a user function that does the meat of the teleportation, and call it from six places: on_north, on_east, etc. The most complicated part of user functions is what to call them. The name of a user function includes its return type (in this case it will be boolean) and the types of its arguments. In this case there is one argument, the direction that the character tried to move, which is a string. The function will then be called fn_b_trymove_s. The _b_ means that the return type is a boolean (that is, true or false) and the _s means that it has one argument which is a string. If it had three arguments, a string and two characters, the _s would have been replaced with _scc. Here's the command to create my function:
setproc cloak 0 fn_b_trymove_s if ( location( %o ) = 'equipment' AND bearer( %o ) = %a, ( if ( exit_to( room( %a ), %1 ), ( if ( flag( room( %a ), %1, 'closed' ), ( msg_everyone_else( %a, '$n phases out of the room through the '+%1+' wall.' ), teleport( %a, exit_to( room( %a ), %1 ) ), msg_everyone_else( %a, '$n phases into the room through the '+opposite(%1)+' wall.' ) ),( msg_everyone_else( %a, '$n leaves '+%1+'.' ), teleport( %a, exit_to( room( %a ), %1 ) ), msg_everyone_else( %a, '$n arrives from the '+opposite(%1)+'.' ) )), cmd( %a, 'look' ), msg_character( %a, 'Ok.' ) ),( msg_character( %a, 'There~`s no exit in that direction.' ) )), return(true) ),( return(false) )) @This code no doubt raises many questions. First, what is that %1 that appears all over the place? It's the first argument to the function, the string. In this case it will be 'north'; notice how all of the occurrences of 'north' have been replaced with %1. Second, what is that opposite function? It's a function that returns 'south' for 'north', 'up' for 'down', and so on, and unfortunately there is no such function. I will have to make one, and I'll get to that in a second. Finally, the + that appears in the strings is the operation of string concatenation with no added whitespace. Thus 'hi' + 'there' becomes 'hithere'.
Here's the code for building the opposite function, which will introduce us to the switch statement:
setproc cloak 0 fn_s_opposite_s switch(%1, 'north',return('south'), 'south',return('north'), 'east',return('west'), 'west',return('east'), 'up',return('down'), 'down',return('up') ), return('**bad value**') @
The switch is a much prettier shorthand for a sequence of if statements. What this one means is: "if %1 is 'north' then return 'south', otherwise if %1 is 'south' then return 'north', otherwise etc." The first argument of the switch (in this case %1) is what is being compared with stuff. The second, fourth, sixth, and subsequent even-numbered arguments are what the first argument is being compared with. The third, fifth, seventh, and subsequent odd-numbered arguments tell MUDL what to do if the corresponding even-numbered argument matches.
Finally I need to add MUDL code to intercept all six of the commands move in various directions and to call the new trymove function:
setproc cloak 0 on_north return(trymove('north')) setproc cloak 0 on_south return(trymove('south')) setproc cloak 0 on_east return(trymove('east')) setproc cloak 0 on_west return(trymove('west')) setproc cloak 0 on_up return(trymove('up')) setproc cloak 0 on_down return(trymove('down'))There's really nothing new here except for the calls to our homemade trymove function, and those should be pretty much what you expected them to be. And now, you have a fully functional cloak that lets you walk through walls.