CHAPTER 8: The data type "object"
Review
You should now be able to do anything so long as
you stick to calling functions within your
own object. You should also know, that at the bare
minimum you can get the create() (or reset()) function in your
object called to start just by loading it
into memory, and that your reset() function
will be called every now and then so that you may write the
code necessary to refresh your room. Note that neither
of these functions MUST be in your object.
The driver checks to see if the function exists
in your object first. If it does not, then it does not
bother. You are also acquainted with the data types
void, int, and string.
Objects as data types
In this chapter you will be acquainted with a more
complex data type, object. An object variable
points to a real object loaded into the driver's
memory. You declare it in the same manner as other data types:
object ob;
It differs in that you cannot use +, -, +=, -=, *,
or / (what would it mean to divide a monster
by another monster?). And since efuns like say()
and write() only want strings or ints, you cannot write() or
say() them (again, what would it mean to say a monster?).
But you can use them with some other of the most
important efuns on any LPMud.
The efun: this_object()
This is an efun which returns an object in which
the function being executed exists. In other
words, in a file, this_object() refers to the object your
file is in whether the file gets cloned itself or
inherted by another file. It is often useful
when you are writing a file which is getting inherited
by another file. Say you are writing your own living.c
which gets inherited by user.c and monster.c,
but never used alone. You want to log the
function set_level() it is a player's level being set (but you
do not care if it is a monster.
You might do this:
void set_level(int x) {
if(this_object()->is_player()) log_file("levels", "foo\n");
level = x;
}
Since is_player() is not defined in living.c or anything
it inherits, just saying if(is_player()) will
result in an error since the driver does not
find that function in your file or anything it inherits.
this_object() allows you to access functions which
may or may not be present in any final products
because your file is inherited by others without
resulting in an error.
Calling functions in other objects
This of course introduces us to the most important
characteristic of the object data type. It
allows us to access functions in other objects.
In previous examples you have been able to find out
about a player's level, reduce the money they
have, and how much hp they have. Calls to
functions in other objects may be done in two ways:
object->function(parameters)
call_other(object, "function", parameters);
example:
this_player()->add_money("silver", -5);
call_other(this_player(), "add_money", "silver", -5);
In some (very loose sense), the game is just a chain
reaction of function calls initiated by player
commands. When a player initiates a chain of function
calls, that player is the object which is returned by the
efun this_player(). So, since this_player() can change depending
on who initiated the sequence of events, you want
to be very careful as to where you place calls
to functions in this_player(). The most common place
you do this is through the last important lfun (we have mentioned
create() and reset()) init().
The lfun: init()
Any time a living thing encounters an object (enters
a new room, or enters the same room as a certain
other object), init() is called in all of the
objects the living being newly encounters. It is at this point
that you can add commands the player can issue in
order to act.
Here is a sample init() function in a flower.
void init() {
::init();
add_action("smell_flower", "smell");
}
Ito smell_flower(). So you should have smell_flower()
look like this:
1 int smell_flower(string str); /* action functions are type int */
2
3 int smell_flower(string str) {
4 if(str != "flower") return 0; /* it is not the flower being smelled */
5 write("You sniff the flower.\n");
6 say((string)this_player()->query_cap_name()+" smells the flower.\n");
7 this_player()->add_hp(random(5));
8 return 1;
9 }
In line 1, we have our function declared.
In line 3, smell_flower() begins. str becomes whatever
comes after the players command (not including
the first white space).
In line 4, it checks to see if the player had typed
"smell flower". If the player had
typed "smell cheese", then str would be "cheese".
If it is not in fact "flower" which
is being smelled, then 0 is returned, letting
the driver know that this was not the function which should
have been called. If in fact the player had a piece
of cheese as well which had a smell command
to it, the driver would then call the function for
smelling in that object. The driver will keep calling all functions
tied to smell commands until one of them returns
1. If they all return 0, then the player sees
"What?"
In line 5, the efun write() is called. write() prints
the string which is passed to it to this_player().
So whoever typed the command here sees "You
sniff the flower."
In line 6, the efun say() is called. say() prints
the string which is doing the sniffing, we
have to call the query_cap_name() function in
this_player(). That way if the player is invis, it will say
"Someone" (or something like that), and
it will also be properly capitalized.
In line 7, we call the add_hp() function in the this_player()
object, since we want to do a little healing
for the sniff (Note: do not code this object
on your mud, whoever balances your mud will shoot you).
In line 8, we return control of the game to the driver,
returning 1 to let it know that this was in
fact the right function to call.
Adding objects to your rooms
And now, using the data type object, you can add
monsters to your rooms:
void create() {
::create();
set_property("light", 3);
set("short", "Krasna Square");
set("long", "Welcome to the Central Square of the town of Praxis.\n");
set_exits( ({ "d/standard/hall" }), ({ "east" }) );
}
void reset() {
object ob;
::reset();
if(present("guard")) return; /* Do not want to add a guard if */
ob = new("/std/monster"); /* one is already here */
ob->set_name("guard");
ob->set("id", ({ "guard", "town guard" }) );
ob->set("short", "Town guard");
ob->set("long", "He guards Praxis from nothingness.\n");
ob->set_gender("male");
ob->set_race("human");
ob->set_level(10);
ob->set_alignment(200);
ob->set_humanoid();
ob->set_hp(150);
ob->set_wielding_limbs( ({ "right hand", "left hand" }) );
ob->move(this_object());
}
Now, this will be wildly different on most muds.
Some, as noted before, in that object so you
have a uniquely configured monster object. The last
act in native muds is to call move() in the monster object to
move it to this room (this_object()). In compat
muds, you call the efun move_object() which
takes two parameters, the object to be moved, and the object
into which it is being moved.
Chapter summary
At this point, you now have enough knowledge to code
some really nice stuff. Of course, as I have
been stressing all along, you really need to
read the documents on building for your mud, as they detail which
functions exist in which types of objects for you
to call. No matter what your knowledge of
the mudlib is, you have enough know-how to give
a player extra things to do like sniffing flowers or glue or whatever.
At this point you should get busy coding stuff. But
the moment things even look to become tedious,
that means it is time for you to move to the
next level and do more. Right now code yourself a small area
Make extensive use of the special functions coded
in your mud's room.c (search the docs for
obscure ones no one else seems to use). Add
lots o' neat actions. Create weapons which have magic powers which
gradually fade away. All of this you should be able
to do now. Once this becomes routine for you,
it will be time to move on to intermediate stuff.
Note that few people actually get to the intermediate stuff.
If you have played at all, you notice there are few
areas on the mud which do what I just told
you you should be able to do. It is not because
it is hard, but because there is a lot of arrogance out there
on the part of people who have gotten beyond this
point, and very little communicating of that
knowledge. The trick is to push yourself and think
of something you want to do that is impossible. If you ask someone
in the know how to do X, and they say that is impossible,
find out yourself how to code it by experimenting.
Back to the LPC basic manual table of contents.