I was going to post a quick example as a counter to Musashi's drunk post, but I realised that as I went, more things needed to be explained. I'll assume anyone reading this has little to no coding experience, so if it's a little dumbed-down, apologies.
So to start with, a .h file -- these are useful to define things that you might be using in many places throughout a section of code (like an area) Essentially, they look something like this:
So, anywhere I need to use "/w/sylwen/Alataciria/" somewhere, I type PATH instead. Same for if I need "/w/sylwen/Alataciria/room/", I can just use ROOMPATH. This also becomes useful if you need to change the path of things, for example, if you change the directory. Then you only have to make the change in the .h file, instead of changing lots of instances all throughout the code.
There are also system .h files, for example, combat.h, that have defined values for certain aspects of the mud. These aren't something one would write onseself, they already exist.
So now, a basic monster/npc/mob/whatever you want to call it.
//Since we're not writing an entirely new object, but rather a //monster, we first inherit the basic monster object. inherit "obj/monster"; //that says that we're creating a monster, and to use the //file /obj/monster.c as a base. //next, we'd #include anything we needed. Generally, .h files //you've written are surrounded by ""'s, while system .h files are //noted with <>'s. For example: #include "../mydefs.h" #include <living.h> //note that a relative path "../" can be used. In this example, it //doesn't really matter what if anything is defined in those -- //we're not going to be using anything that needs defining. //Now, let's assume that this npc can heal players a total of twice //per reset if he's given a particular item. We need to keep track //of that, so we need a global variable. It's global because it's //declared outside of any function, and any function can access //its value. By contrast, a local variable would be declared within //a function, and could generally only be accessed by that //function. So, our global variable: int has_healed; //We could have called it pretty much anything we wanted. //has_healed makes sense, so I use that. It's of type int (integer) //because we need it track how many times he's healed people. //now we have the basic stuff -- where his description, stats, etc, //get set. This function is called create(). It's type is void, //because it has no return value. Most every object, be it an npc, //weapon, room, etc, has this function.
void create() { //now we tell it to first call the create() function in the base file //we inherited -- in this case, obj/monster.c -- this is so that it //behaves properly, and we don't lose any functionality that we //might otherwise be overwriting, since we're writing a new //create() function.
::create();
//now, we don't want the master object to take up extra memory, //so we don't give it anything unless it's a clone (the subject of //masters and clones being a subject for another time).
if(!is_clone()) return;
//so, there's a function is_clone() that tells us whether an object //is a clone or not. The ! means "not" -- so that statement says, //"If this object is NOT a clone, stop executing." Now, we want to //initialise our healing variable. When he first gets created, he //hasn't healed anyone, so we set it equal to 0.
has_healed = 0;
//now we have all the descriptive/stats/emotions, etc.
set_short("Bob the healer"); //note the lack of punctuation here -- it gets added later. set_long("Bob is a nondescript human male in his older years. " "He has short white hair, and a closely-trimmed " "white beard and moustace. His Orange eyes twinkle " "with an easy kindness."); set_race("human"); set_male(); set_level(5); //now, we could stop at set_level(), and all his stats would be //set for him, but it's always more interesting to set them //separately, and make him different from other level 5's. //we can use set_stats, or we can use the individual functions //such as set_dex(), etc. We'll use set_stats -- an integer array, //in order: set_stats( ({str, dex, int, con, wis}) ); set_stats(({4, 5, 7, 3, 8})); //Note I just made those up. Now we have the fun part -- his //ac/wc, which affect how he performs in combat. These numbers //are guided by Balance, based on the level of an npc. The //numbers I'll use here may or may not be to actual Balance //standards -- that of course, would be wizinfo set_head_ac(12); set_body_ac(11); set_arm_ac(11); set_leg_ac(7); //obviously he's got a gimpy leg. set_wc(9); //now we go on to keywords, and emotion responses. Emotion //responses are base on what type of category an emotion falls //into. For the example, he'll have one emotion response and one //keyword. add_emotion("greet", "acknowledge"); //the first argument is the category of emotions. This can actually //be an array, to give the same response to multiple types: // add_emotion(({"intimate", "affectionate_physical"}), "return_love"); //the second argument is the name of function we're going //to write that handles the response. Then we have his keyword: add_keyword("heal", "talk_heal"); //again, the first argument can be an array or words that all //evoke the same response. For example, I could have written //the above as add_keyword(({"heal", "cure", "revivify"}), "talk_heal"); //now we're done with create(). You'll notice he has no //equipment, but then he's just a simple healer. So we close the //function. }
//now we need to deal with those keyword responses we set up. //since we named the functions we want to use above, we use //the same name. So, our emotion response:
void acknowledge(object player) { //any emotion response function passes the player object that //called the response. This may or may not be of use, but it's //there if needed. Type is void because we're not returning any //value. Now we respond to the emotion.
message("Bob respectfully nods at # ."); //message is a function defined elsewhere in the code we've //inherited with obj/monster.c -- there are lots of symbols that //all get replaced with various things. Suffice the say that the //above will show "you" to the player that initiated the response, //and that player's name to all others. That's all we need, so we close the function. }
//Keywords are similar. void talk_heal(object player, string verb) { //we get both the player that called the response, and the verb //they used. These can both be useful, particularly in quests. We'll //ignore them for now.
message("Bob says: If you could bring me some beer, I'd heal you."); //we're using message() again, even though we're not replacing //anything. This is for simplicity. And we're done, incidentally. }
//So, now we get to actually dealing with his ability to heal, which //is a bit more complex than all the basic stuff from create(). Since //he's going to heal someone who gives him a beer, we can use //the given() function, which comes once again from our inherited //code.
void given(object obj) { //Here the object obj argument is the object he's being given. If //it's not beer, we should probably have him drop it. He wants to //get plastered, not hold your crap.
if(obj->id("beer")) call_out("given_beer", 1, this_player(), obj); else call_out("given_useless", 1, this_player(), obj); //so, most objects define id(), to return the aliases the object //will identify to. call_out calls a given function after a certain //delay, with whatever extra arguments you specify. So the above //says: "If the object identifies to "beer", call the //function, "given_beer" in 1 second, with the argument //this_player() (the player giving him the object), and the object. //Otherwise, call the function "given_useless" in 1 second, with //the object given (obj) and the player trying to give it to him //(this_player()) as the argument. And we're done with the basic //given() function. So we move on to the given_useless function, //as it's a bit less complex than given_beer. Note that both of //these are not predefined functions, so we're writing them //ourself. Close the function. }
//so, it's not beer, he doesn't want it. He'll try to give it back, but //if they've picked up something else in the meantime, and can't //carry it, he'll "drop" it. We need to tell the function that it's //getting the two arguments we passed above. void given_useless(object player, object obj) { //so now we're going to have a local variable, weight, which is //only used in this function. Obviously, it's an integer. int weight;
weight = obj->query_weight(); //and we assign to it the value of the object's weight.
//first, he wonder's why they're giving him crap:
tell_room(environment(this_object()), "Bob asks: Why have you given me "+obj->short()+", " +player->query_name()+"?\n");
//Now we see if they can carry it if(player->add_weight(weight)) { //they can, so we continue and move it back to them obj->move(player); message("Bob gives "+obj->short()+" back to # ."); } //so, we moved it back to the player, and gave a message for //giving it back. Now, if they can't carry it: else { obj->move(environment(this_object())); message("Bob drops "+obj->short()+"."); } //Okay, but what about his weight? it got added to him when he //was given the object. In order to avoid weight bugs, we //subtract the object's weight from him.
this_object()->add_weight(-weight); //And we're done with this function. }
//So now, the part that really matters. He's got beer. void given_beer(object player, object obj) { message("Bob says: Ah, nothing like a good beer."); message("Bob guzzles the beer, and disposes of the bottle."); this_object()->add_weight(-(obj->query_weight())); destruct(obj); //So, messages for him receiving it and drinking it. Then we //subtract the weight to avoid weight bugs, and destruct the //beer object. Note he'll actually gain no healing himself from this.
if(has_healed < 2) { message("Bob says in a slightly slurred voice: Well, here's you're healing I promised."); message("Bob lays his hand on $ chest. # &0 healed!@are is"); player->reduce_hit_point(-10); has_healed++; } else { message("Bob says: Sorry, but I'm a little too drunk to heal you now."); } //So, since he only heals twice, if has_healed is less than 2, he'll //heal. We write a message for it, and use reduce_hit_point() //with a negative value to restore 10 hit points. //(reduce_hit_point() with a positive number would do damage, //though non-lethal) If he heals, we need to increment the global //variable. If he's already healed twice, he gives some excuse //and doesn't heal. And this fun is done. }
//Now, we want him to be able to heal again when he resets, if //he isn't killed.
void reset() { ::reset(); //because our inherited code has its own reset()
has_healed = 0; //and he'll be able to heal twice more. Alternatively, we could //make him take more time to recover by only decrementing the //variable: has_healed--; and it'd take him two resets to be able //to heal twice -- he'd be able to heal at least once a reset, but //only twice if he hasn't healed for 2. And we're done. With this //function and this npc. }
Not to mention that fixed-width usually sucks on here. =)~ Of course, you forgot one section of code:
if(player->query_real_name() == "lunger") { message("Bob recognises who you are and nearly stumbles over his tongue as he "+ "says in a slightly slurred voice: Yessir, I'll get right on that!"); message("Bob lays his hand on $ chest. # &0 healed!@are is"); player->reduce_hit_point(-20); }
Jarlaxle: "We will not be caught by surprise." Entreri: "Almost everyone I've killed uttered similar last words." Jarlaxle: "Then I am glad once again that you are on my side." Entreri: "They've often said that too."
None of the code is explained though, and it's all working code in the game. It's intended to post example code here, not code that's working in the game.
I'd suggest asking nicely for Scarecrazy's example code and tutorials then, she's already got loads of sample code typed up and explained, much better than what I could do.
I think Sylwen has more examples of this sort. Let's see if she'll put more up. =)
Jarlaxle: "We will not be caught by surprise." Entreri: "Almost everyone I've killed uttered similar last words." Jarlaxle: "Then I am glad once again that you are on my side." Entreri: "They've often said that too."