Lesson 1A (Introduction to LPC/Beginning Objects)

LPC is a small, object oriented type C language developed by Lars Pensj| for
LP-MUD, a Multi-User Dungeon environment under many UNIX systems.  Since the
premise of this language is based on C, it contains many of the syntactical
qualities of C, but also maintains a large set of functions capable of
performing many actions inside the game.  The objective is to begin to look at
LPC as a way of creating objects, rather than specific items, so that new
coders can begin to experience the way LPC actually works.  Rooms, weapons,
monsters, armor, and whatever creation you can think of, even yourself, are
objects.  LPC allows you to create, modify, delete, and reproduce these objects
in almost any manner you choose.  This tutorial will guide you through some of
the basic principles of LPC, and how to begin to design objects of your own
choosing.  All objects will be stored in .c files, using the 'ed' editor or
some uploading method of your choice.

Objects can be created easily, using some of the built-in functions that LPC
allows.  Here are a few of them; your local LPC site might have more available
for you.  From here on out, the functions listed below are RESERVED functions,
in that they serve a special purpose.  They should not be used in any manner
except for what their purpose entails.

NOTE: Always look in /doc/efun and /doc/lfun for a list of functions to use
      (These are the functions for 2.4.5...They may or may not be available
      for you to use.)

-------------------- LIST OF FUNCTIONS FOR YOUR MUD -----------------------
add_action      add_money      add_verb        add_weight       allocate
attacked_by     call_other     call_out        can_put_and_get  capitalize
cat             catch          catch_tell      clear_bit        clone_object
command         create_wizard  creator         crypt            ctime
destruct        drop           ed              enable_commands  environment
exit            explode        extra_look      extract          file_name
file_size       find_living    find_object     find_player      first_inventory
get             heal_self      heart_beat      hit_player       id
implode         init           input_to        intp             living
log_file        long           lower_case      ls               move_object
next_inventory  notify_fail    objectp         parse_command    people
pointerp        present        previous_object query_attack     query_auto_load
query_gender_string            query_idle      query_info       query_level
query_money     query_name     query_npc       query_value      query_verb
query_weight    random         remove_call_out reset            restore_object
save_object     say            set_bit         set_heart_beat   set_light
set_living_name short          shout           show_stats       sizeof
sscanf          stop_fight     stop_wielding   stringp          tell_object
tell_room       test_bit       this_object     this_player      time
transfer        users          write           write_file
---------------------------------------------------------------------------

Let's take an object of any kind, and build it from the ground up.  In order to
build something trivial, say, a small rose, we need to use certain functions
given to us by LPC.

To start, we will first need a description of the rose.  Two of the functions
from the list above that we will need are short() and long().  As a player of
LP-MUD, you may recognize these as the verbose and brief descriptions of an
object.  short() is the short description of an object, and returns the string
for that description, whereas the long() is a more verbose description of the
object, as is seen when someone examines it.  Remember that functions will for
the most part return a value to its caller.  In the case of short() and long(),
we are returning strings.  So, back to our rose example, we might have in our
rose.c file:

short()
{
    return "a small red rose";
}

long()
{
    write("This small red rose is very beautiful to look at.\n");
}

The two functions above are coded as follows.  Let's take short() for
starters.  short() is a way of defining a function in LPC, where 'short' is the
name of the function, and the information contained between the { and the } is
the action list for that function name.  The () is what is referred to as a
parameter list.  In this case, because there are no variables in between the
( and the ), there are no paramters.  short() is a RESERVED function, in that it
serves a special purpose.  short() returns to the user a string that will
contain a brief description of the item.  long(), on the other hand, uses
write() to tell users about the object.  We write out information to the person
holding the object, and tell them something about the rose.  In this case, we
write out that the rose is beautiful to look at.  By using write(), we can tell
users various things.  long() is another RESERVED function, and is called
whenever someone 'exa' or 'examine's the object.

So now we see how to make the objects with description.  But other questions
might pop up from this.  How do we get the object?  Can we set a price on the
object?  What about its weight?  And how to we assign a name to an object?

Let's finish off our object, and take a look at what it does, breaking down
each function as necessary.

/* Check for a name of the object */
id(str)
{
    return str == "rose";
}

/* The short description of the object */
short()
{
    return "a small red rose";
}

/* The long description of the object */
long()
{
    write("This small red rose is very beautiful to look at.\n");
}

/* Make sure that the object can be picked up */
get()
{
    return 1;
}

/* Set a weight of 1 on the object */
query_weight()
{
    return 1;
}

/* Set a value of 10 coins on the object */
query_value()
{
    return 10;
}

With all of the above code, you have just created a small rose!  To complete
our understanding of the code, we need to understand what get(), id(str),
query_value(), and query_weight() do.  get(), as you might guess, simply allows
the item to be picked up.  So, since get() is a RESERVED function, we can note
that by returning 1 to the caller, as we do above, we are saying that yes,
someone can pick up this object.  If we return 0, then we are saying that this
item cannot be picked up.  id(str) is another RESERVED function,
which serves to check if a string passed to id(str) is one of the names of
the function.  When we say (return str == "rose") what we are really doing
is saying that if the string passed to id(str) is "rose", then return 1,
or true; otherwise, return 0, or false.  It is simply checking to see that the
strings, when compared, are identical.  In LPC (as well as C), 0 is used to
represent a false condition, while 1 (or any other non-zero value) is used
to represent a true condition.  To add other strings to the comparison,
separate them with the 'or' sign (||).  For example, this is also a valid
setting for the id(str) function:

/* Add a name to the object */
id(str)
{
    return str == "rose" || str == "red rose";
}

If the caller of id(str) passes "rose" or "red rose", the function will return
1 back to the user, or 0 otherwise.  query_weight() and query_value() are two
functions which also return values to their caller, for weight and value
respectively.  We set a weight on the object so that someone carrying too much
can't pick up more than possible, and we set a value on the object so that they
can sell it for a certain amount of gold coins.  In the example above, we have
set the value of the object to 10 coins, and the weight on the object to 1.
Weights and values will vary according to both your personal tastes, and
specifications for your MUD.

Once this is saved in a file, we can then update the file, and clone it, and it
will appear in our inventory.  Assuming that we are in the proper directory,
and the code is in a file called 'rose.c', we can do the following:

> load rose
Ok.
> clone rose
Ok.
> i
a small red rose.
> exa rose
This small red rose is very beautiful to look at.
> sell rose
You get 10 gold coins.
>

At this point, it is best to explain what the differences between 'load',
'update', and 'clone' are.  You will be using these commands quite frequently,
especially as you make changes to your files.  'load' will load an object into
memory.  It will take the .c file your specify, and load an instance of it into
memory.  For example, if I type in 'load rose', it will load a copy of rose.c
into memory.  If a copy already exists, it does nothing.  When you 'update' a
file, you are removing the old instance of the file in memory.  That way, the
next time you decide to 'load' or 'clone' that object, it will put a new
version into memory.  Clone will create an instance of the object from memory.
If there isn't a copy in memory, it loads one up.  A scenario would be the
following:  First, you edit rose.c, then load it.  After that, you clone it.
You will find a copy of the rose in your inventory.  But say you made a new
version of rose.c.  If you don't 'update rose', then you will simply be getting
a copy of the old instance of rose.c, and not your newly edited one.  Once you
'update rose', you can 'load rose', then 'clone rose', or you can simply 'clone
rose' (Remember that 'clone' will also load the file into memory, if one
doesn't exist.)

Now let's get a bit more advanced with our object.

As you will notice, the ability to create objects has been made very easy by
the designers of LPC, and your usage of the RESERVED functions will enhance the
objects you create.  In order to spice up the rose we have created, we will
append these two functions onto the end of our rose.c file:

/* Set some initial actions for our object */
init()
{
    add_action("smell_rose", "smell");
}

/* smell_rose() -- smell the rose.  An assigned action from init() */
smell_rose(str)
{
    if ((!str) || !id(str))
    {
        return 0;
    }
    write("You smell the sweet fragrance of the rose.\n");
    return 1;
}

These lines will allow us the capability of actually 'smell'ing the object.  In
that sense, we are now assigning 'actions' to the object.  This is an important
point, in that everything in LPC revolves around an action of some sort.  A
person cannot move, walk, talk, breathe, or even exist if actions were not
allowed.  In the example above, we have added a function called init().
init(), as you will find out in future lessons, is one of the key functions in
LPC.  It will allow us to define actions and functions associated to those
actions.  In the previous example, we have set up add_action("smell_rose",
"smell").  "smell_rose" is the name of the function that will perform the write
to the user, and "smell" is the action.  So, when we type in "smell" in any
sense, the add_action() will catch that word, and try to perform some action in
the smell_rose() function.

Within the smell_rose() function, we need to check for a couple of things.
First, we need to make sure that the string that is being sent to the
smell_rose() function is actually "rose".  If it isn't, then we want to
return 0 back to the user, as this function is useless.

NOTE:  By setting up an add_action() function, we are saying that we catch
       the word "smell", but we do not pass it to the associated function.
       So, in other words, when we type in "smell rose", only "rose" goes to
       smell_rose().  And likewise, if we type in "smell     rose", then
       "    rose" goes to smell_rose().


> update rose
players/you/rose will be updated next reference.
> clone rose
Ok.
> smell rose
You smell the sweet fragrance of the rose.
> smell    rose
What ?
> smell
What ?
>

>From the example above, we notice that we must say "smell rose" properly
in order to get the function to work.  We will deal with how to handle improper
typing on the users side later, but for now, we will assume that the user
holding the object will not add extra spaces when typing in "smell rose".

There you have it!  One complete object.  Most objects from this point on in
the documentation will now become easier to develop, because we are no longer
think of specific objects, but rather an object in general.  This will become
very clear in the next lesson, when we begin to talk about inheritance, then
objects as rooms, and how they will take shape.

                  EXAMPLES TO USE AND STUDY FOR LESSON 1A
-------------------------------------------------------------------------------
Example #1 -- A small rose.
-------------------------------------------------------------------------------
/*
// Rose -- A small rose example
// By Matt D. Robinson
*/

/* Check for a name of the object */
id(str)
{
return str == "rose" || str == "red rose";
}

/* The short description of the object */
short()
{
return "a small red rose";
}

/* The long description of the object */
long()
{
write("This small red rose is very beautiful to look at.\n");
}

/* Make sure that the object can be picked up */
get()
{
return 1;
}

/* Set a weight of 1 on the object */
query_weight()
{
return 1;
}

/* Set a value of 10 coins on the object */
query_value()
{
return 10;
}

/* Set some initial actions for our object */
init()
{
add_action("smell_rose", "smell");
}

/* smell_rose() -- smell the rose.  An assigned action from init() */
smell_rose(str)
{
    /*
    // If the function obtains no string, or the string is not "rose", then
    // we return 0, or a false condition, to the caller.
    */
    if ((!str) || !id(str))
    {
        return 0;
    }
    write("You smell the sweet fragrance of the rose.\n");
    return 1;
}
-------------------------------------------------------------------------------
Example #2 -- A pair of cymbals.
-------------------------------------------------------------------------------
/*
// Cymbals -- Items to clang together
// By Matt D. Robinson
*/

/* Check for a name of the object */
id(str)
{
return str == "cymbals";
}

/* The short description of the object */
short()
{
return "a pair of shiny cymbals";
}

/* The long description of the object */
long()
{
    write("This is a pair of shiny cymbals.  Try to 'clash' them together.\n");
}

/* Make sure that the object can be picked up */
get()
{
return 1;
}

/* Set a weight of 2 on the object */
query_weight()
{
return 2;
}

/* Set a value of 40 coins on the object */
query_value()
{
return 40;
}

/* Set some initial actions for our object */
init()
{
add_action("clash_cymbals", "clash");
}

clash_cymbals(str)
{
    /*
    // If the function obtains no string, or the string is not "cymbals", then
    // we return 0, or a false condition, to the caller.
    */
    if ((!str) || !id(str))
    {
        return 0;
    }
    write("You clash the cymbals together, making a lot of noise!\n");
    return 1;
}
-------------------------------------------------------------------------------

Please refer to /doc/efun and /doc/lfun for more information about functions
mentioned in this documentation, or, ask your local LPC guru for more help.

Lesson 1B (Objects Using Inheritance)

So far we've discussed how to make objects simply using RESERVED functions
given to us.  Now we want to take this knowledge, but use it so that we save
the amount of resources used by the MUD. In order to keep most MUDs running
for long periods of time, using only small amounts of memory, inheritance was
developed, allowing a single object to simply take attributes on for a given
object.  This might sound a bit confusing, but we'll explain it as we go along.
Please refer to this definition as we continue.

First, let's take a small example of an object, using /obj/treasure.c as the
inherited item, and make the small rose (without the 'smell' function.):

inherit "obj/treasure";

reset(arg)
{
    if (arg) return;
    set_id("rose");
    set_short("a small red rose");
    set_long("This small red rose is very beautiful to look at.\n");
    set_weight(1);
    set_value(10);
}

That's it!  It's very simple to use inheritance, and it makes the coding we
do much neater.  Now, let's explain what this does, in more detail, in a line
by line fashion.

First, let's take the line

inherit "obj/treasure";

What this does is make the object /obj/treasure.c our inherited object.
This object has a bunch of generic functions that allow it to change attributes
easily (such as set_id(), set_value(), etc., but we will explain those how
those work in a minute.).  Let's call this file newrose.c, and save it in a
file in our LPC directory.  Think of an attribute as a 'feature' of an
object, like a name, or the short description, or the value, etc.  These
attributes are changable in your newrose.c file, and we will do just that.
But first, let's explain what reset(arg) is.

The object we are creating is identical to the one we first had (rose.c),
but we only need one function to do the work for us.  reset(arg) is the only
function we need in order to create our object.  What reset(arg) does is allow
an item to perform actions when a reset occurs.  This function is called with
different values all of the time, and always with the value of 0 on creation.
reset(arg) is a RESERVED function, in that it is designed with a special
purpose in mind, that being to perform actions when it sees arg equal to a
certain value.

So, what we need to do is program all of our code to do whatever actions it
plans on when we see a 0 as the value of arg in reset(arg).  So the line of
code:

if (arg) return;

Simply checks to see if arg is 0.  If it is not (if it is a non-zero value),
then the function just returns.  If the value is 0, then it continues down
through the function.  We need this line so that we do not reset all of the
attributes each time a reset occurs.

Now we finally have the following calls in our reset(arg) function:

set_id("rose");
set_short("a small red rose");
set_long("This small red rose is very beautiful to look at.\n");
set_weight(1);
set_value(10);

Each of these is easy to explain, so I won't go into too much detail, except
to note their association to the functions in our rose.c file.

set_id(string)     -- This will set the value of id(str) that is to be whatever
                      string you choose.  In our example, we chose "rose" to be
                      the identifier string.

set_short(string)  -- This will set the short description for the object.  The
                      function it associates to is short().

set_long(string)   -- This will set the long description for the object.  The
                      function it associates to is long().

set_weight(int)    -- This will set the weight of the object.  The function it
                      associates to is query_weight().

set_value(int)     -- This will set the weight of the object.  The function it
                      associates to is query_value().

Now that we understand how to make an object using inheritance, let's show how
to add actions to the object, by taking advantage of our inheritance, and using
the init() function.  In this way, we can add the smell_rose() function from
our previous lesson.

Let's add these two functions to our object:

/* Set some initial actions for our object */
init()
{
    add_action("smell_rose", "smell");
}

/* smell_rose() -- smell the rose.  An assigned action from init() */
smell_rose(str)
{
    if ((!str) || !id(str)) return 0;
    write("You smell the sweet fragrance of the rose.\n");
    return 1;
}

After adding this to our newrose.c file, let's update it, clone it, and try
it out.  After you have done this, you'll notice that it works identically to
the rose we made in the first lesson, except that this version is smaller,
easier to code, and takes up less memory.  The objective here is to begin to
show how all objects, even the most complex ones, can be designed around many
inherited objects.

In future lessons, we will begin to review how inheritance is used in many
different ways, in armor, weapons, monsters, and rooms.  All of your coding
should revolve around inheritance, and putting it to use.  But to start, we
simply want to show you how it works, and note that you should use it whenever
possible.

                 EXAMPLES TO USE AND STUDY FOR LESSON 1B
-------------------------------------------------------------------------------
Example #1 -- A small rose.
-------------------------------------------------------------------------------
/*
// Rose -- A small rose example
// By Matt D. Robinson
*/

/* First, we inherit the treasure file for a generic object */
inherit "obj/treasure";

/* Then we call reset with the value of 0 on initial creation */
reset(arg)
{
    if (arg) return;
    set_id("rose");
    set_short("a small red rose");
    set_long("This small red rose is very beautiful to look at.\n");
    set_weight(1);
    set_value(10);
}

/* Set some initial actions for our object */
init()
{
    add_action("smell_rose", "smell");
}

/* smell_rose() -- smell the rose.  An assigned action from init() */
smell_rose(str)
{
    if ((!str) || !id(str)) return 0;
    write("You smell the sweet fragrance of the rose.\n");
    return 1;
}
-------------------------------------------------------------------------------
Example #2 -- A pair of cymbals.
-------------------------------------------------------------------------------
/*
// Cymbals -- Items to clang together
// By Matt D. Robinson
*/

/* First, we inherit the treasure file for a generic object */
inherit "obj/treasure";

/* Then we call reset with the value of 0 on initial creation */
reset(arg)
{
    if (arg) return;
    set_id("cymbals");
    set_short("a pair of shiny cymbals");
    set_long("This is a pair of cymbals.  Try to 'clash' them together.\n");
    set_weight(2);
    set_value(40);
}

/* Set some initial actions for our object */
init()
{
    add_action("clash_cymbals", "clash");
}

clash_cymbals(str)
{
    if ((!str) || !id(str)) return 0;
    write("You clash the cymbals together, making a lot of noise!\n");
    return 1;
}
-------------------------------------------------------------------------------

Please refer to /doc/efun and /doc/lfun for more information about functions
mentioned in this documentation, or, ask your local LPC guru for more help.

Lesson 2A (Building Rooms From Scratch)

In order to now get a grasp on how objects work as rooms, we will begin
defining a room from the ground up.  Most LP-MUD games have a standard file
that is normally included into your room file so that you can build object
rooms easier. But in this case, we want to begin from ground level and show
you how to develop rooms using a small set of functions, and how to build from
there.

To start, you need to make sure you understand the ideas behind lesson 1, so
that lesson 2 can discuss certain functions easier (such as short(), long(),
and init().)  For building a room, the number of functions will depend on the
number of exits, and any built-in actions you wish to allow.

Here, we will show an object room's code, and explain it to you in more
detail.

/* Make sure that the room has light on the first call of reset(arg) */
reset(arg)
{
    if (arg) return;
    set_light(1); return;
}

/* Set the short description on the object */
short() { return "A Small Closet"; }

/* Set the long description on the object */
long()
{
    write("This is a small closet, with lots of side panels.\n\n");
    write("There are two obvious exits: north and south.\n");
}

/* Set some initial actions available to this object */
init()
{
    add_action("move_north", "north");
    add_action("move_south", "south");
}

/* North will take us to the standard shop */
move_north()
{
    this_player()->move_player("north#room/shop");
    return 1;
}

/* Sorth will take us to the church */
move_south()
{
    this_player()->move_player("south#room/church");
    return 1;
}


Breaking down the code, we find that we have added a number of functions to our
current knowledge of LPC, and with luck, these will soon become integral parts
of your LPC vocabulary.  First, let's look at reset().

In reset(arg), we are passed "arg", which is the number of the current reset.
When the object is first made, "arg" is 0, and by that, we can use it as a flag
to check when the object is created or not.  Inside of the function, when we
say "if (arg) return;," we are saying that if arg is something not equal to
zero, then we want to return back to the caller.  On the first reset() call,
however, since "arg" will be 0, we will go past this first condition, and
execute set_light(1).  This will set the light condition on the object to 1.
Normally, all objects have a light value of 0; but because rooms are actual
environments to work in, the light value must be set to 1 (unless you want the
room to be dark, which might be the case.)  After setting the light in the room
to 1, we return back to the user.

Since we already understand what the short() and long() functions do, and we
have an understanding of how init() and add_action() works, we will explain
them only briefly.  The RESERVED function init() will set up the initial
actions in the room, that someone will perform when they first walk in.  In
this case, we are allowing "north" and "south" to be legitimate actions.  They
will call the functions move_north() and move_south(), respectively.  Within
these functions, however, we need to look at how the moves actually occur.
We call a special function, called move_player(), which does the moving for
us.  What might not seem familiar is the -> format.  What this does is specify
object->function() format.  This is a very neat and helpful way of referring
objects to functions.  So when we say:

this_player()->move_player("out#room/church");

We are saying that this_player() is the player to move, and that any additional
arguments (In this case, "out#room/church") are sent inside of the ( and ) for
the function call.  Another example might be:

level = this_player()->query_level();

Where query_level() is the function, and this_player() is the object.  The
function returns the level of this_player(), and sets level (the variable)
to that value.

Simply remember that this_player() refers to the caller of the function.  If I
type in "north", then inside of the move_north() function, I am this_player().
move_player(), another RESERVED function, with parameters "DESTINATION#ROOM",
where DESTINATION is the direction in which the person is leaving, and ROOM is
the name of the room to transfer the person to.

Now, let's see what happens when we save the file as myroom.c, load, and
goto this room.

> load myroom
Ok.
> goto myroom
This is a small closet, with lots of side panels.

There are two obvious exits: north and south.
> n
You are in a shop. You can buy or sell things here.
Commands are: 'buy item', 'sell item', 'sell all', 'list', 'list weapons'
'list armours' and 'value item'.
There is an opening to the north, and some blue light in the doorway.
To the west you see a small room.
There are two obvious exits, south and west.
> goto myroom
This is a small closet, with lots of side panels.

There are two obvious exits: north and south.
> s
You are in the local village church.
There is a huge pit in the center, and a door in the west wall.
There is a button beside the door.  This church has the service of
reviving ghosts.  Dead people come to the church and pray.
There is an exit to south, and a small door to the clinic to the east.
> brief
Brief mode.
> goto myroom
A Small Closet.
>

As you can tell, the room works just as any room, but with exits to the church
and shop.  You can make these explicit exits to the church and shop by
changing all occurances of "north" with "shop", and "south" with "church" in
the previous program example.  This will allow "church" to exit to church, and
"shop" to exit to shop.

But let's take the room idea a bit further.  What if we want something special
in our room, where something would occur if we searched the room?  Let's try
this example on for size:


/* Make a variable for finding an exit */
int search_count;

/* Make sure that the room has light on the first reset */
reset(arg)
{
    if (arg) return;
    set_light(1); search_count = 1; return;
}

/* Set the short description on the object */
short() { return "A Small Closet"; }

/* Set the long description on the object */
long()
{
    write("This is a small closet, with lots of side panels.\n\n");
    write("There is one obvious exit to the church.\n");
}

/* Set some initial actions available to this object */
init()
{
    add_action("search_room", "search");
    add_action("move_church", "church");
}

/* Add the north action to the room */
move_church()
{
    this_player()->move_player("to the church#room/church");
    return 1;
}

/* Allow the room to be searched. */
search_room()
{
    object ob;

    if (search_count == 3)
    {
        write("You find a torch in the room!\n");
        search_count = search_count + 1;
        ob = clone_object("obj/torch"); transfer(ob, this_player());
        return 1;
    }
    else
    {
        search_count = search_count + 1;
        write("You find nothing in the room.\n"); return 1;
    }
}

Even though the code is a bit bigger, we aren't doing much more than the first
example from this section.  We have added a function called search_room() that
allows us to hide an object in the room until a player searches three times.
(We initially set the search_count to 1.)  If you look at the function
search_room(), you will notice that when the search count is equal to 3, we
then clone up a new object, and move the object to this_player(), which is the
person that called the function.  Those are functions to be mentioned later in
other lessons, as this example serves only to show how new functions can be
added to a room.

Obviously there are many other examples that could be used for add-on functions
to rooms, but this is an easier one to work on.  Each wizard in their own
infinite imagination can create new functions for each room depending on their
interests.

In the next lesson, we will more fully develop our ideas by using std.h, which
is an important included file for building rooms, and also using inheritance
with room/room, which is something also very important.

Please refer to /doc/efun and /doc/lfun for more information about functions
mentioned in this documentation, or, ask your local LPC guru for more help.

Lesson 2B (Building rooms with std.h)

One method of creating rooms, which has not been covered so far, is that of
using standard default files for creating a new room.  A pretty common one,
that of std.h, is used quite frequently on most LP-MUD games, and it can almost
be stated that it is by far the most dominating type of room.  The format is
similar to this below:

#include "std.h"

ONE_EXIT("room/church", "church",
         "A Small Closet",
         "This is a small closet, with lots of side panels.\n\n", 1)


In this scenario, we are taking the code from the first example and putting it
in the normal std.h format.  As you can see, the code is considerably smaller,
and less hassle to the writer.   The types of rooms for most LP-MUD std.h files
range from ONE_EXIT, TWO_EXIT, all the way to EIGHT_EXIT (sometimes even higher
than that.)  The first two arguments are the ROOM and DESTINATION, followed by
the short() message, then the long() message, and finally finished with the
light code for the room (either 1 or 0).

Because most std.h files are built well, there is even the ability to add the
search function to a room with minimal effort.  Here is the same code for the
second example from this lesson, with the search function added.

#include "std.h"

int search_count;

#undef EXTRA_RESET
#define EXTRA_RESET extra_reset();

extra_reset(arg)
{
    if (arg) return;
    search_count = 1;
    return;
}

#undef EXTRA_INIT
#define EXTRA_INIT\
    add_action("search_room", "search");

ONE_EXIT("room/church", "church",
         "A Small Closet",
         "This is a small closet, with lots of side panels.\n\n", 1)

/* Allow the room to be searched. */
search_room()
{
    object ob;

    if (search_count == 3)
    {
        write("You find a torch in the room!\n");
        search_count = search_count + 1;
        ob = clone_object("obj/torch");
        transfer(ob, this_player());
        return 1;
    }
    else
    {
        search_count = search_count + 1;
        write("You find nothing in the room.\n");
        return 1;
    }
}

We need to explain some of the standard macro definitions in the std.h file
to show how reset(arg) and init() actions are added to the code.  Certainly,
the std.h format is much nicer than writing a simple room, and makes the
coding much more uniform.  The EXTRA_RESET definition needs to be #undef'd,
or undefined, and we redefine the EXTRA_RESET definition to be extra_reset(),
so that we call our own function, as well as the one defined by the ONE_EXIT
macro.

This might seem confusing, but think of it as adding an additional reset(arg)
to our room.  We want this extra function so that we can add stuff that is not
normally included in the reset(arg) provided by std.h.  If you have not guessed
already, you will see that EXTRA_INIT follows the same pattern:  We #undef
the EXTRA_INIT that already exists, #define a new EXTRA_INIT to use, and
add functions accordingly.  Each line (except for the last) must be preceded
by a \ (As is shown in the example above.)  All we are doing is adding an
additional add_action() to the room.  The action we add in this case is
the "search" string, which in turn will call our newly created search_room()
function.

The last thing we need to cover is how to put objects in room.  It is important
to note that we normally put objects in room only on reset(arg), or under some
special case (like the one where a person searches and finds something.) We
need to watch how we perform the reset(arg), so as not to mess up our object
placement.

Let's use the code from the first std.h example, and add code to our
extra_reset() function.  We will show how to clone up an object, and move it
into the room when we reset for the first time..

#include "std.h"

#undef EXTRA_RESET
#define EXTRA_RESET extra_reset();

ONE_EXIT("room/church", "church",
        "A Small Closet",
        "This is a small closet, with lots of side panels.\n\n", 1)

extra_reset(arg)
{
    object ob;

    /* if arg is something other than 0, we simply return */
    if (arg) return;

    /* we look for a torch in the room.  if we do not find one, make one, */
    /* and put it in the same room.                                       */
    if (!present("torch"))
    {
        ob = clone_object("obj/torch");
        move_object(ob, this_object());
    }
    return;
}

If you take a look at this room, you'll notice that we have a larger
extra_reset() function than before.  Inside of this function, we first
check to see if arg is something other than 0.  If it is, we return.

NOTE:  This should ALWAYS be the first line of any reset() function you
       have.  Only under rare cases would you have something different.

If we pass through the arg check, we then look to see if there is an
object in the room with an id of "torch".  With present(), a RESERVED
function, we can check to see if there is an object in the present
environment.  At this point, we are introducing something not mentioned
before: this_object().  This function is designed to refer to the code
you are working on.  So, when we say this_object() in this code, we really
mean the room we are in.  In the last six code lines of the previous example,
notice how we first check to see if an item called "torch" is in the room.
If there is not (by saying !, which means 'not'), we then clone_object()
the /obj/torch object.  clone_object(), a RESERVED function, will clone
up objects from code files given to it.  If you look at /obj/torch.c, you
will notice that it will create a torch that will can be lit or extinguished.
We clone up the object, and then move_object() the object "ob" into the
present room, or this_object() (as just mentioned.)  We return as soon
as we call this function.

Please refer to /doc/efun and /doc/lfun for more information about functions
mentioned in this documentation, or, ask your local LPC guru for more help.

Lesson 3A (Making Monsters)

Now that we have fully covered what we want to do with basic objects, and rooms
as a whole, let's now concentrate on making other items, so that we can more
fully expand the rooms into parts of a castle or an area you want to design.
Monsters are the basic topic of this paper, but we will also discuss how to add
weapons and armor to your new creations.

I will continue to stress the importance of using inheritance in all of your
code.  The example we will use for this lesson is the following:

inherit "obj/monster";

reset(arg)
{
    if (arg) return;
    set_name("dragon");
    set_short("a dragon");
    set_long("This is a big dragon, with acid breath!\n");
    set_level(15);
    set_hp(500);
    set_ac(15);
    set_wc(19);
}

There's the monster!  The monster is written to be very generic, so that we
can understand what the function calls in reset(arg) actually mean, before
going further by giving more advanced function calls for monsters.  Note that
a monster is a living thing; it uses a file called "living.h" in order to take
on living attributes.  Because of this, it has many other variables that should
be set, so when the monster is alive, the values are correct.  The monster is
similar to a player, but is an NPC (non player character), instead of a PC
(player character).  Because of this similarity to players, the monster can
perform many actions similar to those of a player.  It is important to remember
this, as we will be setting values, such as the level, or the hit point level,
etc., things that would normally be set on a player.  Let's go through each
function call, explaining what each does.

set_name(string)   -- Sets the id(str) of the monster.

set_short(string)  -- Sets the short() of the monster.

set_long(string)   -- Sets the long()  of the monster.

set_level(int)     -- Sets the level of the monster.  Normally all monsters
                      made have this value set somewhere between 1 and 19,
                      although there are sometimes exceptions.

set_hp(int)        -- Sets the number of hit points the monster has.  Remember
                      that some MUDs have specific levels at which you need to
                      keep the hit point level.  Always try to find out what is
                      a fair number to set this value to.

set_ac(int)        -- Sets the armor class of the monster.  This will determine
                      how well the monster can take blows from the player who
                      is killing it.

set_wc(int)        -- Sets the weapon class of the monster.  This will set how
                      good the monster's hitting is against its opponent.  This
                      is very similar to how weapons classes for players work,
                      so be careful not to set it too high.

After going through all of these functions, we now understand how the basics
of a monster work.  What we want to do now is to expand that knowledge, by
taking advantage of many function calls that exist for monsters.  When we use
all of these function calls correctly, monsters are simple and fun to make.

Let's continue by adding more functions to the list of those that we already
know.  We will add the following lines to our dragon:

    set_aggressive(1);
    set_whimpy();

These two lines are easy to explain, and are used quite often on many of the
monsters that you have seen:

set_aggressive(int) -- Sets whether the monster will aggressively attack a
                       player that walks into the room.  If the value given to
                       the function is 1, then the monster will be aggressive,
                       and with a 0 the monster will be non-aggressive.

set_wimpy()         -- Sets whether the monster will "wimpy" away (run away)
                       when it reaches about %20 of its hit points.  This is
                       very similar to a player's wimpy setting.

Now that we have written a basic monster, who can run away, and attack a player
on sight.  Now, we want the monster to be able to cast spells, so that every
once in a while, the player will be blasted by a spell of some sort.  What is
nice about monsters is that adding spells and stuff is easy.  Let's add a bit
more code to our dragon:

    set_chance(30);
    set_spell_mess1("The dragon breathes hot acid on you!");
    set_spell_mess2("The dragon has struck you with a mighty blow.");
    set_spell_dam(15);

The code looks easy enough, but let's go into further explanation on what these
functions actually do, so there isn't a misunderstanding.

set_chance(int)         -- Sets the percentage of chance per hitting round that
                           the monster will cast a spell.  In our example,
                           there is a 30% chance that the monster will cast a
                           spell at each hit round.

set_spell_mess1(string) -- Sets the first spell message that a player will see
                           when the monster casts a spell.  If the programmer
                           has also created a set_spell_mess2(), then it will
                           be random which message is chosen.

set_spell_mess2(string) -- Sets the second spell message that a player will see
                           when the monster casts a spell.

set_spell_dam(int)      -- Sets the amount of damage to be done by the monster
                           when the spell is cast.  In the example above, when
                           the monster does cast a spell, 15 hit points will
                           be taken away from the player.


A couple of other nice functions to have are:

set_alias(string)       -- Sets an alias for the monster (it is an additional
                           name for id(str) to choose from.)

set_alt_name(string)    -- Sets yet another alias for the monster.


In order to keep the beginner documentation simple, I won't be going into
detail about how catch_tell() functions work (If you don't know what I am
referring to, all the better), but I will explain a few other nice functions
that exist for monsters.  Remember that your MUD may not use these in the same
manner as I am using them here; differences between American and European
versions of monster.c are drastic in some places.  Take for example, chat
and attack chat strings.  When a monster is sitting in a room with you, he
can sometimes talk away, without being aggressive.  But when you hit him, all
of a sudden the messages change, to something different from what you saw.
For those MUDders who want a prime example, take a look at Harry.  He's an
advanced monster, who talks away, with different messages while aggressive
and non-aggressive.

Let's add the following lines of code to our file, and remove the line with
set_aggressive(1) in it.  (That way, the monster does not attack us when we
walk into the room.)

    set_chat_chance(45);
    load_chat("The dragon says:  How do you do.");
    load_chat("The dragon says:  A nice day we are having.  Don't bug me.");

    set_a_chat_chance(35);
    load_a_chat("The dragon screams:  Why are you attacking me!");
    load_a_chat("The dragon ponders why you are hitting him.");

Just by looking at the code, it is somewhat easy to understand what we want to
do, but let's describe in more detail what these functions are doing, so that
we understand how they work.  Remember that chat is for peaceful talking, while
a_chat is for attacking chat messages.

set_chat_chance(int)   -- Sets the percentage of chance at which the monster
                          will speak up and say something.  This chat is a
                          chat when the monster is NOT being attacked.

load_chat(string)      -- Sets a string that will be said to the player in the
                          room.  If there is more than one load_chat() call,
                          then one of the strings will be chosen at random.

set_a_chat_chance(int) -- Sets the percentage of chance at which the monster
                          will speak up and say something.  This chat is a
                          chat when the monster IS being attacked.

load_a_chat(string)    -- Sets a string that will be said to the player in the
                          room.  If there is more than one load_chat() call,
                          then one of the strings will be chosen at random.

Please refer to /doc/efun and /doc/lfun for more information about functions
mentioned in this documentation, or, ask your local LPC guru for more help.

Lesson 3B (Making Armor)

At this point in your experience with LPC, you are hopefully beginning to learn
how inheritance works in order to make programming easier, and in turn, how to
create things using the standard files (like monster.c, or treasure.c) given to
us already.

Well, let's get right to it, and make a piece of armor, and explain what we
can about it, in full detail.  Remember that armor and weapons are easy to
build, much simpler than monsters.

inherit "obj/armor";

reset(arg)
{
    set_name("platemail");
    set_alias("gold platemail");
    set_short("gold platemail");
    set_long("This gold platemail shines in the light!\n");
    set_ac(4);
    set_weight(3);
    set_value(2500);
    set_type("armor");
}

The armor is done, and now all we need to do is explain what each function
does in terms of how armor works.

NOTE:  If you are using a European version of a MUD, all spellings of "armor"
       turn into "armour".

set_name(string)  -- Sets the id(str) of the armor.

set_alias(string) -- Sets the an additional id(str) on the armor.

set_short(string) -- Sets the short() of the armor.

set_long(string)  -- Sets the long() of the armor.

set_ac(int)       -- Sets the armor class of the item.  One thing you need to
                     keep in mind is that many MUDs have armor specifications.
                     Make sure that you ask your local LPC guru what those are,
                     so that all of your armor falls within the local MUD LPC
                     guidelines.

set_weight(int)   -- Sets the weight on the armor.

set_value(int)    -- Sets the value of the armor.

set_type(string)  -- This is a string that specifies the type of the armor.
                     Most MUDs support the following types:  armor, helmet,
                     shield, glove, cloak, boot, ring, and amulet.  Remember
                     that the spelling of "armor" might be "armour".  The
                     piece of armor MUST be one of these types.  Otherwise,
                     when

An additional function exists for armor, which you might be interested in
placing on your piece of armor:

set_arm_light(int) -- Sets a light value on the armor.  (Normally this is
                      set to 1 or 0, but it can be set higher.)

Lesson 3C (Making Weapons)

We are almost done with the basics of programming in LPC.  We need to cover
how to make weapons, and the features therein.  Weapons are more complicated
than armor, because of additional abilities in a weapon.  Any weapon can say
something on initialization, or on a hit.  These two extra intrinsics give us
much more flexibility.

So, without further ado, let's make a weapon.

inherit "obj/weapon";

reset(arg)
{
::reset(arg);
set_name("lance");
set_alias("lance of death");
set_short("The Lance of Death");
set_long("This lance is a powerful and deadly weapon, capable of " +
"destroying entire groups of monsters.\n");
set_class(18);
set_weight(3);
set_value(2500);
}

There's our weapon!  Now after a little explaination of what each function
does, we can explore a couple of the more advanced features given to weapons.

<And here I stopped....> :)

Please refer to /doc/efun and /doc/lfun for more information about functions
mentioned in this documentation, or, ask your local LPC guru for more help.

Daemon

/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\
| willr@s.ms.uky.edu   | "Just because my name is Will Rodgers doesn't mean"  |
| uk02951@ukpr.uky.edu | "that you have to give me money or anything..."      |
\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/
