Being A Wizard In OSB
---------------------
Congratulations. That you are reading this probably means that you have just
become an apprentice wizard in OSB.
As an apprentice, you have full wizard powers except that you can't
create objects on your own. Use your powers carefully to learn the
responsibilities and secrets of wizardhood, and you'll be welcomed
fullheartly. Read this text to get a first insight.
First of all, try to remember this at all times
-----------------------------------------------
As a wizard, you have immense powers. But, with those powers comes
responsibility - so always think before you do anything. For example,
it is possible to move other wizards' objects, to crash the game,
and to kill all the players. And it IS possible to do all these
things accidentally, so PLEASE be careful!
Remember that as far as the game is concerned you are NO LONGER
a part of it. Wizards do not play the game, they are above that
and participate only by adding new ideas, like quests and rooms.
If you don't know what you are doing, then you probably shouldn't
do it. If you know what you are doing, but not if you should do it,
then you probably shouldn't do that either.
The file structure is a tree
----------------------------
LPmud mimics the file system hierarchy and file handling commands of UNIX.
A directory is a set of files, where those "files" can be both normal
files and other directories. Everything starts with the so-called root
directory, the name of which is "/" (at least from inside LPmud!).
As in UNIX, a file or a directory is referred to by its file name or its
path name, i. e. for example "thing.c" and "/players/padrone/thing.c".
The rooms in the "basic" LPmud world are in the directory "/room",
and the objects (like the weapon and monster objects) are in "/obj".
Each wizard has his own home directory, called for example "/players/padrone".
Some commands for handling files:
ls - list the files in a directory
rm - remove a file
ed - edit a file using LPmud's built-in, line-oriented, editor
cat - print the contents of a file on the screen
more -- print the contents of a file, approx. one screenful at a time
tail -- print the last few lines of a file
cd -- change the current directory
As in Unix, most commands do their work without further asking. For
example, if you command 'rm *' accidentally, LPmud will remove all
your files.
Where do I find the documentation?
----------------------------------
There is a directory called "/doc" (try the commands "ls" and "ls /doc"),
that contains documentation. You'll find at least these
subdirectories:
/doc/lpc : information about the language LPC
/doc/concepts : information about the basic mudlib concepts
/doc/std : the documentation of all /std objects
/doc/obj : the documentation of all /obj objects
/doc/lib : the documentaiont of the /obj/lib files
/doc/efun : descriptions of all external functions supplied by the GD
It can happend that you won't find what you look for in the documentation.
In that case, either ask another wizard, read the code (which actually
contains some very useful comments), or find out by testing.
You can also use the built in 'man' command. It will list search for
and list documentations.
One of the first things you should do is to read the entire contents of
/doc. This is not as much work as it seems and it really pays off very soon,
since you later will remember where to look for explanations of commands
and routines.
Remember also that the Domainlords and Archwizards, i.e all wizards
with level > 30, have as part of their duties the obligation to help
other wizards with their problems in NIGHTFALL.
Especially the wizard which made you an apprentice has responsibility
for you.
The best way of reading the documentation is to do "cd /doc" and then "ls"
to see what you have there. Then you can change directory down into the
different subdirectories and do "ls" in them to see what you can read there.
I specially want to point out that the information on the editor "ed" can be
found in "/doc/intro" in the files "ed".
You read any file with the command "more" like "more /doc/build/ed"
or just "more ed" if you already have written "cd /doc/build".
Object-oriented programming in LPmud
------------------------------------
When you play LPmud, everything you encounter (weapons, monsters, players,
rooms etc.) are OBJECTS. Each object is an INSTANCE of a CLASS of objects.
One class can INHERIT part of its design from another class.
In LPmud, an instance is either created by CLONING or LOADING the object
class, and each class is described in a file, written in a C-like language
called LPC.
These "class descriptions" are the programs in LPmud. All program code
that you write as a wizard, or that is part of the standard LPmud world,
will be part of a description of an object class, a class of objects
that can be loaded and cloned.
As an apprentice, you are not able to create own objects. You can
write code, but any attempt to load or clone it will result in an
error. Nevertheless, the attempt to load will compile the written code
first, so you have the opportunity to learn the correct syntax of LPC
by trying it out, without the danger that a buggy object of yours does
damage.
Loading and cloning of other wizard's objects is of course possible.
Loading, cloning and updating
-----------------------------
"Loading an object" means the process of taking a class description
file, for example "/obj/weapon.c", and reading it into the game.
This is done as soon as that object is used in any way, for example
if a function in the object is called from LPC, or if another object
tries to enter it (as when a player walks into a room).
When the object is loaded, one instance of the object class is created.
If there should be only one instance, as for example for (most) rooms,
this is enough.
If you need to create several instances of a class of objects, you
clone it. Objects can be cloned using the wizard command "clone",
as in "clone /obj/torch", and with the LPC function clone_object(),
as in
this_torch = clone_object("obj/torch");
Cloning an object will cause it to be loaded, if it wasn't already loaded.
This will create a basic instance of that class (in our example named
"obj/torch"), from which all clones ("obj/torch#42", with 42 being an
unique number) are created from. This basic instance is called
'blueprint' for that reason.
When you have changed the description of an object by editing the file,
and want to load the new version of the object, you can use the wizard
command "update" which destructs the blueprint of the object.
Then load (or clone) the new version. If the object in question is a
room, a "goto ROOMNAME" will load the room as well.
LPC
---
LPC is a dialect of C and differs from C mainly in being simplified
and in that it allows for handling the objects. It has only four data
types (plus arrays of those data types), fewer operators and a simplified
control structure.
Note that before the contents of a file is read it is run through
the standard C pre-processor. This program can be used to insert other
files into the file being read, and to define constants and macros.
Lines starting with "#", as the line (no blanks before the #!)
#include "std.h"
#define INSURANCE_LIMIT 1000
#define CLONE(var, file) var = clone_object(file);
are all commands to the preprocessor.
Read more about LPC in the files in the directory /doc/LPC !
You can read more about the C preprocessor in any book about C,
but that is hardly neccesary for the ordinary use of LPC.
Functions:
----------
There exists two types of functions in LPC, called LOCAL and EXTERNAL
functions. A local function is defined in LPC code in an LPC file, and
is contained in an object. A local function can be called from LPC code
inside the object or from LPC code in another object.
Objects communicate with each other by calling functions in each other.
A local function can be declared STATIC, meaning that it can only be called
from LPC code within the same object. External function are "built in" in
LPC, so it has no definition in LPC code.
Examples of local functions are the functions set_value and query_value
in "/obj/treasure.c". Examples of external functions are write(), time()
and add_action().
Read more about functions in the file "/doc/LPC/function".
All external functions are documented in "/doc/efun/FUNCTION", where FUNCTION
is the name of the function. Many local functions are documented in
"/doc/lfun/FUNCTION". Not all can be documented, as they are unlimited.
Error messages
--------------
If an error occurs when LPmud is trying to load one of your objects,
an error message will be printed on the files "/players/PLAYERNAME/.err"
(where PLAYERNAME is your name).
Those error messages, for all the wizards, and also some other error
messages, are also written on the file "/parse.log". Sometimes you have
to look at that file too to find awhat caused an error. That file will
usually grow quickly, and the command "tail /parse.log" will prove useful.
The last error is stored for every wizard. It can be obtained simply by
doing 'ed'. If there was an error, it will be displayed with the offending
line number, and the correct file will be edited.
Also, don't forget to periodically check "/log/rep/PLAYERNAME". When
a player files a bug report in one of your rooms, using "bug", "typo"
or "idea", the message ends up in that file!
Building
--------
As it is said before, you as an apprentice aren't able to build own
objects. So take this as an advice for the future.
Start by looking at the existing rooms. As a wizard, when you enter a room,
LPmud prints the path name of the object that is the room, for example
"/room/forest2". You can look at the definition of the room by using the
command "more /room/forest2.c" or "cat /room/forest2.c".
For most purposes it is sufficient to use the standard room routines that
you get by inheriting "/std/room".
Below the LPC office is an dungeon with room and object examples; the
sources can be found in the directory "/d/archwiz/common/lpc/exm".
The sources of these objects have been commented extensively, and
cover most of a builder's work.
Use them as a basis for own objects.
An example of a room
--------------------
This is an example of how a file describing a room can look like
(This file exists and is called "/obj/def_workroom.c"; it is the
default workroom for all new wizards):
/* The default workroom for new wizards. It's copied to their homedir */
#ifndef NAME
#define NAME "Someone"
#endif
inherit "/std/room";
create () {
::create();
SetIntShort ("The workroom of "+NAME+"\n");
SetIntLong (
"This is the workroom of "+NAME+".\n"
+"Since "+NAME+" is just a new wizard, it is completely empty, but\n"
+"nevertheless quite comfortable.\n"
+"There is one exit labelled 'tower'.\n"
);
AddExit ("tower", "/d/archwiz/common/room/tower/center");
}
When the first player enters the room after the game was started, the
file is run through the LPC compiler, yielding a much larger code in
an internal representation which is then executed by the gamedriver.
The code produced is much larger and contains a lot of other functions
besides the "create()" that was actually written above. Some of these
local functions ('funs') are of special interest, and many objects
will have them locally defined:
create():
This function is called automatically when the object is created
(that is, loaded or cloned). It is used to initialize things in an
object (like setting the room descriptions or adding items).
Here it just sets the descriptions and adds an exit to the Archwiz
Tower.
reset():
This function is called in each loaded object every 70 minutes (or
when a player get in sight, whatever happens later).
It is used to periodically reset it (like putting new monsters in
the different rooms, when they have been killed by all those
brutal players).
QueryIntShort():
This functions returns a one-line string giving the internal
description of the room (as set by SetIntShort()).
QueryShort() doesn the same for the external description.
QueryIntLong():
This functions returns a multi-line string holding a long
description of the room's internals. It is initialized by
SetIntLong(). QueryLong() does the same for the long external
description.
init():
When a living object (a player or a monster) 'sees' this object (if
he, she or it enters the object, or arrives in the same room as the
object, or the object is moved into the player or monster, or the
object is moved into the same room as the player or object), the
living object calls the function init() in this object, with the
living object as current player.
remove():
Tells the room to selfdestruct. This could be of course also be
done using the efun 'destruct()', but this way final actions (like
updates of weight and light) can be done before the actual
destruct.
If you redefine functions that are automatically defined by the inherited
objects, you will sometimes want to call that function in the inherited code.
You do that with the operator "::". If you define the function "init()" in
your room but still would like to call the "init()" in "/std/room" you do
that with the command "::init();". Typically this IS something that you want
to do but remember to put the call to the inherited function first.
The example above was a special room called a "workroom". As a full
wizard you will have a command called "home" that will bring you to
your workroom if you have one. You will be supplied with the default
workroom depicted above, but you can change this room later to fit
your own tastes.
Some more local functions that are defined in many objects
----------------------------------------------------------
move():
Tells the object to move at a certain place. This is the only way
to move other objects!
heart_beat():
For objects that have a heart beat, the local function heart_beat
will be called (automatically) every two seconds.
The heart beat is started by calling the external function
"set_heart_beat()", with a non-zero argument from the object.
This can be used to simulate that time is going between events in
the room, but usually the external function "call_out()" is MUCH
better to use for that purpose.
To find all the available functions you should read the following files
and try to understand them, since they are the most inherited and cloned
objects in the game. Read their docs in /doc/std for easier understanding.
/std/thing.c - For standard thing functions.
/std/container.c - For standard container functions.
/std/room.c - For standard room functions.
/std/weapon.c - For weapon functions.
/std/living.c - For common monster and player functions.
/std/armour.c - For armour functions.
/std/npc.c - For monster functions.
/std/player.c - For player functions.
Player commands
---------------
A living object (a player or a monster) can perform commands.
Every command has to be defined in an object, although some commands
are defined by the player object itself, like "quit" and "look".
All other commands, like "south", "north" and "buy" (in the shop)
has to be defined in an object. This is usually done in the local
function "init".
Which function finally executes the command is determined this way:
If the player gives an command, the gamedriver searches all objects in
the vicinity which have defined an action by the external function
'add_action()' for the given command verb. The search starts with the
first object in the players inventory, continues after the inventory
with the first object in the players environment and ends with the
environmental object and then the player object.
During this search, the gamedriver applies the command (minus the
command verb itself) as argument to the function specified for this
verb. If the function call returns something non-zero (usually 1), it
means that the gamedriver found the right object for this command, and
thus the execution is terminated.
Else, if the function call returns zero, it wasn't the right object
and the gamedriver has to continue it's search.
If no object pleads responsibility for this command, the gamedriver
returns an error message to the player. Default for this message is
the wellknown 'What?', but it can be changed for this commandsearch
with the external function 'notify_fail()'.
The "current player"
--------------------
The external function "this_player()" will return the current player.
This is the player OR monster that performed the command that LPmud is
now executing or caused the function "init()" in an object to be run.
The external function "write()" will send the text it is given as argument
to the current player. The function "say()" will send the text to all living
objects that are in the same room as the current player (or that are inside
the current player!), but NOT to the current player itself.
See the documentation on light and vision for a more refined methods.
When changing your rooms etc
----------------------------
ALWAYS, ALWAYS, ALWAYS test all changes that you make to your code, at least
by doing update and clone or load on the object that you changed. Do this
even after a trivial change, like correcting the spelling of a word in a
message. It is very easy to accidentaly put a period or a semicolon somewhere
in your code, and even if the your code is correct there can be bugs in the
LPmud game itself that can cause it not to work.
Some non-technical hints on how to be a good and/or popular wizard
------------------------------------------------------------------
In this section I will relate parts of the /doc/intro/RULES file.
I believe that some of the things discussed in the rules and guidelines
can be of general interest to how a wizard should behave and what things
he (or she or it) should build and not build. So here are some of the
rules and guidelines:
Do not harm a player directly!
This means, among other things, that you should never attack or
kill a player. Also take care not to zap or teleport away (or heal)
a monster a player is currently fighting (stat the monster first).
Avoid teleporting a player without asking first.
Do not help a player directly!
This means, for example, that you must not help by killing or
attacking monsters, healing players or giving away items (an
occasional beer might be excused). Also take care not to leave
valuable items lying around, e. g. don't zap a monster and leave
its weapon-containing corpse to rot on the street.
Remember that the business of players is their business only! A typical case
is when one player kills another and the killed one starts yelling for blood.
Let them sort it out on their own. You may NOT interfere and if you get really
mad about it, tell or mail an Archwizard or administrator.
Do not make deadly traps!
A player must never die because he didn't know what happens in the
next room. If some room is very dangerous, make some hint (like the
giant footprints outside the giant's lair).
Do not send killer-monsters out into the world!
This means that if you create a monster that can walk out of the
castle, make it a nice monster (i. e. it should never attack players).
Don't make too good things!
For example, don't create weapons with weapon class > 20 if there is
no big drawback with them. Weapons with wc 20 should be at least as
hard to get as the swords of the three giants in the giant conference.
Also, body armour (type "armour") should be max armour class 4, and
additional armour, like helmets, always class 1.
And don't make the monsters too easy to kill. A monster with much
treasure and/or many experience points should be big, dangerous and
very hard to kill.
Remember that the purpose of the game is for it to be fun and
enjoyable both for players and for wizards - not to have all players
converge on your castle just to get that kill'em'all sword.
Don't make healing too easily available!
It's an important part of the game that players have to wait for their
hit points to regenerate. Also, the pub is one of the main sources of
player interaction and role playing. Remember that even if you only
heal a few points per player command (like "drink from fountain"),
many players use macros and abbreviations and can still heal almost
instantly. Make the players drunk, the healing very limited, the cost
very high, or the place very hard to get to! Portable healing in the
way of potions and food upsets the balance of the game. If you create
such an object make sure they are rare and very expensive.
Don't build lots and lots of extra pubs and shops!
The pub, as mentioned above, and also the shop, are natural meeting
points and important sources of player interaction. Therefore you
should try not to build your own pub or shop if you don't really need
it (for example in a quest or for a guild).
Don't make your own "perfect tool".
It seems that every wizard wants to make a perfect tool that
can do anything.
There are a selection of tools available in "/obj/tool", and
especially the xtool fulfills most needs. A good knowledge of
LPC and the mudlib gives you more power than a bag of tools.
Prices of equipment.
Should be set with some sense. A player will NEVER be payed more than
1000 coins in a shop. The only reason to set a higher value is to
prevent another player from buying the same object back from the shop
at a too low price.
It is very easy to create powerful magic items in LPmud, but this is generally
NOT a good idea. The balance of the game must be kept and remember that
NOTHING in the world is given away for free. It's very hard to write rules
that define how magic may and may not be used, and becuace of that the
wizard's own judgement must be trusted in almost all cases. There are
however some things I'd like to warn about right now.
Teleporting players is a nuisance.
It wreaks havoc with quests and is generally disliked. If you allow
a player to teleport it should only be to one or at most two
predifined locations, like the church or your guild, and the cost
in spellpoints should be high.
New magic spells for players.
Are very easy to write but it is very hard to judge what effect they
will have on the game in advance. A discussion about magic and how
it should be limited is currently on the way. Some very loose rules
exist and the best option is to ask an Archwizard FIRST and implement
the spell AFTERWARDS, if ever. If you happily make something and put
a lot of time into it only to be told in no-negotiation-possible-
terms to remove it, you will only get mad and frustrated. Use your
good judgement and in doubtful cases do something else or ask the
other wizards what they think first.
Examples of rooms, monsters, objects etc.
=========================================
Read the files in the directory /d/archwiz/common/lpc/exm. They are
all just documented examples of standard (and not so standard)
problems in LPmud. Like creating new rooms, monsters and equipment
along with some general advice. The best thing might be to visit the
example rooms, just below the Office of the Archwiz of LPC, and walk
around for a while, before actually viewing the code.
NOW: Read /doc/intro/RULES by typing "more /doc/intro/RULES" .