LambdaMOO Programmer's Tutorial

version 1.6

For MOO Canada
September 1997
by Neil Fraser (aka Odo)

This is a tutorial for players who have achieved Programmer status on a MOO, but don't know how to program. It will be necessary to print out a copy of this document (5 pages) so that you can refer to it while on the MOO.

Before starting this tutorial (and programming) you must have mastered the builder commands. Make certain you have successfully used ALL of the following commands:

  ? (The most useful command; use it before pestering someone.)
  @create, @recycle, @rename, @describe
  @lock, @addalias, @messages
  @mail, @send, @rmm, @notedit
  @audit, @dump, @d
  @kids, @parents, @locations, @contents

If you are not familiar with all of those commands you will waste a lot of time programming features that are already available through the command line.

The first task in this tutorial is to use the familiar builder commands to make a new object called Odo's mouse (substitute your own player's name), with aliases mouse, and pet. Give it a suitable description, and configure the mouse so that whenever somebody other than you tries to pick it up they fail, and are told Odo's mouse bites you, and you drop the rodent. while everyone else in the room is told Max tries to catch Odo's mouse, but gets bitten. If you can't do all of that unaided, you are not ready to program.

Now create a new verb on your mouse that will make it squeak when the mouse is pinched. Type: @verb mouse:pinch this none none You now have the verb, but there is no code to execute. Type: @edit mouse:pinch You are now in the verb editor, we will worry about how it works later, right now let's get some code writen. Type the following commands (including the initial quote):
  "this.location:announce_all("Odo's mouse squeaks!");
  "this.location:announce_all("Odo's mouse gnashes its teeth.");

The verb now has two lines of code. Use the compile command to save the code. If there was an error while it was compiling, type del 1 or del 2, retype the offending line, and then compile. If you somehow get into serious trouble, type abort and start over again. When it has successfuly compiled, quit back to the command line, and test the verb by typing: pinch mouse

Now for some theory. Type @list mouse:pinch to see the code for your verb. The word this in a verb is a built-in variable that returns the object that the verb is running on. In your verb, this always returns the object number of your mouse. All objects have a location property that stores the object number of the room, player, or container that they are sitting inside of. Putting these two together this.location returns the mouse's containing object. All rooms have a verb announce_all that tells all objects in the room the text that you give it (@list here:announce_all to see this verb's code). If the mouse is sitting in a room, then the room's announce_all verb is called. If the mouse is being carried by a player, then the player's announce_all verb is called (which passes it to the room).

The verb editor is not very user-friendly, so it deserves some explanations. Type @edit mouse:pinch to get back in and list to show your two lines of code. You can use p and n (previous, next) to move the insertion point (cursor) up and down. Position this point between your two existing lines of code and use " (say) to add suspend (15); as a line of code. Remember, use ? if you run into dificulties, and look while in the editor gives you an overview of the editing commands. Once you have got the line inserted, list the code and make sure that it is between the other two. If it isn't, use move to correct the problem. Now change the 15 to be 2 with substitute. Type s /15/2 and list the code to verify the change. The substitute command will be the most used command because it allows you to fix bugs without deleting the line and restarting. Compile the code (and fix any errors), then quit from the editor.

Pinch your mouse again and note the two second delay between the two messages. Take a guess as to what suspend (60); would have done. There is still a problem with the verb; when someone pinches your mouse, other people hear the mouse's reaction, but they have no idea what caused it. Edit the verb again and insert the following two lines before all the others, then compile (fix errors) and quit.
  player:tell("You pinch Odo's mouse.");
  this.location:announce(player.name + " pinches Odo's mouse.");

There are four new items in these lines. You already know that this in a verb returns the object that the verb is running on. The built-in variable player is simmilar, it always returns the object number of the player who activated the verb. On every player is a verb tell that will tell the text to only that player. The exact opposite is a room's announce verb that tells the text to everyone in that room, except the player who activated the verb. You know what the location property returns, so given the context you should have figured out that the name property returns.

Find somebody else who is logged on to the MOO and test your mouse with them. Now make a rat with exactly the same attributes as your mouse. There are three methods.

  1. Create another $thing and program it from scratch.
  2. Create another $thing and @copy mouse:squeak to rat
  3. Create a child of your mouse.
The first suggestion is stupid if you know about @copy. The second isn't bad, but it wastes disk space and is no good when copying objects with dozens of verbs. The third is really simple:
  @create #1234 called "Odo's rat", "rat", "pet"
where #1234 is the object number of your mouse, and Odo is replaced with your character's name. Note that when you @dump your mouse you see the pinch verb, but not when you dump the rat. @list rat:pinch will show you the verb, but after telling you that it was found on the mouse. This is called inheritence.

There is a final problem with the pinch verb, when you pinch the rat, one is informed that the mouse was pinched. This is because the name is hard-coded into the text. Edit the mouse's pinch verb, and on all four applicable lines use substitute to change Odo's mouse to this.name with the apropriate quotes and pluses. While you are editing the verb, replace the expression player.name with player:title(). It is customary to use :title() instead of .name when requesting the name of a player. This is so that if the player wants to add Mr/Mrs/Cptn/Dr in front of the name (or otherwise change how their name is seen) they may do so without modifying the name property (which, for players, isn't allowed to have spaces in it). After all the editing, your verb should look like this:

  player:tell("You pinch " + this.name + ".");
  this.location:announce(player:title() + " pinches " + this.name + ".");
  this.location:announce_all(this.name + " squeaks!");
  suspend(2);
  this.location:announce_all(this.name + " gnashes its teeth.");
One of the most interesting types of verb is the one that listens to conversation in a room, and reacts accordingly. This is easilly done. Whenever anything is said, the room calls the notify verb on every object in the room, if that verb exists. Type @verb mouse:notify this none this then edit the new verb so that is contains the following (try using enter instead of "):
  if (index(args[1], " says, "))
    if (index(args[1], "cheese"))
      this.location:announce_all(this.name + " looks up at the mention of its favourite food.");
    endif
  endif

The text of what was said is returned by the args[1] variable. The function call index(args[1], "cheese") returns true if the word cheese was found in the text of args[1]. The first if is checking to see whether the notify verb was called by somebody saying something (Odo says, "I like cheese.") as opposed to an emote (Odo likes cheese.) or background noise (Odo drops the cheese.). In the event that both ifs are passed, someone has said the word cheese (or is trying to trick the verb).

Test the verb; remember that objects in your pocket can't hear what is said outside -- you must drop your rodents first. Find somebody else who is logged on and have them test it. You should notice a bug. Occasionally when somebody else says cheese you get the rodent's response BEFORE you hear the triggering text. This 'time travel' effect occurs because the room calls the notifys on all the objects one by one. If it gets to the rodent before it gets to you, then the rodent responds before you hear the trigger. Were you to insert suspend(1); in the mouse's notify verb, the whole proccess would be suspended for one second, but the order of messages would be unchanged. What you need to do is insert the line fork (1) before, and endfork after, the announce_all line. This will cause the normal execution to jump over the announce_all line and continue uninterrupted, and then one second later the announce_all line will be executed. Crude, but it works.

Insert the following lines between the endfork and the first endif:
  elseif (index(args[1], "cat") || index(args[1], "hawk"))
    fork (1)
      this.location:announce_all(this.name + " looks around nervously.");
    endfork

The || symbol means or. Other symbols you should know about are:

Make your own improvements to the verb so that it will catch other words. Try making the rodent say Hello when somebody says hello or hi (the index() function is case-insensitive). Or make your rodent cough or sneeze when somebody says something with the letter q in it, or tion.

When you are ready to continue, add these lines before the very last endif.
  elseif (index(args[1], " squeaks!"))
    fork (1)
      player:tell(this.name + " glares at you.");
      this.location:announce(this.name + " glares at " + player:title() + ".");
    endfork

Because of the location of these lines they can never be trigered by spoken text. Try pinching the mouse. The mouse will squeak and will thus trigger the glare reaction. It will also trigger the glare reaction from all other sympathetic rodents in the room.

The second last verb I am giving you is created with @verb me:whistle none none none and has the following code:
  player:tell("You whistle a note so high that it is inaudibe.");
  player.location:announce("You hear " + player:title() + " whistle an inaudibly high note.");
  rodent_list = {#1234, #2345};
-- substitute the #s of your rodents.
  for rodent in (rodent_list)
    move(rodent, player.location);
    rodent.location:announce_all(rodent.name + " scampers in.");
  endfor

Since this code is located on you, and you would normally be the one executing the code, player and this are interchangable. There are three new items in this verb. The simplest is the function move(what, where). You may use this function to move objects that you own (including yourself), to locations that aren't locked. The next new item is the list. Lists are equivalent to the 'arrays' found in other programming languages. Lists can store groups of strings (text), numbers, object numbers, other lists, variables, or a combination of the above. One use for lists is as a control for for loops, the third new item. The for loop will step through the controlling list and assign the rodent variable to be equal to each item in the list in turn, and execute the enclosed statements once every time. The total number of repetitions equals the length of the controlling list. If you create more rodents, then just add their object numbers to the list. When you test the verb (just whistle) you will discover a bug. If a rodent is already in the room when you whistle it emits the 'scampers in' message, and stays where it is. Fix this problem by enclosing the contents of the for loop with if (rodent.location != player.location) and endif.

You are already familiar with the location and the name properties found on every object. You can create your own properties on objects you own. Use @prop me.rodent_list to create one on your character. Then set the contents of the new property with @set me.rodent_list to {#1234, #2345} where the numbers are the object numbers of your mouse and rat (and any others you create). The property now contains a list which may be used in any verb. Edit the whistle verb and replace the current list with this.rodent_list, or delete that line completely and replace rodent_list in the for loop line with this.rodent_list. The advantage of this change is that other verbs and other objects now have access to that list as: #<your player's #>.rodent_list

One such item would be a status report of your rodents. Create an object (based on a $thing) called a rodent report with aliases rodent report and report. Set the description to be: The current locations of Odo's rodents are:. Use @verb report:description this none this to create a new verb, and add the following lines to it:
  desc = {pass(@args)};
  for rodent in (#
<your player's #>.rodent_list)
    line = rodent.name + " is in " + rodent.location.name + ".";
    desc = {@desc, line};
  endfor
  line = "Odo has " + tostr(length(#
<your player's #>.rodent_list)) + " rodents.";
  desc = {@desc, line};
  return desc;

When you look at an object, its description verb is called. The default description verb simply returns the object's description which is then printed. The description verb you just wrote calls the default description verb (using pass(@args)), adds some extra text to the description, and returns (to the look verb) the modified description. Thus you never have to call this verb, you just look at the object. The phrase desc = {@desc, line} appends the line to the end of the existing description list. Each entry in this list is a line of text. The length() function simply returns the number of items on the given list. Since that value is a number, the tostr() function must be used to convert it to text. Also note that on line #3 rodent.location would have returned an object number, not a name.

You now have at least four verbs. When you try to make your own verbs you will find that they will not work unless you have defined their arguments correctly with the @verb command. In the examples I have used this none none, this none this, and none none none. The best way to explain what arguments a verb needs is by example, here are some of the ones you will most likely use.
Verb: Arguments: Usage:
Odo:whistle none none none whistle
mouse:pinch this none none pinch mouse
Odo:kick any none none kick Max
pie:throw this at any throw pie at Max
Odo:apologize none to any apologize to Max
Odo:title this none this May only be called from other verbs.
Use the above list for referance when creating new verbs.

If you have actually done this tutorial (as opposed to just reading it), you've got a basic foundation of knowledge regarding the MOO programming language. To explore further, obtain a copy of the excellent LambdaMOO Programmer's Manual, and experiment.


This tutorial is also available in a text-only version.