Chapter 1

Meet the Dream Maker

The first step to mastery in the lands of sleep is the realization, without waking, that one dreams. In the day worlds, mastery begins by forgetting, without dreaming, that one is awake.
--DoD

DM is a programming language for the creation of multi-user worlds. By `world' I mean a virtual multi-media environment where people assume personae through which they interact with one another and computer-controlled objects. This could take the form of a competitive game, a role-playing adventure, a discussion board, or something we haven't even imagined.

Frequently, the terminology of a role-playing game is most suitable: humans are PCs (playing characters) and computer-controlled personalities are NPCs (non-playing characters). The virtual embodiment of a player is often called an avatar. The game rules are written in DM and faithfully carried out by the computer. These define what actions players may instruct their avatar to perform, what effect these will have in the game, and any other events that may happen as time progresses.

To understand the mechanics of the system fully, it is helpful to know a few simple terms. Computer programs that operate over a network are often divided into two parts: a client and a server. In this case, the client is the program that players use to enter commands and see what happens as a result. In other words, the client handles input and output. The server is the program that runs the game, carrying out the rules defined in the DM language. The game designer writes these rules in a third program called the compiler. This reads the DM program (known as the source code by programmers), checks it for grammatical errors, and generates a more compact, computer friendly, file known as the byte code or binary. It is this file which the server reads to see how to run the game.

So there are three main programs: the client, server, and compiler. We call these Dream Seeker, Dream Daemon, and Dream Maker, respectively. (The word daemon is just another (more fantastical) word for server.) As a whole, we refer to this collection of software as BYOND, which stands for Build Your Own Net Dream--an apt description of its purpose and also of how far it has wandered beyond our original plans. But that is another story!

Every introduction to a programming language must begin with the same example. Call it destiny, inevitability, or pure chance; it is rather uncanny that the name of the universal introductory example is hello world. Spooky, no? That's exactly what happens in this example--we say hello to the world.

In DM you write it like this:

mob
   Login()
      world << "Hello, world!"

If you have any prior programming experience, the last line probably looks vaguely sensible. It outputs the text inside the double quotes to the whole world. But what on earth is a mob and why is each line indented like stair-steps? All in good time. For now, simply understand that the player's avatar in the game is a mob. When a player logs in, the server is instructed to output the message "Hello, world" to everybody.

Compile and run this program (see figure 1.1). If all goes according to plan, you should see the words, "Hello, world" magically appear on Dream Seeker's output screen. Voila! You have created your first BYOND world.

Now you know the basic steps for designing worlds. You write some DM code, compile it, and run it. But this world didn't have anything for the player to do. That's next.


Figure 1.1: Hello World!

This first world serves not only as an introduction to the DM language, but to the Dream Maker editor/compiler as well. Fortunately, the system has been designed to be quite simple to use, and with just a few steps you should be on your way to BYOND programming wizardry!

Dream Maker refers to the collection of files comprising the project as the world environment. When you make a new project, you create a single environment file, which has the name "[worldname].dme". This file may contain source code, but in general it will only be comprised of automatically generated references to other files in the project. This is best seen by example, so let's stop talking, and get coding!

  1. Create the "hello" project by selecting New Environment... from the File menu. This prompts for a directory in which your project will be stored. Enter "hello" as the desired directory. This creates a new directory called "hello", which now contains the hello.dme environment file. Notice that hello.dme also appears in the file tree displayed on the left side of the screen. All files in the environment directory are listed there.

  2. Let's put the code for this project in a separate file. Select New File... from the File menu. Choose Code File for the type, and enter "hello" for the name. This creates a file called "hello.dm" in the environment directory, and a corresponding listing in the file tree. The checkbox next to it indicates that the code in hello.dm will be included in this project.

  3. The hello.dm file is now ready for editing. Type the following code. (Make sure the first line is not indented, the second is indented once, and the third is indented twice. It is easiest to use tabs for the purpose.)

    mob
      Login()
        world << "Hello, world!"
    

  4. Compile the code by selecting Compile from the Build menu. If there are problems, they will appear in the output box at the bottom of the screen. But unless you indented incorrectly, all should proceed smoothly.

  5. Run the compiled world by selecting Run from the Build menu. This launches Dream Seeker, which should subsequently welcome the world!


Consider the Hello World example again. The DM code says that when a player logs in, a message should be displayed. We can do a similar thing for other types of actions. For example, if the player types a command, a message could be displayed.

In DM, commands are called verbs. A verb is defined in the following example:

mob
   verb
      smile()
         world << "[usr] grins."

Notice the funny stair-step indentation again! That will be explained in a little bit. For now, read this example from top to bottom. Once again we are defining a property of a mob (the player's avatar). In this case we are adding a verb, an action that the player can instruct the mob to perform. The name of the verb is smile. The final line displays a message when the mob smiles. Notice the [usr] in the message; as you may have guessed, that is not displayed literally but is replaced by the name of the user, the player who initiates the command.

Run this example. Once you have logged in, try typing smile and pressing enter. You should see the grinning message with your login name substituted into it. Amazing! Fantastic! But playing god is a serious business. Don't let anyone catch you grinning.

For variety, you could add some more verbs. Here are a few:

mob
   verb
      smile()
         world << "[usr] grins."
      giggle()
         world << "[usr] giggles."
      cry()
         world << "[usr] cries \his heart out."

Now the stair-step pattern has been broken because all three verbs smile, giggle, and cry are at the same level of indentation. In DM, indentation at the beginning of a line serves to group things together. Here, smile, giggle, and cry are all grouped together as verbs belonging to mob. Each of these verbs has it's own contents indented beneath it.

Notice the use of \his in the cry verb. This macro is replaced by the appropriate possessive pronoun. It could be his, her, its, or their, depending on the gender. DM provides a few other useful macros like this one to make life easy.

So far nothing has been said (because you never asked) about the empty parentheses after the verb names in the above examples. They were in the first example after Login too. These are the mark of a procedure definition. The verbs and Login are all examples of procedures, which are a sequence of instructions to be carried out. In the examples so far each procedure consisted of only one line--an instruction to display some text. They can, of course, become much more complicated than that.

There are two general categories of procedures: those that show up as player commands and those that do not. These are called verbs and procs respectively. By that definition, Login in the Hello World example was a proc.

The parentheses after a procedure name are more than decorative. They can be used to define procedure parameters. This allows for providing additional information to the procedure. The information is stored in a variable, that is, a piece of memory with a name. To confuse matters, a programmer will often call such variables, which serve as the parameters to procedures, arguments. Why? Well, just for the sake of argument.

Here is an example of a verb that takes a parameter--in this case a short message to be broadcast to the world.

mob
   verb
      say(msg as text)
         world << "[usr] says, [msg]"

In these few short lines are the bare bones of a chat world. Users can log in and start gabbing using the say verb. Try it out. Your session might look something like the following:

say "hello world!"
Dan says, hello world!

The main point of interest in the DM code is inside the parentheses where the parameter msg is defined. It could have been called anything; the variable name is arbitrary. The statement as text indicates that a short message supplied by the user will be stored in the variable. This message is then inserted into the final output at the position marked by the expression [msg].

So far I have casually introduced mobs, verbs, procs, and arguments. Now it is time for a formal (tediously exciting) description of the DM syntax. It may take several multi-clausal sentences to get through this, so don't hold your breath.

DM code is structured like a tree. The top of the tree is called the root. The various types of virtual objects (mobs being one) branch off of the root and may in turn give rise to additional strains that branch down from them.

If you haven't noticed, the code tree terminology is upside down. Of course, so is the file-system on your hard-drive, and every other informational tree in existence. It is quite possible that the vast majority of computer scientists have never actually seen a real tree. The sheer weight of their ignorance keeps the jargon from flipping right side up, and we are stuck with trees having a root at the top and leaf nodules at the bottom. Or it might just be standard obfuscation. That's why I do it.

It is time for an example. One particularly interesting type of virtual object is a turf. It is a building block used to make graphical maps that players can walk around on. Suppose we wanted to make a maze. That would require two types of turfs: floors and walls. Here's how you would define them:

turf
   floor
   wall

All we did was branch two new types of objects off of the basic turf. One is called floor and the other wall. The terminology of a family tree is often used to describe the relationship of the various objects defined here. Turf is the parent of floor and wall. The two children are siblings of each other. A child inherits all the properties of its parent and adds some of its own to distinguish it from its siblings. Both floor and wall are turfs because they are derived from the turf object type.

To make a maze, we need to specify a few properties of floors and walls: what they look like and whether you can walk through them. While we're at it, the appearance of a player should be defined too. This is how it is done:

turf
   floor
      icon = 'floor.dmi'
   wall
      icon = 'wall.dmi'
      density = 1
mob
   icon = 'player.dmi'

Several assignments have been made. These take the form of a variable on the left-hand side and a value on the right. In the case of the icons, the value is the name of an icon file inside single quotes. In the case of density, the value should be 1 or 0 to indicate if it is dense or not. A dense turf will not allow other dense objects (like mobs) to walk through them.


Figure 1.2: The Amazing Mapper

For most programs, adding graphic support is a massive chore. The facilities in Dream Maker, however, make this task quite simple. For our example, we'll just draw a couple of icons and put them on a map.

  1. Create a project called maze through the New Environment... option.

  2. Create the main maze.dm file, and enter the following code:

    turf
      floor
        icon = 'floor.dmi' 
      wall
        icon = 'wall.dmi'
        density = 1
    mob
      icon = 'player.dmi'
    

  3. Build the floor.dmi icon. Do this by selecting New File... and choosing Icon File for the type. This will bring up a new window, with the option to build pixmaps (static, directionless, icons) and movies (animated or directional icons). Choose New Pixmap... from the Graphic menu and show off your artistic flair by drawing a picture of a floor. Repeat this step for the wall.dmi and player.dmi icons.

  4. With the three icons in place, the project should compile. Test this by selecting the Compile option. If it is successful, you should be able to see your icons in the object tree tab on the left-hand side of the screen. This tree illustrates the objects defined in your world.

  5. You can run the world now, but you won't see any icons because you haven't put any objects on the map yet. To build a map, again select New File... and choose Map File. You can name it whatever you'd like; the .dmm extension will be appended, indicating that this file is a map.

  6. Now for the fun part! Create the map by selecting objects from the tree and placing them with the various functions. For example, to add a row of walls, select the Add function on the map pane, click on the wall tile in the tree (it's underneath turf), and draw them on the map by left-clicking the mouse. You can remove tiles by right-clicking. The map editor has considerable functionality; you can learn about it by reading the included documentation.

  7. Compile the new, graphical project and run it with Dream Seeker. If all goes well, you should see your creation on screen. Your avatar can roam the floors and bump into the walls. Not too bad for a couple minutes of work!


The reason we did not have to set the density of the floor to 0 is that the default density of a turf is 0. Since the floor is derived from a turf, it inherits all the default properties of one. This sort of inheritance of characteristics is one of the important elements of object-oriented languages like DM. Ultimately, it is just a compact way of describing closely related things.

Before you test this example, you will need to design the icons and the maze itself. Fortunately, this process is a natural part of Dream Maker's functionality (see figure 1.2).

When you are done making the map, you can compile and test the world. When you log in, you should be able to walk around in the maze you designed by using the arrow keys. Amazing!

Of course there are always small details that one doesn't think about until after the fact. For example, where is the starting point in the maze? We never specified, so players are just dumped onto the map in the first available spot. Here is one way to do it:

turf
   floor
      icon = 'floor.dmi'
   start
      icon = 'start.dmi'
   wall
      icon = 'wall.dmi'
      density = 1
mob
   icon = 'player.dmi'
   Login()
      loc = locate(/turf/start)

You will have to make a new icon for the start turf and then edit the map to mark the starting position with it.

The code that makes the initial placement of the mob is in the Login proc. It sets the location of the mob (loc) to the position of the start turf. This is done by using the locate() instruction--one of the many built-in procedures in DM (see figure 1.3). It computes the position of an object type (in this case, the start turf).

Notice how the object type /turf/start is specified. This notation is called a type path because of the way you specify the path (starting from the root) down to the specific type of object you want.

Now suppose you forgot to put a start turf on the map. What would happen? The locate() instruction would fail and the player would not get placed on the map and therefore wouldn't even be able to see the maze after logging in. A total disaster! Wouldn't it be nice to fall back on the default behavior of at least putting the mob somewhere on the map? In other words, we have to somehow run the default Login proc as well as the one we defined, just in case there is no start turf. Here is how to do it:

mob
   Login()
      loc = locate(/turf/start)
      ..()

The final line does the job. It invokes a procedure with a strange name: just two dots. That is the name DM uses for the default procedure, more generally known as the parent or super procedure. In the case of Login, the default proc checks to see if the mob is somewhere already. If not, it finds a vacant spot on the map, which is just what we wanted.

Now you can begin to see the general flavor of DM programming. There are a number of events (Login being one) which are handled by procedures. When necessary, you can override the default procedure with one of your own to make things work exactly how you want.

This is another important component of object oriented programming. Each type of object can respond to events differently. The way in which they respond is inherited from their parents by default, but can be redefined and augmented as needed.

This introduction has just scratched the surface of DM. You should begin to see some interesting possibilities. At the same time, you should have a lot of unanswered questions. Keep both of those in mind; they will be your guide through the more detailed exploration of the language that follows.


Figure 1.3: Help is on the way!

No programming environment is complete without a comprehensive, accessible reference. Dream Maker provides this in the form of a searchable index of topics and built-in properties. You may access this by selecting Help On... in the menu, or by hitting the F1 key. If the cursor is positioned on a word (such as "locate"), help will be found for that topic.