(last change: april 27th, 1992)

3.0... What changed?


Index


Preface


I will divide this doc into several chapters, so you can quickly find the things you are interested in. In this document, when some- thing is between brackets (<...>), it means that it is required to fill in the piece between the brackets in your command with something that makes sense. If something is between braces ([...]) it means that that part is optional, and can be left out.

Introduction


I wrote this doc, to help people who have absolutely no experience in programming LPC 3.0, or with the 3.0 mud itself. I hope that you will be enlightened in the use of 3.0 and that you can make simple 3.0 objects after you have read this doc. Some people think this is the reference guide for LPC 3.0. Let me assure you that it isn't, and that such a document would be hard to write. LPC 3.0 is a language on the move, and I have learned that the best way to learn it is to look in the sources. However, not everyone will be able or willing to do that, so I hope that reading this doc will get you on the coding track a bit sooner. If you want to know the bottom of everything you are using, I suggest you take a look in all the files you include/inherit.

It could very well be that this document is not very clear to the layman. The problem is that I have been programming in LPC for a long time now, and to me everything seems very clear, so I might not even notice that I skip important explanations that ought to be given. If you find such a spot in this document, or notice an erroneous remark or erroneous code, do not refrain from mailing me, your help is greatly appreciated.

Tricky

The 3.0 mud


Some commands


There are a few commands that really are interesting in 3.0, of which the most important ones are probably:

allcmd
Shows all your commands.
localcmd
Shows all commands added by your direct environment.
help <cmd>
Shows often help on the command <cmd>. In order to keep this document short, I have not explained every following command in full. Should you need more help, use "help".
sman [-k] [-s] <fun>
Searches for help on the function <fun>. If you use the -k flag, the function <fun> will serve as a keyword, all matching functions will be displayed. This is used to search the mudlib sources. If you use the -s flag, you get to see the sourcecode of the function. Note that this works fine when the searchtable is updated regularly, but that it could occur that you end up in the wrong spot in the sourcecode. No fear; you probably are near the place where the source really is.
man [-k] <subject>
Searches for help on the function <fun>. If you use the -k flag, the function <fun> will serve as a keyword, all matching functions will be displayed. This command is to search for information on efuns and general subjects.
ed <file>
Edit a file. Most of the help on the editor can be obtained within it! Try "h", or "?" in the editor. NOTE: you don't have to do your editing in Ed, if you can ftp, try this: "ftp milou.cs.chalmers.se", use the login "lpmud:<yourname>" and your own mud-password. Then you can use "[m]put" and "[m]get" to transfer files to and from your own directory. That way, you can use all the comforts of your home editor.
cp <from> <to>
Copy the file <from> to the file <to>. You may even use wildcards like "*" to match files. There is a maximum number of files you may match, though. <To> may also be a path.
mv <from> <to>
Move (rename) the file <from> to the file <to>. The file <to> doesn't have to be in the same directory as <from>.
rm <file>
Remove (delete) the file <file>. Use wildcards if you like.
sanction <flags> <who>
Allow/disallow someone to read/write in your own directory or to snoop you. Try "help sanction" for more info.
start here
Set your startup-place. I like to start in the privacy of my workroom, instead of in the crowded church.
title
Set your title.
adjdesc
Set your adjective description.
altitle
Set your alignment title.
busy [flags]
This command sets your busy level. There are too many flags to explain them all here. Let me just say that it is possible to make yourself completely deaf with this command.

Well, those were by far not all commands, but these are the most basic ones. If you want to know more, use "allcmd" to find out your commands, and then ask "help" on one of them.

Directories under 3.0


The paths under 3.0 have changed radically. I will point out what the interesting directories are.

/doc
This directory contains only subdirectories. As the name suggests, those directories are filled with docs.
/doc/examples
This directory contains a growing number of examples of various objects. If you want to make something, see if there is an example of it somewhere in this directory.
/doc/man/efun
This directory contains all manuals for the external functions that are provided by LPC. If you search for a function that is not specific for any object in particular, look in this directory. You can also "man <external function>" to look at its description.
/doc/sman
This directory contains all manuals for the local functions of most objects that are often inherited. There is a lot to be found in here! It is probably easier to do "sman <local function>". "sman" stands for source manuals; the sources of the functions can be examined by doing "sman -s <local function>". If you only know a name partially, you can do "sman -k *<partial name>*", that will show all function names that match.
/std
This directory contains all objectfiles that need to be inherited if you make an LPC object. Multiple inheritance is allowed in LPC, but usually inheriting one objectfile will suffice. Inherit eg.:
armour.c
for amours, like a chainmail
board.c
for a bulletin board.
coins.c
for money, like some platinum coins
container.c
for a container, like a bag
creature.c
for a very basic creature, like a snail
drink.c
for a drink, like brandy
food.c
for food, like bread
herbs.c
for herbs, like blackberries
monster.c
for a human-like monster, like an orc
object.c
for generic objects, like a painting
spells.c
for an object that defines spells, like a scroll.
weapon.c
for weapons, like a two-handed broadsword.
/lib
Here you find inheritable modules that do stuff for you. If you want to make a shop, for example, a lot of convenience routines have been implemented for you in "/lib/trade.c". Just inherit that module as well in your shoproom, and you can use those routines. Here is a list of some library modules:
guild_support.c
for make guildshadows
herb_support.c
for creating herbs more easily
skill_raise.c
for making guildrooms where skills are trained
store_support.c
for making shops
trade.c
for things that use trading with coins
/sys
In 3.0 you use a lot of values to access things. In order to keep things transparent to the wizards, all values are hidden behind logical sounding names. This also enables the shifting of values without anyone noticing it in his objects. Therefore you should use always defines instead of their real definitions. In this directory you will find almost all of the includes that are interesting to include, such as:
macros.h
handy macros.
stdproperties.h
standard properties of all kinds of things.
wa_types.h
weapon and armour types.
ss_types.h
standard skill types.
formulas.h
standard conversion formulas.
money.h
money definitions
/secure
The 3.0 security system is more tight than that of 2.4.5. More and more the access to certain functions is checked. The first object that is loaded when the GameDriver is rebooted is the security master, or master for short. Some functions can only be called via this object, e.g.: SECURITY->query_domain_lord("Aurora");. The name SECURITY is defined in the file /secure/std.h
/d
In this directory all the domain directories are located. As you probably have noticed, all domains start with a capital, whereas wizardnames start with a lowercase letter. If a wizard has a directory, but is not in a domain, he is automatically in the Wiz domain.
/d/Wiz
This directory is the home of the braves. Most of the keepers and arches have their directories in this directory. You will probably not be able to read any files in them ;-).
/d/<Your_domain>
In this directory is your directory located. You have read access to all files and directories in this directory.
/d/<Your_domain>/<your_name>
Yep, this is your directory. You have read and write access to it. This directory can also be accessed as ~. E.g.: ~/workroom.c is your workroom, whereas ~skywise/workroom.c is Skywise's home office.
/d/<Your_domain>/common
This is the common directory of the domain you're in. Every wizard in the domain has write-access on it. That can be real handy if someone wants to debug your stuff when you are away.
/d/<Your_domain>/open
/d/<Your_domain>/<your_name>/open
Everyone (including people outside your domain) has read-access to these directories. That is why you don't need to write in the /open directory, simply put your open stuff in your own open directory!
/d/Standard
This is the domain that contains all startup-areas of all races, the wiz-area, the race-specific souls and much more. All directories can be read by you.

These were the most interesting directories. You probably cannot write in most of them. This rule does not apply to all people. Lords can write in every directory in their domain, arches and keepers can write everywhere they please. Note that you can allow other wizzes to read and/or write in your own directories by doing "sanction [R][W] <name>", or by simply putting the stuff you want them to examine in your open directory.

So much for the introductory part, let's get down to the serious business:

LPC 3.0


Some fear it, some think it is impossible, others adore it. I don't know. For those of you who know LPC 2.4.5 (or 2.4.6), it hasn't changed that much. Ofcourse there are lots of new functions defined by LPC 3.0, but they are implemented because they make your life easier. All that is written below is made with the intention to enable you to make simple objects in LPC 3.0. If you want to make advanced objects, ask someone who knows about it. You'd better try to make some simple objects at first, and then try to improve them.

Types


If you look for the first time at official 3.0 code, you will notice one difference with 2.4.5: the functions and their arguments have types, e.g. "object *query_team_members(int flag)".

It is possible to use types in your code, however you don't have to. If you use types, you must use it for all functions, and you must make sure that functions are put in the right order, i.e. if function a() calls in its body function b(), function b() must be declared above function a(). This declaration can be done in two ways: you can put the entire funtion b() above a(), or you can implement the function b() somewhere below a() and put the prototype of b() (e.g.: "int b(int arg);") above a(). One good reason to use types is that the compiler will be able to detect errors in your code more easy, and maybe even directly at the compilation and not wait until the bugous function is called.

LPC 3.0 knows these types:

void
Used for functions that do not return anything
int
Integers, ranging from -(2^31) to 2^31
int *
An array of integers, e.g. ({ 1, 2, 3 })
string
A string of characters and special characters, e.g. "\tHi!\n"
string *
An array of strings, e.g. ({ "foo", "bar" })
object
An object as you get with clone_object()
object *
An array of objects
mapping
A special array, e.g. ([ "foo":"f", "bar":"b" ])
mixed
Anything of the above.
mixed *
An array of mixed values.

Several of these types can be checked within your code with the functions intp(), stringp(), objectp(), mappingp(), which can be useful if you want to enable people to call your functions with e.g. objects or arrays of objects.

Type qualifiers


By adding certain keywords to the basic types you can modify the behaviour of your objects, functions and variables. They work differently depending on if they are applied to functions or variables.

Applied to functions:

static <basic type>
The function can only be called by this object. It can't be directly accessed with call_other.
private <basic type>
This function is only callable from other functions in the same compilation unit (the same file and files that include or are included by the file). It can't be directly accesessed with call_other, and it can't be called from functions in other modules in the inheritance chain.
public <basic type>
This function is normally callable. It is the same as if you didn't put a qualifier at all.
nomask <basic type>
A nomask function can't be hidden by an inheriting function. Neither can it be shadowed.
varargs <basic type>
A note to the compiler that the function may be called with a variable number of arguments.

Varargs and nomask can be combined with any of the other modifiers and with each other...

Applied to global variables:

static <basic type>
This variable will not be saved when the object is saved with save_object() or changed by a restore_object().
private <basic type>
This variable can only be accessed by functions in the same compilation unit.
public <basic type>
This is the same as a variable with no modifier.

Arrays


Arrays have become very powerful in LPC3.0. There are many new functions that support the use of arrays. Let me first explain what an array looks like in LPC: ({ a, b, c }) is an array with 3 elements, a, b and c. these elements can be of any type: integer, string, object or even array. Arrays can simply be added like this: ({ "foo" }) + ({ "bar" }) gives ({ "foo", "bar" }). Arrays are allocated on the fly, i.e. you don't have to allocate or deallocate anything, the gamedriver does that for you.

Here are a few array functions that are very handy:

arr[index]
This will give you the "index"th element of the array arr. Note that the elements of an array are numbered from 0 to the length of the array minus one (sizeof(arr)-1)).
member_array(elt, arr)
With this you can check if a given element is a member of the array you want to check. If it is a member, the index will be returned. Since this means that 0 can be the return value, -1 will be returned if the element is not included in the array.
explode(str1, str2)
This function returns an array that consists of the loose strings that come into existence after the string str1 is divided in parts by cutting on every occurence of str2. Eg. if you did explode("Humpty Dumpty fell of a wall.", " "), you would get ({ "Humpty", "Dumpty", "fell", "of", "a", "wall." })
implode(arr, str)
This function returns a string that is made of all elements of the array arr, with the string str placed between them. E.g.: implode(({ "foo","bar","zukini." }), " and ") would return "foo and bar and zukini."
arr[from..to]
This notation returns a piece of an array from element from to element to. The function slice_array, which did exactly the same, is obsolete. E.g.: ({ 1, 2, 3, 4 })[1..2] returns ({ 2, 3 }).
filter(arr, fun, obj)
This function returns an array with all elements in it for which the function fun in object obj returned 1. The elements for which 0 was returned are left out. fun is the function name, enclosed in quotes.
map(arr, fun, obj)
This function returns an array in which all elements have been replaced by whatever the function fun in the object obj returns for each element. fun is the function name, enclosed in quotes.
sort_array(arr, fun, obj)
This function returns the sorted version of arr, by using the function fun in object obj as less-equal function. Say eg., you want your array of strings to be sorted alphabetically. The you would make a function str_less_eq() like this:

And then you would do:

Et voila! An alphabetically sorted array.

Mappings


Mappings? Yes. Mappings are arrays that are addressed not with a number as index, but with a string. Mappings were designed to make certain function a little bit faster. Since mappings use more memory than normal arrays, you are asked to use mappings as little as possible, preferably not at all. This is how a mapping looks: ([ "foo":"bar", "jacuzi":"zukini" ]). The words before the colon are the indices. Let's call the previous mapping M. In that case M["foo"] would be "bar" and M["jacuzi"] would give "zukini". The part after the colon is of the type mixed, so it can be anything, even arrays or other mappings.

Objects in 3.0


As Commander wrote in his NEW_V3 doc, all objects will inherit something. The file that is being inherited is called /std/xxxx. The first function you define in your object, in which you will probably set the name, the long description and the short description, is called "create_xxxx", where xxxx is the name of the object you inherited. The function "create_xxxx" will be called the first time the object is loaded or cloned.

Every half hour, the mud will send a reset signal to all its objects. Then, the function "reset_xxxx" will be called. So, if you want to refresh your guards periodically in a room, so it will not stay unguarded too long, the "reset_room" function is a good place to put a check in.

The last interesting function is init(). This function will be called every time a living creature comes 'in sight' of an object. Eg. if the object is picked up, if a monster enters the same room, if an object is taken out of a bag, its init() will be called. Some objects require that you put a line "::init();" in the init() function. Typically, you will add commands to a player in the init(). Note that it is not possible to use the function init() in a monster. Instead, you should use init_living(). The '::' in front of a function means that you try to call the old version of init(), the one that existed before you decided to redefine it.

Objects and properties


Suppose you want to make a lead ball and a big cardboard box. To create these things, you set their name, and make up a long description. This is not enough, however. If you only do that, people will be able to carry about seven of each, whereas in real life two lead balls, because of the weight, or only one big cardboard box, because of its size, could be carried by a person.

To make objects more like in real life, objects have properties that their creator can set. Many standard properties have been invented, and are used to determine lots of things. They can be found in /sys/stdproperties.h, which you probably will want to include in your object. The defined names have a standard form, take for example OBJ_I_WEIGHT. The first part OBJ means that it is an object property. The second part, I, means that the property wants a value of the integer type. Other type-descriptors are S, O and V, and combinations like AI, AS, etc. for arrays of a type. S means string, O means object and V means 'value by function call'-string. An M means mixed type = anything :)

Properties can be added by doing "add_prop(<property>, <value>);" in the object. If the property already existed in the object, the old value will be replaced by the new value. You can ask the value of a certain property by calling "query_prop(<property>);" in an object. If a property was not set, query_prop() will return 0.

Some examples of interesting properties to set:

   #include "/sys/stdproperties.h"

      ...
      add_prop(OBJ_I_WEIGHT, 10000); 	    /* Set weight to 10 Kg    */
      add_prop(OBJ_M_NO_DROP, "@@my_drop"); /* VBFC (explained below) */
      add_prop(OBJ_I_INVIS", 1);	    /* Make invisible	      */
      ...

   int
   my_drop()
   {
       if (this_player()->query_wiz_level() > 0)
           return 0; /* Wizards are allowed to drop this */
       else
           return 1; /* Players are not */
   }

As you can see, the weight is set to 10000 grams, and invisibility is set to 1, meaning on. The NO_DROP property is used cunningly to detect whether someone wants to drop this object. If someone tries to drop our object, query_prop(OBJ_M_NO_DROP) is done, which causes a call to our function my_drop(). There the wizardlevel of the person is checked, and 1 (meaning NO_DROP is on) is returned if it was a player who attempted to drop it.

Some examples


There are a few basic objects that I will explain here. Ofcourse that will not be all objects, but the most interesting ones. I hope that the examples have some didactic value.

One thing: if you code an object, please, please, please, pretty please code with indentation like /doc/man/general/code_standards describes. There even is a command to help you: "indent <filename>". You don't have to use exactly that way of indenting, but use one alike. Other peoples code is unreadable as it is, not properly indented code is really not readable. Not for you, and not for the person that is trying to figure your code out.

ROOM.C


This is probably the most interesting object, since it is the one you will create most often. This is what the world is made of. Let's make a simple room.

   inherit "/std/room.c";

   #include "/sys/stdproperties.h"

   void
   create_room()
   {
      set_short("Entrance");   /* This will show up when the player is in */
                               /* "brief" mode.                           */
      /* Set the long description of the room */
      set_long(break_string(
         "There is something very peculiar with this room... It is "
       + "cube-shaped, yet it has no corners! You wonder what sorceror "
       + "would make up such a mind-boggling kind of room... Luckily "
       + "you can escape to the south.\n",70));

      add_exit("/d/Standard/start/church", "south", 0, 1); /* add exit */

      /* Make the corner examinable */
      add_item(({ "corner", "round corner", "edge" }), break_string(
         "You desperately search for a corner, but you cannot locate one. "
       + "How is this possible? You are starting to get a headache.\n", 70));

      add_prop(ROOM_I_INSIDE, 1); /* This is an indoors room */
   }

That was the room... As you can see, I used the break_string("...",70) technique to make sure that the string is cut into lines of length < 70 characters. The same technique is used with add_item. Note that it is possible to give either one string, or an array of strings as first argument to add_item. The given description will be shown every time a player wants to look at the identifier string. The add_exit function has four arguments: The filename of the room that is connected to this room, the command that will get you there and the third argument is an optional VBFC function (explained below). If that function returns 1, the exit cannot be taken. If the function returns 0, the exit can be taken. The last argument indicates how tiring it is to walk that direction. 1 is the default value.

OBJECT.C


Let's say you want to make a light-bulb. This is typically an generic object. This is what the code would look like:

   /* A light bulb */

   inherit "/std/object";

   #include "/sys/stdproperties.h"  /* We want to use standard properties */

   void
   create_object()
   {
      set_name("bulb");           /* The id of this object   */
      set_pname("bulbs");         /* The plural id           */
      set_short("lightbulb");     /* The short description   */
      set_pshort("lightbulbs");   /* Plural short descr.     */
                                  /* Long description        */
      set_long("This lightbulb is now more like a dark bulb.\n");
      add_prop(OBJ_I_WEIGHT,75);  /* Set weight property to 0.075 Kg  */
      add_prop(OBJ_I_VOLUME,200); /* Set volume property to 0.200 Ltr */
   }

That was all! You could leave out the add_prop(...) part, but then the light bulb would weigh 1 Kg and measure 1 Ltr. This goes for all properties: if you don't set them specifically, they will take a standard value. You can also leave out the set_pname and set_pshort part, but then the parser will try to make its own plural, which you might not like. Eg. when I cloned another "test version of the pipe of the Shires", I suddenly carried "two test versions of the pipes of the Shireses"...

Suppose you want the players to be able to light the lightbulb. Then you would have written the lightbulb something like this:

   /* A little bit more interesting light bulb */

   #include "/sys/macros.h"         /* Some useful macros                 */
   #include "/sys/stdproperties.h"  /* We want to use standard properties */

   inherit "/std/object";

   int lighted; /* Global variable to indicate if the bulb is lighted */

   void
   create_object()
   {
      set_name("bulb");           /* The id of this object   */
      set_pname("bulbs");         /* The plural id           */
      set_short("@@my_short");    /* The short description   */
      set_pshort("@@my_pshort");  /* Plural short descr.     */
      set_long("@@my_long");      /* Long description        */
      add_prop(OBJ_I_WEIGHT,75);  /* Set weight property to 0.075 Kg  */
      add_prop(OBJ_I_VOLUME,200); /* Set volume property to 0.200 Ltr */
      lighted = 0;
   }

   void
   init()
   {
      add_action("do_light", "light"); /* Add the command 'light' */
   }

   string
   my_short()
   {
      if (lighted)
         return "lightbulb (bright)";
      return "lightbulb (dim)";
   }

   string
   my_pshort()
   {
      if (lighted)
         return "lightbulbs (bright)";
      return "lightbulbs (dim)";
   }

   string
   my_long()
   {
      if (lighted)
         return "The lightbulb is currently lit.\n";
      return "The lightbulb is more like a dark bulb. Perhaps you can light "
           + "it.\n";
   }

   int
   do_light(string str)
   {
      if (str != "bulb") /* Did the player type 'light bulb'? */
      {
         notify_fail("Light what?"); /* Set message if everything fails */
         return 0;                   /* 0 means we didn't recognise it  */
      }
      if (lighted)
      {
         notify_fail("The bulb is already lighted.");
         return 0;                   /* 0 means we didn't recognise it  */
      }
      lighted = 1;
      write("You light your lightbulb.\n"); /* Message to the player */
      say(QCTNAME(this_player()) + " lights "
        + this_player()->query_possessive() + " lightbulb.\n");
      return 1; /* We recognised the command */
   }

As you can see, I use for the short, plural short and long description weird strings, like "@@my_short". This is called Value By Function Call, and is explained in another part of this document. For the moment it is enough to know that they return the value of the mentioned function.

To add a command to a player I use the add_action() function in the init(). Each time a player "comes near" the object, the command "light" is added. If he "leaves" the vicinity of the object, the command is gone again. Should the player type "light torch", the function do_light() is called with whatever was typed behind the command "light" as argument. Functions that are called by add_action() have to return 0 if they do not recognise the command, or 1 if they handled the command. This allows multiple objects to define the command "light" in our case. The moment some function returns 1, the quest for "light"-commands stops. If no function returns 1, the player will get to see "What?". That is, if no notify_fail() was set. If some string was set, then the player does not get to see "What?", but the set string.

If the player typed "light bulb" and the bulb was not lighted, then we can turn the bulb on. First we give a message to the player that she succeeded, with write(). Write() goes to the player that gave the command, this_player() to be exact. Then we give a message to everyone in the same room as the player, but not to the player herself. For this you can use say(). Now a typical 3.0 thing happens: the macro QCTNAME(). This stands for Query- CapitalizeTheName, and makes sure that people who know this_player() get to see her name, but that people who do not know her get to see something like "The cute happy hobbit" instead. There is a good document that describes the met-nonmet system exellently, I believe it is called /doc/man/general/meet_people.

Because we want to say "his lightbulb" for men and "her lightbulb" for women we ask the possessive article of the player with query_possessive().

After all of this we return 1, because we have recognised and completed executing the command "light". Perhaps it would be a good exercise to enable the players to "darken" the lightbulb as well.

CONTAINER.C


This is a more tricky object, because it does not only have its own volume and weight, but can also hold a specific volume and weight. Let's say you want to make a nightstand:

   /* A nightstand */

   inherit "/std/container"

   #include "/sys/stdproperties.h"

   void
   create_container()
   {
      set_name("nightstand");
      set_short("crummy old nightstand");
      set_adj(({"crummy","old"});      /* extra adjectives, the player    */
                                       /* can now do "exa old nightstand" */
      set_long("The crummy old nightstand will probably not last long.\n");
      add_prop(CONT_I_WEIGHT,     20000);   /* It weighs 20 Kg            */
      add_prop(CONT_I_MAX_WEIGHT, 27000);   /* It can contain up to 7 Kg  */
      add_prop(CONT_I_VOLUME,     14000);   /* It measures 14 Ltr         */
      add_prop(CONT_I_MAX_VOLUME, 17000);   /* It can contain 3 Ltr       */
      add_prop(CONT_I_RIGID, 1);            /* It is a rigid object       */
   }

As you notice, the MAX_WEIGHT and MAX_VOLUME are the WEIGHT and the VOLUME plus what they can contain. The nightstand cannot change shape, so it is defined RIGID. A bag would typically not be rigid, unless it has been washed with too much paste ;-).

MONSTER.C


These babies can do a lot. I myself am not quite aware of all their capabili- ties, but there sure are a lot. I will only show how to make a really simple talking monster. Let's make an ugly nazgul by the name of Dschik.

   /* Dschik, the ugly nazgul */

   inherit "/std/monster";
   #include "/sys/stdproperties.h"
   #include "/sys/ss_types.h"

   void
   create_monster()
   {
      set_name("dschik");
      set_race_name("nazgul");
      set_adj("ugly");
      set_long("Nazguls are ugly, but this one is one of the uglier types.\n");

               /* STR DEX CON INT WIS DIS */
      set_stats(({ 15, 13, 19,  3,  3, 70 })); /* Set his stats */
      set_hp(1000);           /* Heal him fully */
      
      set_chat_time(10);      /* Set the time between speaking    */
      add_chat("Go away!");   /* Add some lines to say (randomly) */
      add_chat("Who are you?");
      add_chat("Go hither, foul creature!");

      set_cchat_time(4);      /* Set some combat chat lines */
      add_cchat("This is that one, fatal mistake!");
      add_cchat("Prepare to die!");

      set_act_time(7);        /* Set some random actions */
      add_act("growl");
      add_act("grin");
   }

This really is the most basic monster you can possible make. There are much more intersting features of monsters, such as making them do sequences of actions, letting them react to the outside world etc., but I won't discuss them here. Notice that I don't set the WEIGHT and VOLUME properties, so they will be set to 70 Kg and 70 Ltr.

This monster is not very tough. Novices begin with their stats between 8 and 18. This is what the stats mean:

STR
Strength, determines how much one can carry or how hard a player can hit.
DEX
Dexterity, determines how good someone is in handling weapons.
CON
Constitution, determines the maximum number of hitpoints.
INT
Intelligence, determines the maximum number of mana.
WIS
Wisdom
DIS
Discipline, determines how quickly someone will wimp out. Someone with low DIS is a chicken, high DIS means she's bold.

ARMOUR.C


Ofcourse you don't want your monsters to run around unprotected against the violent players. Therefore you wish to give them good armours. On the other hand: too many good armours will devaluate all armours. So, be reluctant in making good armours. I think the general rule is this: don't make very good armours, unless you make a very strong monster. Good armours should be tough to obtain. Here is an example of a blue platemail:

   /* A blue platemail */

   inherit "/std/armour";

   #include "/sys/wa_types.h"       /* Weapon and armour types        */
   #include "/sys/formulas.h"       /* Some handy conversion formulas */
   #include "/sys/stdproperties.h"  /* Standard properties            */

   void
   create_armour()
   {
      set_name("platemail");
      set_short("blue platemail");
      set_long("The blue platemail is heavy and doesn't look magical.\n");

      set_default_armour(
         19,          /* Armour class */
         A_BODY,      /* Armour type  */
         0,           /* Armour/weapon modifier list, it can be use to modify */
		      /* the armour class towards different damage types      */
         0);          /* Object that defines wear and remove */

      add_prop(OBJ_I_WEIGHT, 11000); /* 11 Kg. Well, it is a platemail... */
      add_prop(OBJ_I_VOLUME,  1380); /* An iron platemail (see table)     */
      add_prop(OBJ_I_VALUE, F_VALUE_ARMOUR(19) + random(200) - 100);
                  /* Standard formula to calculate value with given ac */
   }

I think this platemail is selfexplanatory... It is a body armour, when worn is provides a protection of ac 19, then the value is set to the standard formula for an armour of ac class 19. The value is randomized a bit, so the players will not be able to judge an armour by the price it has. With this construction, it will be max 100 coins more or 100 coins less than it should be.

WEAPON.C


With these objects you can arm your monsters. The same goes here, as with the armours: don't make too strong weapons, for it will cause inflation. If you have a good reason to make a strong weapon, then don't. There are probably hundreds of other people with similar ideas, and they all have the same conviction that you have. So, stick to bad/mediocre/fairly good weapons. Don't forget: there are more ways to make a good weapon other than making its weapon class high; you could make for example an aluminum sword, which would be very light.

   /* A copper katana */

   inherit "/std/weapon";

   #include "/sys/wa_types.h"       /* Weapon and armour types        */
   #include "/sys/formulas.h"       /* Some handy conversion formulas */
   #include "/sys/stdproperties.h"  /* Standard properties            */

   void
   create_weapon()
   {
      set_name("katana");
      set_short("copper katana");
      set_long("The copper katana looks like it can slice things easily.\n");

      set_default_weapon(
         19,      /* Weapon hit (see /sys/wa_types for max.)      */
         33,      /* Penetration (see same file for max)          */
         W_SWORD, /* Weapon type                                  */
         W_SLASH | W_IMPALE,  /* Damage type                      */
         W_NONE,  /* A one hand weapon, free to choose which hand */
         0);      /* The object defining the (un)wield functions  */

      add_prop(OBJ_I_WEIGHT, 5000); /* 5.0 Kg   */
      add_prop(OBJ_I_VOLUME,  560); /* 0.56 Ltr */
      add_prop(OBJ_I_VALUE,F_VALUE_WEAPON(19) + random(130) - 65);
                  /* Standard formula to calculate value with given hit */
   }

VBFC


This is one of the neatest new possibilities of 3.0. Instead of filling in what a function needs, you fill in "@@my_func@@", where my_func() returns the desired value. This means that my_func() can return a value that is dependant of the status when the function is called. You only need to give the latter "@@" if you want to imbed the string in another string, like:

It is possible to specify arguments that should be passed to the function, as well as an object in which the function should be called. To be more exact, one can do: "@@my_func:object_to_call|arg1|arg2...|argN@@". If you don't provide the object_to_call, this_object() is assumed.

Let's give a simple example; in the previous weapon, I change the set_long line to this:

And I add this function to the code:

   string
   dependant_long()
   {
      /* Check if the environment of the weapon is human */
      if (environment(this_object())->query_race() == "human")
         return "The copper katana has a firm grip in human hands\n";
      else
         return "The copper katana looks like a standard weapon.\n";
   }

Every time someone examines the object, the function dependant_long() will be called. This function then checks if its carrier (if any) is of the human race. If so, another long than usual is returned. Virtually all the standard set_... functions support the usage of VBFC. It is a very powerful feature, which will often come in handy.

Very important is the difference between these two lines:

  1. set_long(dependant_long());
  2. set_long("@@dependant_long");

If you can explain the difference, you have understood VBFC truly. Let me give it a try: (1) sets the long description to whatever dependant_long() returns on the moment that set_long() is done. This will always be "The copper katana looks like a standard weapon.\n". The long description will not change after the set_long(), and even if dependant_long() changes, it will not affect the long description. (2) however, sets the true long description to the string "@@dependant_long", which will make the internal function of an object that really returns the long description evaluate the function dependant_long() each time it is called. This way the long description will be different under different circumstances.

Sometimes VBFC contructions do not work, e.g. sometimes if you use

you will get to see a weird string. In that case, use the function process_string() to fix things:

The functions write() and tell_object() sends their messages exactly as they where given, the weird string you get. The function say() on the other hand handles VBFC nicely.

The whole VBFC effect is destroyed by using process_string, though. To get back at our previous examples:

  1. set_long(dependant_long());
  2. set_long(process_string("@@dependant_long"));
have exactly the same effect. The outcome of the process_string() will always be "The copper katana looks like a standard weapon.\n" and the long description will be set to that string.

It is very easy to use VBFC too much. As a rule of thumb one could use that if the outcome of a function is can be evaluated immediately, one should use a function. When the evaluation has to take place somewhere in the future, VBFC can be used.

Note that VBFC is not implemented in the driver, so if you want your functions also to be able to use VBFC, you will have to make sure that they can. Let's say you want to make a function that turns on a remote-control. You would make the code something like this:

    void
    set_remote(string arg)
    {
         global_remote = arg;
    }

    string
    query_remote()
    {
         return check_call(global_remote);
    }

The check_call() function handles the VBFC part, so you don't have to worry about that yourself.

Skills


There are dozens of skills, and you can find all of them in the file /sys/ss_types.h. This is how you would use skills in your code:

    #include "/sys/ss_types.h"

    /* This function returns 1 if the player can climb good enough */
    int
    try_climb(object pl)
    {
       if (pl->query_skill(SS_CLIMB) > 10)
          return 1;
       else
          return 0;
    }

It is not mandatory, but use the skills that players can have as often as you can. Make object values dependant of the value of SS_TRADING, or make a dog more aggressive when he encounters someone with SS_ANIMAL_HANDLING low. If the skills are used often the players will be more interested in raising them. Another way to use skills is to use tasks. This is more complicated, but gives you more freedom. To explain tasks adequately would take a lot of words, but luckily you can do "man tasks" and read all about them in great detail.

Debugging


If you are new at coding in LPC, you are bound to make errors in your code. If your are not new at it, you will still make errors. They just slip into the code. Luckily the gamedriver offers you a way to debug your programs.

When you have edited an object, say "bulb.c", you want to see your result. To do this, you do "clone bulb". If all goes well, you will find that you are carrying a bulb on you. Check it, to see if it works fine. I.e. can you examine it, can you drop it, can you get it, etc. etc.

If you made a mistake in the code, you will get a message like "Error in line 23: syntax error". Beside that, an error message will be written to a log file. Which log file, depends on the directory that "bulb.c" is in. If it is in your directory, you can do "errlog" to see your error logfile. If the file is in a domain directory, you can do "errlog Domain" to check that errorlog. The last few errors on the bottom are probably the ones that made the error occur. In some muds the errors of the gamedriver are also logged to a file. Those errors tell usually more about the error you made, but also give a lot of other errors that came with it. So, more searchwork for you. On Genesis you can examine that file with the command "tail /lplog".

All errors are of the form:

If you look at the errorlog, usually a number of errors are given. It is wise to solve the highest new one first, because all the lower errors might exist due to it. For example: if you forget one bracket (")"), the gamedriver will claim not to know any variables in other functions below. Also the line number can best be interpreted in a wide sense: "the error must be around that line number". It is very well possible that a forgotten semicolon (";") generates an error a few lines lower.

Let's have a few error messages, and see what causes them:

"d/Genesis/fatty/donut.c line 28:Illegal LHS"
The LHS stands for Left Hand Statement. Line 28 contains an equal-sign ("="), the part on the left is not of the same type as the part on the right. So, you may not make them equal.
"d/Genesis/fatty/donut.c line 12:Newline in string"
On that line, a string was declared, but not closed with a doublequote ("). E.g. set_short("donut);
"d/Genesis/fatty/donut.c line 186:Variable SS_EATING not declared !"
This one is clear. On line 186 a variable is used that has not been declared previously. Don't be misled by the name `variable'. It can well be that you forgot to #include some file that does define it. Since #defines are usually names consisting of only capitals, we probably forgot to #include or #define something here.
"d/Genesis/fatty/donut.c line 20:Return type not matching: "int ""
At that line you return a value that is not of the same type as the function promised to return in its declaration. Either adjust the declaration, or return something of the correct type.
"d/Genesis/fatty/donut.c line 27:Wrong number of arguments to eat"
The function eat() was not declared to get the number of arguments given to it on line 27. That might be an error of you, but perhaps you want to give not always the same number of arguments. In that case you forgot to give the function the following declaration: "varargs eat(...)".
"d/Genesis/fatty/donut.c line 59:Undefined function smear"
There are two possibilities: you forgot to define the function (or mistyped its name), or you did define it, but below the function that calls it. If you don't declare functions with types, the gamedriver is not picky about that last fault, but if you do, you should either move the declaration of the function above the call to it, or place a prototype (e.g. "int smear();") on top of the file.
"d/Genesis/fatty/donut.c line 100:syntax error"
This is a mean one. A syntax error means to say that the the syntaxrules of LPC were violated. This could mean that you forgot a semicolon, bracket or brace ("}") somewhere. It could also mean something completely different, like you put to "+"-signs next to each other, without something like a number or text between them. Look closely at the lines preceding the line of the error.
"d/Genesis/fatty/donut.c line 85:Illegal to redefine nomask function"
The name of the function on that line has already been used before. Probably in the object it inherits. Whoever wrote that object didn't want us to redefine that function, and declared it "nomask".
"d/Genesis/fatty/donut.c line 85:Missing type for argument"
If the functions are declared with types, their arguments should also have types. E.g. "void foo_gnu(object gnat)".
"d/Genesis/fatty/donut.c line 93:Bad type for argument 1 ( object vs string )
The function called in line 93 was declared to receive a different type of argument then what was given to it. E.g. "foo_gnu("bar");", where foo_gnu() was defined as above.

Interesting documents:


You can do "man <subject>" if you want to, "more /doc/man/general/<subject>" is also possible.

/doc/man/general/
LPC
A more exact definition of the language.
meet_people
Explains the usage of the met/nonmet feature of 3.0.
experience
Explains how experience works.
guilds
Rules about building a guild.
healing
Rules about healing.
spells
Rules concerning spells.
tasks
Explains how to use tasks.
values
How to give values to objects.
xp_scale
How much experience can one give for a quest?

Constants for different metals


The following table is of course utterly silly, because nobody is really going to use it. You could use it to get an indication of the weight differences, though. It is not obliged to make things look extremely real, but to get a feeling, here are the actual values for the Rho of several kinds of metal, where Weight = Volume x Rho.
MetalRhoAlloyRho
Aluminum
Chrome
Gold
Lead
Magnesium
Copper
Platinum
Tin
Iron
Silver
Zinc
2.7
7.1
19.3
11.3
1.7
8.9
21.4
7.3
7.9
10.5
6.9
Bronze
Cast iron
Brass
Steel
Stainless steel
8.9
7.3
8.5
6.9
7.8

Example: If you think your gold bar measures about 1.5 ltr, it will weight about 1.5*19.3 = 29 Kg.