chapter 25 "Rooms"
			    Building Rooms
		     The Nightmare IV LPC Library
		 written by Descartes of Borg 950420

This document details how to build rooms using the Nightmare IV LPC
Library's inheritable room object.  This document is divided into
simple room building and complex room building.  The first part
teaches you about basic rooms.  The second part tells you what
features are there to allow you to do creative things with the room object.

	   ************************************************
		     Part 1: Basic Room Building
	   ************************************************

I. The Simple Room
The simple room minimally looks like this:

#include <lib.h>

inherit LIB_ROOM;

static void create() {
    room::create();
    SetProperty("light", 2);
    SetClimate("indoors");
    SetShort("an empty room");
    SetLong("An empty room with no exits leading anywhere.  It is "
            "completely barren with nothing to describe.");
}

#include <lib.h>
This first line is one you need in any object.  It defines the exact
location of objects which you inherit.  In this case, the object is
LIB_ROOM.  It is currently located at /lib/room.c.  If we wanted to
change that location, however, we could do it easily since you only
reference LIB_ROOM.  So lib.h is a file that says that LIB_ROOM is
"/lib/room".  

inherit LIB_ROOM;
The third line, the inherit line, says that this object will inherit
/lib/room.c. 

static void create() {
The fifth line begins the meat of any object you will write.  This is
the beginning of a function.  This one is called create().  If you are
curious, the static means no one can use the call command to call the
function.  Do not worry about that too much, however, as it is always
something you put there for the create() function.

The "void" part simply says that you are returning no value from the
function.  See the LPC Basics textbook for more information on
functions.

room::create();
Inside the create() function are the calls which define what the
object will do.  The first call calls the function create() in
/lib/room.c, the object you just inherited.  /lib/room.c has its own
create() function which does some things needed in order to set up
your object.  You need to make sure it gets called through this line.

SetProperty("light", 2);
This sets the light in the room to be 2.  In outdoors rooms, this is
the average light during day time.  In indoor rooms, it is the average
light all the time.

SetClimate("indoors")
Every room has a climate.  An indoors room, among other things, is not
affected by weather or the time of day.

SetShort("an empty room")
This is the description that the player sees when in brief mode.  In
addition, in brief mode, obvious exit abbreviations are automatically
added.  This is done through the SetObviousExits() function described
later.  However, the short should be phrased in such a way that it
makes sense from something like a scry command which would say
something like: "You see Descartes in an empty room."

SetLong("An empty room with no exits leading anywhere.  It is "
            "completely barren with nothing to describe.");
This sets the long description seen by the player when in verbose
mode.   Note that items in the room as well as scents and sounds are
added to what the player sees automatically.

That's it! You now have a room which no one can leave!

II.  Adding items
Approval on any decent MUD will eat you for lunch if you do not
describe your items.  This is likely the most tedious part of area
building, however, it is also the part that largely makes the
difference between a dull area and a fun one.  You must be sure to
make it so that anything a player might logically want to see in
detail in a room is described in detail.  For example, say you have
the following long description for a room:

You are in Monument Square, once known as Krasna Square. The two main
roads of Praxis intersect here, where all of Nightmare's people gather
in joy and sorrow.  The road running north and south is called Centre
Path, while Boc La Road is the name of the road running east and west.
A magnificent monument rises above the square.

You should have descriptions for the following items placed in your
room:

square, monument, monument square, krasna square, roads, road,
intersection, people, centre path, boc la road, magnificent monument

How to do this with a minimum of hassle:

SetItems( ([ ({ "square", "monument square", "krasna square" }) :
	"The central square of Praxis where citizens and adventurers "
        "gather to chat and trade.  Formerly known as Krasna Square, "
        "is now known as Monument Square as thanks to those who helped "
        "to build the town",
        ({ "monument", "magnificent monument" }) : "A giant monolith "
        "rising above Monument Square",
        ({ "intersection", "road", "roads" }) : "The two main roads of Praxis "
        "intersect in Monument Square.  The one to the north and south "
        "is called Centre Path, while the other is Boc La Road.",
        ({ "people", "adventurers", "citizens" }) : "A varied group of "       
        "people from countless realms hanging about talking and trading.",
        "centre path" : "The main road leading north to the North Forest "
        "from Praxis, and south to the sea.",
        "boc la road" : "The main east-west road through Praxis, going "     
        "east towards the jungle, and west towards the Daroq Mountains." ]) );

That may seem like a mouthful, but it is easier to break down into
smaller points and see what is going on.  The SetItems() prototype
looks like this:

mapping SetItems(mapping items);

That means it accepts a data type called a mapping as the argument and
returns a new mapping of items.  A mapping is a special data type in
LPC that allows you to associate two values together, for example, to
associate an item with its description.  For example, above we wanted
to associate the items "monument" and "magnificent monument" with the
description "A giant monolith rising above Monument Square".  To do
that, a mapping looks like this:

([ value1 : assoc_value1 ])

where assoc_value1 is the value associated with value1.  In this case,
we might have something like:

([ "monument" : "A giant monolith rising above Monument Square." ])

But, we also wanted to associate "magnificent monument" with this
description.  One way, which is perfectly legitimate, would be:

([ "monument" : "A giant monolith rising above Monument Square",
   "magnificent monument" : "A giant monolith rising above Monument Square" ])

But that would be damned annoying, especially with long descriptions
or things with a lot of synonyms.  You can therefore group values
which have the same description together using array notation:
({ value1, value2, value3 })

And thus, make that mapping look like:

([ ({ "monument", "magnificent monument" }) : "A giant monolith rising " 
  "above Monument Square." ])

To complete setting the items, you simply add other item/description
pairs separated by commas:

([ ({ "monument", "monument square" }) : "A giant monolith rising "
  "above Monument Square.",
   "house" : "A little white house with white picket fences." ])

Mappings are a rather difficult concept to grasp, but once grasped
they are very powerful.  You should take a look at some sample code
from /domains/Examples/room to get a good idea of what proper code
looks like.  In addition, there is a chapter in Intermediate LPC
dedicated to the concept.  Finally, you can always mail
borg@imaginary.com to ask questions.

III. Adding Exits and Enters
If you understand the section above, exits and enters are simple.
They too use mappings, but less complicated ones:

SetExits( ([ "north" : "/domains/Praxis/n_centre1",
             "south" : "/domains/Praxis/s_centre1",
             "east" : "/domains/Praxis/e_boc_la1",
             "west" : "/domains/Praxis/w_boc_la1" ]) );

SetEnters( ([ "hall" : "/domains/Praxis/town_hall",
              "pub" : "/domains/Praxis/pub" ]) );

With an exit mapping, you simply match the direction to the room to
which it leads.  With an enter mapping, you match a thing being
entered with the room to which it leads.

Unlike other LPC Libraries, the Nightmare IV LPC Library distinguishes
between the concept of motion towards and motion into.  Motion towards
is exemplified by the "go" command, which is affected by SetExits().
For example, to go east, you type "go east".  You are simply going
towards the east (Note that "go east" is by default aliased to "e").

Motion into is exemplified by the "enter" command, which is affected
by SetEnters().  Enter marks anything you enter into, for example a
building or bushes or the like.  In the above example, a player would
issue the command "enter pub" to enter the pub.

IV. Adding Objects
If you want to add physical objects into your room, you use the
SetInventory() function.  For example, if you wanted to place a balrog
in the room:

    SetInventory(([ "/domains/Praxis/npc/balrog" : 1 ]);

Every reset, the room will then check to see if any balrogs are in the
room.  If no balrogs are in the room it will clone 1.  Again, this is
another function using a mapping.  In this case it is associating the
file name of an object with how many of that object should be in the
room at every reset.  If you wanted 5 balrogs in the room, you would
have changed the 1 to 5.

V.  Adding Smells, Listens, and Searches

The functions:
SetSmell()
SetSearch()
SetListen()

All work identically to the SetItems() function.  That is they match
things you can smell, listen, search to descriptions which the player
sees when they smell, listen, search the item.

For example:

SetSmell( ([ "monument" : "It smells of obsidian.", 
             "road" : "It smells dusty.",
             ({ "pub", "bar" }) : "It smells of alcohol." ]) );

If a player types:
"smell monument"
then they see
"It smells of obsidian."

One unique thing about these three functions, however, is that you can
use the special thing "default" to set a smell, listen, or search that
occurs when no object is specified.  For example, 

SetSmell(([ "default" : "It really stinks here." ]) );

Will have the player see "It really stinks here." when they simply
type "smell".  In addition, this is the smell the player sees when
they simply walk into a room.

VI. Miscellaneous stuff

SetObviousExits("n, s, e")
Sets an obvious exits string which gets seen in brief mode and by
newbies in verbose mode.  Generally, this should consist of the
abbreviations for the room's obvious exits only.

SetTown("Praxis")
For rooms which are considered part of a town, you must specify that
they are part of the town through this function.  In this example, the
room is set to be in the town of Praxis.  See the document
/doc/build/Towns for more information on towns.

SetDayLong("The sky lights up the endless fields of wheat which stand "
           "before you.");
SetNightLong("You are standing in a pitch black field of wheat.");
Instead of using SetLong(), you can call both of these functions to
give different long descriptions for day and night.

SetGravity(2.0)
This makes things in the room twice as heavy as normal.

SetDoor("east", "/domains/Praxis/doors/red_door");
Sets a door to the east which is the file
"/domains/Praxis/doors/red_door.c".  You should have an exit to the
east, and you should do this AFTER you have called SetItems().  See
the document /doc/build/Doors for detailed information on door
building.

VII.  Summary

Here is a room that uses everything described above:

#include <lib.h>

inherit LIB_ROOM;

static void create() {
    room::create();
    SetProperty("light", 2);
    SetClimate("temperate");
    SetTown("Praxis");
    SetShort("a peaceful park");
    SetDayLong("The light of the sun shines down upon an open field "
               "in the middle of Praxis known as Kronos Park.  In spite "
               "of the time of day, no one is around.  East Boc La "
               "Road is to the south.");
    SetNightLong("Kronos Park is a poorly lit haven for rogues in the "
                 "cover of night.  It is safest to head back south "
                 "towards the lights of East Boc La Road");
    SetItems( ([ ({ "field", "park" }) : "A wide open park in the "
                 "center of Praxis." ]) );
    SetSearch( ([ "field" : "You get dirt all over your hands." ]) );
    SetSmell( ([ "default" : "You smell grass after a fresh rain.",
                 "dirt" : "It smells like... dirt!" ]) );
    SetExits( ([ "south" : "/domains/Praxis/e_boc_la3" ]) );
    SetInventory( ([ "/domains/Praxis/npc/rogue" : 2 ]) );
}

	   ************************************************
		    Part 2: Advanced Room Building
	   ************************************************
I. Functionals
MudOS has a data type called a functional.  Most room functions take a
functional as an argument instead of a string.  What this does is
allow you to specify a function to get called in order to determine
the value rather than set it as a string which cannot be changed.  For
example, if you wanted to set a long description that varied depending the
status of a door:

#include <lib.h>

inherit LIB_ROOM;

string CheckDoor(string useless);

static void create() {
    room::create();
    SetProperty("light", 2);
    SetClimate("indoors");
    SetShort("an indoor room with a door");
    SetLong( (: CheckDoor :) );
    SetExits( ([ "east" : "/domains/Praxis/east_room" ]) );
    SetDoor("east", "/domains/Praxis/doors/red_door");
}

string CheckDoor(string useless) {
    string tmp;

    tmp = "You are in a plain indoor room with a door.  ";
    if( (int)"/domains/Praxis/doors/red_door"->GetOpen() )
      tmp += "The door is open.";
    else tmp += "The door is closed.";
    return tmp;
}

In this example, a function called CheckDoor() was written to
determine exactly what the long description should be.  This is done
because in create(), you have no idea what the status of the door will
be from moment to moment.  Using a function, you can therefore
determine what the long description is at the time it is needed.

Functionals can reference any function anywhere on the MUD, including
efuns.  See /doc/lpc/data_types/functionals for details on them.  For
the sake of this document however, you note a functional using smileys
:).

(: CheckDoor :) means the function CheckDoor() in this object.  You
can also specify function in other objects, for example:
(: call_other, this_player(), "GetName" :) would refer to GetName() in
the person who was this_player() AT THE TIME THE FUNCTIONAL WAS
CREATED.

Notice at the top of the file that CheckDoor() was prototyped.  You
must prototype any function you reference inside your objects. The
expression (: CheckDoor :) constitutes as a reference, and thus makes
you need to prototype the function.

The rest of this portion describes individual function calls using
functionals.  The functional prototype part is how your functional
should be declared.:

SetShort(string | function)
Functional prototype: string ShortFunc();
Example: SetShort( (: MyShort :) );
If you pass it a function, then this function gets called to determine
the short description.  The function should return a string which will
be used as the short description.

SetLong(string | function)
Functional prototype: string LongFunc(string unused)
Example: SetLong( (: MyLong :) );
This function should return a string which will be used as the long
description for the room.  The argument "unused" is just that, unused
in this context.  It is something used for other objects.

SetItems(mapping mp);
Functional prototype: string ItemFunc(string item);
Example: SetItems( ([ "house" : (: LookHouse :) ]) );
This function should return a string to be used for the item
description.  The argument is passed the name of the item being looked
at, so you can use the same function for multiple items.

SetSearch(mapping mp)
Alternate: SetSearch(string item, string | function desc)
Functional prototype: string SearchFunc(string item);
Examples: SetSearch( ([ "grass" : (: SearchGrass :) ]) );
          SetSearch("grass", (: SearchGrass :));
Note that there are two forms to SetSearch(), useful depending on how
many searches you are setting at once.  If you have a search function, 
then that function should return a string which is what they will see.
The argument passed is the item being searched.

SetSmell()
SetListem()
see SetSearch()

II. Advanced Exits
SetExits() is fairly straight forward.  However, there exists another
function for exits called AddExit().  It allows you to add one exit at
a time (useful if say a player searches and finds a new exit) as well
as give functional power to exits.  The prototype for AddExit() is:

varargs mapping AddExit(string dir, string dest, function pre, function post);

The varargs part of the prototype simply means you can call it using
less than the full number of arguments specified.  In this case, the
minimum call is:

AddExit("east", "/domains/Praxis/square");

The last two arguments are called pre-exit functions and post exit
functions.  The pre-exit function gets called when a player issues a
command to leave the room, but before the player is allowed to leave.
Depending on the return value of the function, the player is allowed
or denied the right to leave.  For example:

AddExit("north", "/domains/Praxis/square", (: PreExit :));

int PreExit(string dir) {
    if( !avatarp(this_player()) ) {
        write("You are too lowly to go that way!");
        return 0;
    }
    else return 1;
}

In other words, if the player is an avatar, they can go north.
Otherwise they cannot.  The prototype is:

int PreExit(string dir);

where the return value is 1 or 0 for can or cannot leave, and the
argument dir is the direction in which the player is exiting.

Post exit functions work a little differently since it makes no sense
to prevent someone from leaving once they have left.  The prototype
looks like:

void PostExit(string dir);

This simply allows you to do processing once the player is gone.  If
you wish a post exit without a pre exit, then:

AddExit("north", "/domains/Praxis/square"", 0, (: PostExit :));

Enters work exactly the same way.

Please read about the events CanReceive() and CanRelease(), as those
may be more appropriate places to do what you want.  Remember, this
only prevents a player from using the "go" command to go in that
direction.  CanReceive() in the other room would be better if your
desire is to keep non-avatars out of the square at any cost.

III.  Other Functions

AddExit()
RemoveExit()
AddEnter()
RemoveEnter()
RemoveSearch()
RemoveSmell()
RemoveListen()
AddItem()
RemoveItem()

All of the above Remove*() functions take a single string argument
specifying what it is that is being removed.  For example:

RemoveExit("east")

removes the exit to the east.

AddItem(string item, mixed val)
Adds a single item.  Val can be a string or function.

	Descartes of Borg
	borg@imaginary.com
