From the NannyMUD documentation

LAST CHANGE

2000-12-27

TOPIC

NAME

        basics - 

DESCRIPTION

                            LPC Basics

                   Written by Descartes of Borg
                   first edition: 23 april 1993
                   second edition: 12 july 1993
 
 CHAPTER 8: The data type "object"
 
 8.1 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.
  
 7.2 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.
  
 8.3 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.
  
 8.4 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().
  
 8.5 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.
  
 8.6 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.
  
 8.7 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
 youself how to code it by experimenting.
 
 George Reese
 Descartes of Borg
 12 july 1993