TSCENE tutorials




Tutorials


News
Rumors
Reviews
Features
Oracle
Tutorial
Humor
Links
Downloads
Re-Act
Mail
Back Issues

Tutorials

This is the Tutorials section of TScene , where you'll find walkthroughs , hints , tips and other interesting and useful information about games you may play. Future issues will cover a whole range of games and mediums , from computer games to RPGs. This issue however , we have a special treat for you. Direct from Valhalla comes a tutorial in the VME coding system for the Valhalla MUD.



The Valhalla MUD Engine: Past, Present and Future

Background

If you're familiar with MUDs and have tried a few, the chances are you've heard of Diku MUD, or the names Hans Henrik Staerfeldt [God], Sebastian Hammer [Quinn], Michael Seifert [Papi], Katja Nyboe [Superwoman], and Tom Madsen [Stormbringer]. These pioneers of online gaming started writing Diku I in the late 80s while still at university. They released the code to the public (http://www.dikumud.com), and now, almost a decade later, the Internet is home to hundreds of Diku I derivative-based MUDs. SillyMUD, CircleMUD, Merc1, Merc2, Envy, and of course the VME/Diku II project (a complete rewrite by the same team mentioned above), are among the most popular of these Diku children.

What is VME?

The internal functioning of the VME (http://www.valhalla.com/vme) is almost totally different to that of Diku I. The new handling allows easy expansion to the code, and saves as much time as possible, using message parsing, event queues, etc. The VME today is a far cry from that of the early nineties, though, because thanks to God, Papi, Ken & Tim Perry (the main implementers of the system code), the VME has evolved into quite possibly the most advanced and dynamic MUD the Internet has ever seen.

To aid in the development of new zones (areas in a MUD, such as a forest, a castle, etc, and the monsters [known as mobiles] and objects contained therein), a custom programming language (in the mould of C, but far easier to grasp), was created, and has become an integral part of zone creation. This language, known as DIL, is regularly updated with new functions and features at the request of builders who bring their imaginations to life on a VME MUD.

DIL gives you the ability to code almost anything your imagination can conceive of, be it a mobile who gives a quest, a powerful new spell, a seemingly-real computer generated person who can dynamically interact with players, or something as simple as a drawbridge that is raised and lowered at certain times of the day or night.

Coupled to DIL is DMC, an easy-to-use database format with which you create mobiles, rooms, and objects. DMC on its own is enough to make a real working zone, but most builders choose to inject more character and life into their zones by attaching simple DIL procedures to their monsters and items. In short, zones can be as simple or as complex as you wish, making building on a VME almost as accessible as playing the game itself. You can download the Public Domain version of the VME from http://www.valhalla.com/vme.
Valhalla MUD - The VME home site - www.valhalla.com - telnet valhalla.com 4242

But what is it like to play? The developers of VME have run a MUD called Valhalla since the beginning of the nineties, which is at the forefront of VME development. Our VME is a typical example of what a VME is like. When you first enter the game, an automated newcomer guide can show you the ropes when you simply type "say Help me Newbie guide". He teaches many lessons and you can return to him to pick up where you left off while still a relatively low level. Which brings us to the level system. On our VME, Valhalla, there are fifty levels of mortal play, during which you can practice physical abilities like strength, dexterity, etc, and improve your skills in weapons, spells, and skills. Once you reach level fifty, you can still increase in level but you no longer receive points with which to practice natural physical abilities and from then on you concentrate on your weapons, spells and skills. You can of course customise your own VME to work on a different level system. There are many guilds on Valhalla and most of these come as standard with the VME, like Fighters and Sorcerers, and each can train you in its own unique skills, spells and weapons. There are characters on Valhalla over level 1000 who can still practice new things. How do you gain levels? Completing quests (there are hundreds of these, ranging from search and retrieve to slaying creatures and solving puzzles), and killing monsters, gains you experience, and when you accumulate enough experience you can increase your level. With many thousands of locations, monsters, items, the scope for adventuring is almost unlimited.

All of our current Builders (including the author of this article) are, and started out as, players. We work voluntarily to improve the game for other players, Valhalla MUD is currently a non-profit MUD, and any donations we receive are used to pay for equipment and the internet connection. In the past year, the Valhalla MUD Engine has become available for purchase at a very low price, and as a result, we now have over 20 sister VME MUDs.

We invite you to telnet to valhalla.com port 4242 and immerse yourself in the magical and changing world that is Valhalla.
Q&A, DIL, and DMC

In this issue we will tackle a simple "travel" system using a genie, and his lamp. We will need a mobile (the genie himself), and an object (his lamp). We will firstly need the genie to emerge from his lamp when it is rubbed, and then ask the player who rubbed the lamp where he would like to go, preferably from a list of pre-set locations, take the player there, and then retreat into his lamp.

Let us start by making the lamp and the genie himself in DMC code, and then follow this by writing the DIL programs which bring him and his lamp to life. Please note that I'm assuming you have a basic working knowledge of Zone writing. If you don't, then I recommend you visit the builder's library at http://www.valhalla.com and read the builder's tutorial. All of the code below has comments explaining the more complex/important parts of the code. These comments are enclosed between /* and */.

First, the lamp, and this goes in the %objects section.

genie_lamp
names {"genie's golden lamp", "golden lamp", "lamp"}
title "a genie's golden lamp"
descr "A beautiful golden lamp has been carelessly left here."

extra {}
"It appears to be an elegantly styled slim lamp with a long spout and decorative handle. Its highly polished exterior is dazzling and the thought of the number of hours taken to give it such a pristine appearance brings tears to your eyes. Perhaps you could rub it like one of those legendary genie's lamps?"
/* The manipulate list decides most of what we can do with an object. In this case, we can pick it up (and drop it), or hold it (but not wear it on our head, for example). */

manipulate {MANIPULATE_TAKE, MANIPULATE_HOLD}

MATERIAL_METAL("Polish Gold") /* For the purposes of resizing this object, we tell the MUD that it's metal */

weight 5 /* 5 pounds of gold - big and hefty. */

cost 30 GOLD_PIECE /* It's Gold, so it will be expensive */

/* The next line tells the MUD that the genie lamp wants to use a dil program called rub_me which is contained in the %dil section of the zone called myzone. This dil is what will bring the lamp to life. */

dilcopy rub_me@myzone();

end /* genie_lamp */

And now the Genie himself. The following code goes in the %mobiles section of the zone file.

genie
names {"Genie"}
title "the magical Genie"
descr "A magical genie is floating about a foot above the ground here."

extra {}
"He looks rather quizical, but the kindly smile he wears on his pudgy face suggests he has a good soul. He is wearing a turbin and a purple robe, and where his legs and feet should be, only air can be seen."

extra {"turbin"}
"It's very large, and quite funny looking really. That genie sure must have a lot of hair."

alignment 0 /* Our Genie is neutral - he doesn't care either way */
sex SEX_MALE
race RACE_ELEMENTAL_AIR /* Genies are made from Air so I've decided to make him an air elemental. */
level 10 /* Level 10 will mean he's quite weak as monsters go. */
height 2000 /* He'll be 2000 cm tall, or about 6 feet. */
weight 1 /* He's made of air so he's very light - he weighs 1 pound. */

/* If he's in a fight, the genie will use his fists to defend himself. We could also add a line to make him cast a spell, eg dilcopy combat_mag@function("lightning bolt", "", 0, 1); This would make him cast a lightning bolt every round of combat at his opponent. ARM_CLOTHES means hitting the genie is like hitting a person who is wearing clothes, as opposed to wearing something like plate mail (ARM_PLATE). */

NATURAL_DEF(WPN_FIST, ARM_CLOTHES)

/* Now we set his natural physical attributes (these are in proportion to his level, and won't be the actual values, str 20 on a level 10 mob would be 10 strength): 20 strength, 20 dexterity, 8 constitution, 10 hit, 18 brain, 4 charsima, 20 magic, 0 divine */

MSET_ABILITY (20, 20, 8, 10, 18, 4, 20, 0)

/* Broad average skill in all weapons and spells */
MSET_WEAPON(9, 8, 8, 8, 8, 9)
MSET_SPELL(4, 4, 5, 5, 5, 5, 5, 5, 4, 4, 4)

exp 100 /* When you kill him you get 100% of the experience points he is worth. */

money 3 SILVER_PIECE /* He's carrying 3 silver pieces. */

/* As in genie_lamp, this line tells the MUD to put the DIL named genie_dil on this mobile. */
dilcopy genie_dil@myzone();

end /* genie */

Now we come to the two DIL programs that make the genie and his lamp come alive. Rub_me responds when the player types "rub lamp".

dilbegin rub_me();

var
u : unitptr;

code
{
:start:

/* The following line waits for the player to type rub lamp and then the program procedes. */

wait(SFB_CMD, command("rub") and
(findunit(activator, argument,
FIND_UNIT_IN_ME | FIND_UNIT_SURRO, null) == self));

block; /* Rub isn't really a command, so we don't want the MUD giving a "command not found" error, that's why we "block" it. */

/* Only continue if the lamp (self) is being held, if not, quit and tell the player why. */

if (self.equip != WEAR_HOLD)
{
act("You have to hold it first...",
A_ALWAYS, activator, null, null, TO_CHAR);
goto start;
}

/* Act is a very powerful function - the first act below sends the line "You rub the lamp and a magical genie appears!" to self.outside, which is the unit carrying the lamp, in this case the player. The TO_CHAR parameter tells act that we want to send the line to the player, while TO_REST tells act to send the line to everyone in the same place as self.outside, but not to self.outside him/herself.*/

act("You rub the lamp and a magical genie appears!",
A_ALWAYS, self.outside, null, null, TO_CHAR);
act("$1n rubs $1s lamp and a magical genie appears!",
A_SOMEONE, self.outside, null, null, TO_REST);

/* These lines put the genie in the same room as the player. The player is represented as self.outside, and self.outside.outside is the room which is "carrying" the person.*/

u := load("genie@myzone");
link(u, self.outside.outside);

/* We now put the name of the person who rubbed the lamp as an "extra" description on the genie. This allows the genie to find out who summoned him. Every unit has extra descriptions, the default extra description is what you see when you look at the unit. */

addextra(u.extra,{"$Master"},self.outside.name);

/* It's amazing isn't it! */
exec("gasp", self.outside);

/* Destroying the lamp is useful - it means we won't have to worry about where it is or what the player does with it. When the genie is finished doing its work, he can load a new lamp and then destroy himself. */

destroy(self);

goto start;
}
dilend /* rub_me */

That last comment above is how I like to end my dils - it tells me what dil was just finished, and in a zone file with many dil procedures, it is very useful to know what the last dil was without scrolling back up to the dilbegin line.

The next dil is the DIL which does the Genie's work. Again, the comments in the dil explain how it works.

dilbegin genie_dil();

var
extr : extraptr;
pc : unitptr;
my_lamp : unitptr;
place : string;
i : integer;

code
{
:init:

/* Heartbeat is a standard variable which sets the time per "TICK" for this particular DIL program. The SFB_TICK event and the pause command use this variable to decide the length of a pause and the time between TICKs. */

heartbeat := PULSE_SEC * 1;

/* Let's retrieve the name of the person who summoned the genie, from the extra description. */

extr := "$Master" in self.extra;

/* If there is no $Master extra description then go back to the lamp. */

if (extr == null) goto lost_pc;

:start:
/* We're going to have to do operations using the player (who is a unit) but we need to assign him as a unit first, and to do that we use the name from the $Master extra description as a guide. Findunit takes 4 argument: the first is a reference point to start searching from, in this case the genie himself, the second is one of the names in the desired unit's namelist, in this case the player's name. The third parameter is where to look in relation to argument 1, in this case we search the place surrounding self (the genie), which is the room. The last parameter is unneeded in most cases, and in this case. It can be used if we don't use a FIND_UNIT_XXX. For more details see the document in the builder's library which deals with findunit. */

pc := findunit(self, extr.descr, FIND_UNIT_SURRO, null);
if (pc == null)
{
act("The Genie can't find his master so he returns to his lamp.",
A_SOMEONE, self, null, null, TO_REST);
goto return_to_lamp;
}

/* Secure lets us keep track of the Player - if he leaves the room (the local environment), secure sends the program to the label marked lost_pc. */

secure(pc, lost_pc);

exec("say Hello " + pc.name, self);
exec("say I can take you to far away places! All you need do is " + "say 'take me to ', where is midgaard, udgaard " + "or arabia", self);

:get_destination:

/* The next line makes the genie disappear after 30 seconds if the person doesn't tell him where he wishes to go. */

heartbeat := PULSE_SEC * 30;
i := interrupt(SFB_TICK, TRUE, too_long);

/* We wait for the player to say where he wants to go, but it only triggers if the genie isn't fighting. */

wait(SFB_CMD, command(CMD_SAY) and (activator == pc) and (self.position != POSITION_FIGHTING));

clear(i); /* This stops it from quitting after 30 seconds. */

/* Next we establish which of the three destinations the player has requested to go to. */

if ("midgaard" in argument)
{
/* We now set the address of the place we're going to take the player to. */
place := "market_sq@midgaard";
goto take_me;
}

else if ("udgaard" in argument)
{
place := "square@udgaard";
goto take_me;
}

else if ("arabia" in argument)
{
place := "wharf_3@arabian";
goto take_me;
}

/* If he didn't choose one of the three destinations, then tell him his mistake, and then give him the choice again. */

else
{
/* Reset the heartbeat from 30 seconds to 1 second. */
heartbeat := PULSE_SEC * 1;
pause;
exec("say I'm afraid I don't know where that is. Please choose " + "midgaard, udgaard, or arabia.", self);
goto get_destination;
}

:take_me:
heartbeat := PULSE_SEC * 1;
pause;

/* Findroom turns the address string into a unitptr, which is something we can work with, and that's needed for link. */

/* There's no point in going to a place if we're already there! */

if (findroom(place) == self.outside)
{
exec("say But we're already there, master!", self);
act("The Genie returns to his lamp!",
A_SOMEONE, self, null, null, TO_REST);
goto return_to_lamp;
}

/* Take the player to the place. */

exec("say Your wish is my command, master!", self);
act("$1n disappears in a puff of blue smoke!",
A_SOMEONE, self, null, null, TO_REST);
link(self, findroom(place));
act("$1n appears in a puff of blue smoke!",
A_SOMEONE, self, null, null, TO_REST);
link(pc, findroom(place));
act("You suddenly find yourself in another place!",
A_ALWAYS, pc, null, null, TO_CHAR);
exec("look", pc);
pause;
act("The Genie returns to his lamp!",
A_ALWAYS, self, null, null, TO_REST);
goto return_to_lamp;

:too_long:
act("The Genie gets tired of waiting for an answer and returns to his " +
"lamp.",
A_SOMEONE, self, null, null, TO_REST);
goto return_to_lamp;

:lost_pc:
/* It's customary to unsecure a unit which was secured previously when the unit is lost from the local environment. */

unsecure(pc);
act("The Genie's master has left him, so the Genie returns to his lamp.",
A_SOMEONE, self, null, null, TO_REST);

/* Load a new lamp, and then put the lamp in the genie's place. Then the genie self-destructs, but it appears to return to the lamp.*/

:return_to_lamp:
my_lamp := load("genie_lamp@myzone");
link(my_lamp, pc.outside);
destroy(self);
quit;
}
dilend /* genie_dil */


So there you have it. A working genie and lamp. Obviously this can be further developed to do other things, but it is a good starting point for a lot of ideas and illustrates the use of many basic dil functions and commands. Should you have any questions (I would appreciate them for further articles), then please send them to me, John Clare, aka Eirinn on Valhalla, at eirinn@valhalla.com. Happy coding!

Written by John Clare.
1