#include "log.h" #define KILL_NEUTRAL_ALIGNMENT 10 #define ADJ_ALIGNMENT(al) ((-al - KILL_NEUTRAL_ALIGNMENT)/4) #define NAME_OF_GHOST "some mist" /* * If you are going to copy this file, in the purpose of changing * it a little to your own need, beware: * * First try one of the following: * * 1. Do clone_object(), and then configur it. This object is specially * prepared for configuration. * * 2. If you still is not pleased with that, create a new empty * object, and make an inheritance of this objet on the first line. * This will automatically copy all variables and functions from the * original object. Then, add the functions you want to change. The * original function can still be accessed with '::' prepended on the name. * * The maintainer of this LPmud might become sad with you if you fail * to do any of the above. Ask other wizards if you are doubtful. * * The reason of this, is that the above saves a lot of memory. */ /* * Include this file in objects that "lives". * The following variables are defined here: * */ int time_to_heal; /* Count down variable. */ int money; /* Amount of money on player. */ string name; /* Name of object. */ string msgin, msgout; /* Messages when entering or leaving a room. */ int is_npc, brief; /* Flags. */ int level; /* Level of monster. */ static int armour_class; /* What armour class of monster. */ int hit_point; /* Number of hit points of monster. */ int max_hp, max_sp; int experience; /* Experience points of monster. */ string mmsgout; /* Message when leaving magically. */ string mmsgin; /* Message when arriving magically. */ static object attacker_ob; /* Name of player attacking us. */ static object alt_attacker_ob; /* Name of other player also attacking us. */ static int weapon_class; /* How good weapon. Used to calculate damage. */ static object name_of_weapon; /* To see if we are wielding a weapon. */ static object head_armour; /* What armour we have. */ int ghost; /* Used for monsters that can leave a ghost. */ static int local_weight; /* weight of items */ static object hunted, hunter; /* used in the hunt mode */ static int hunting_time; /* How long we will stay in hunting mode. */ static string cap_name; /* Capital version of "name". */ int spell_points; /* Current spell points. */ static string spell_name; static int spell_cost, spell_dam; int age; /* Number of heart beats of this character. */ int is_invis; /* True when player is invisible */ int frog; /* If the player is a frog */ int whimpy; /* Automatically run when low on HP */ string auto_load; /* Special automatically loaded objects. */ int dead; /* Are we alive or dead? */ string flags; /* Bit field of flags */ /* * All characters have an aligment, depending on how good or chaotic * they are. * This value is updated when killing other players. */ int alignment; int gender; /* 0 means neuter ("it"), 1 male ("he"), 2 female ("she") */ /* * Character stat variables. */ int Str, Int, Con, Dex; /* * The following routines are defined for usage: * stop_fight Stop a fight. Good for scroll of taming etc. * hit_player Called when fighting. * transfer_all_to: Transfer all objects to dest. * move_player: Called by the object that moves the monster. * query_name: Gives the name to external objects. * attacked_by Tells us who are attacking us. * show_stats Dump local status. * stop_wielding Called when we drop a weapon. * stop_wearing Called when we drop an armour. * query_level Give our level to external objects. * query_value Always return 0. Can't sell this object. * query_npc Return 1 if npc otherwise 0. * get Always return 0. Can't take this object. * attack Should be called from heart_beat. Will maintain attack. * query_attack * drop_all_money Used when the object dies. * wield Called by weapons. * wear Called by armour. * add_weight Used when picking up things. * heal_self Enable wizards to heal this object. * can_put_and_get Can look at inventory, but not take things from it. * attack_object Called when starting to attack an object. * test_if_any_here For monsters. Call this one if you suspect no enemy * is here any more. * Return 1 if anyone there, 0 if none. * force_us Force us to do a command. * query_spell_points Return how much spell points the character has. * reduce_hit_point Reduce hit points, but not below 0. */ /* * This routine is called from objects that moves the player. * Special: direction "X" means teleport. * The argument is "how#where". * The second optional argument is an object to move the player to. * If the second argument exists, then the first argument is taken * as the movement message only. */ varargs move_player(dir_dest, optional_dest_ob) { string dir, dest; object ob; int is_light, i; if (!optional_dest_ob) { if (sscanf(dir_dest, "%s#%s", dir, dest) != 2) { tell_object(this_object(), "Move to bad dir/dest\n"); return; } } else { dir = dir_dest; dest = optional_dest_ob; } hunting_time -= 1; if (hunting_time == 0) { if (hunter) call_other(hunter, "stop_hunter"); hunter = 0; hunted = 0; } if (attacker_ob && present(attacker_ob)) { hunting_time = 10; if (!hunter) tell_object(this_object(), "You are now hunted by " + call_other(attacker_ob, "query_name", 0) + ".\n"); hunter = attacker_ob; } is_light = set_light(0); if(is_light < 0) is_light = 0; if(is_light) { if (!msgout) msgout = "leaves"; if (ghost) say(NAME_OF_GHOST + " " + msgout + " " + dir + ".\n"); else if (dir == "X" && !is_invis) say(cap_name + " " + mmsgout + ".\n"); else if (!is_invis) say(cap_name + " " + msgout + " " + dir + ".\n"); } move_object(this_object(), dest); is_light = set_light(0); if(is_light < 0) is_light = 0; if (level >= 20) { if (!optional_dest_ob) tell_object(this_object(), "/" + dest + "\n"); } if(is_light) { if (!msgin) msgin = "arrives"; if (ghost) say(NAME_OF_GHOST + " " + msgin + ".\n"); else if (dir == "X" && !is_invis) say(cap_name + " " + mmsgin + ".\n"); else if (!is_invis) say(cap_name + " " + msgin + ".\n"); } if (hunted && present(hunted)) attack_object(hunted); if (hunter && present(hunter)) call_other(hunter, "attack_object", this_object()); if (is_npc) return; if (!is_light) { write("A dark room.\n"); return; } ob = environment(this_object()); if (brief) write(call_other(ob, "short", 0) + ".\n"); else call_other(ob, "long", 0); for (i=0, ob=first_inventory(ob); ob; ob = next_inventory(ob)) { if (ob != this_object()) { string short_str; short_str = call_other(ob, "short"); if (short_str) write(short_str + ".\n"); } if (i++ > 40) { write("*** TRUNCATED\n"); break; } } } /* * This function is called from other players when they want to make * damage to us. We return how much damage we received, which will * change the attackers score. This routine is probably called from * heart_beat() from another player. * Compare this function to reduce_hit_point(dam). */ hit_player(dam) { if (!attacker_ob) set_heart_beat(1); if (!attacker_ob && this_player() != this_object()) attacker_ob = this_player(); else if (!alt_attacker_ob && attacker_ob != this_player() && this_player() != this_object()) alt_attacker_ob = this_player(); /* Don't damage wizards too much ! */ if (level >= 20 && !is_npc && dam >= hit_point) { tell_object(this_object(), "Your wizardhood protects you from death.\n"); return 0; } if(dead) return 0; /* Or someone who is dead */ dam -= random(armour_class + 1); if (dam <= 0) return 0; if (dam > hit_point+1) dam = hit_point+1; hit_point = hit_point - dam; if (hit_point<0) { object corpse; /* We died ! */ if (!is_npc && !query_ip_number(this_object())) { /* This player is linkdead. */ write(cap_name + " is not here. You cannot kill a player who is not logged in.\n"); hit_point = 20; stop_fight(); if (this_player()) this_player()->stop_fight(); return 0; } dead = 1; if (hunter) call_other(hunter, "stop_hunter"); hunter = 0; hunted = 0; say(cap_name + " died.\n"); experience = 2 * experience / 3; /* Nice, isn't it ? */ hit_point = 10; /* The player killing us will update his alignment ! */ /* If he exist */ if(attacker_ob) { call_other(attacker_ob, "add_alignment", ADJ_ALIGNMENT(alignment)); call_other(attacker_ob, "add_exp", experience / 35); } corpse = clone_object("obj/corpse"); call_other(corpse, "set_name", name); transfer_all_to(corpse); move_object(corpse, environment(this_object())); if (!call_other(this_object(), "second_life", 0)) destruct(this_object()); if (!is_npc) save_object("players/" + name); } return dam; } transfer_all_to(dest) { object ob; object next_ob; ob = first_inventory(this_object()); while(ob) { next_ob = next_inventory(ob); /* Beware that drop() might destruct the object. */ if (!call_other(ob, "drop", 1) && ob) move_object(ob, dest); ob = next_ob; } local_weight = 0; if (money == 0) return; ob = clone_object("obj/money"); call_other(ob, "set_money", money); move_object(ob, dest); money = 0; } query_name() { if (ghost) return NAME_OF_GHOST; return cap_name; } query_alignment() { return alignment; } query_npc() { return is_npc; } /* * This routine is called when we are attacked by a player. */ attacked_by(ob) { if (!attacker_ob) { attacker_ob = ob; set_heart_beat(1); return; } if (!alt_attacker_ob) { alt_attacker_ob = ob; return; } } show_stats() { int i; write(short() + "\nlevel:\t" + level + "\ncoins:\t" + money + "\nhp:\t" + hit_point + "\nmax:\t" + max_hp + "\nspell\t" + spell_points + "\nmax:\t" + max_sp); write("\nep:\t"); write(experience); write("\nac:\t"); write(armour_class); if (head_armour) write("\narmour: " + call_other(head_armour, "rec_short", 0)); write("\nwc:\t"); write(weapon_class); if (name_of_weapon) write("\nweapon: " + call_other(name_of_weapon, "query_name", 0)); write("\ncarry:\t" + local_weight); if (attacker_ob) write("\nattack: " + call_other(attacker_ob, "query_name")); if (alt_attacker_ob) write("\nalt attack: " + call_other(alt_attacker_ob, "query_name")); write("\nalign:\t" + alignment + "\n"); write("gender:\t" + query_gender_string() + "\n"); if (i = call_other(this_object(), "query_quests", 0)) write("Quests:\t" + i + "\n"); write(query_stats()); show_age(); } stop_wielding() { if (!name_of_weapon) { /* This should not happen ! */ log_file("wield_bug", "Weapon not wielded !\n"); write("Bug ! The weapon was marked as wielded ! (fixed)\n"); return; } call_other(name_of_weapon, "un_wield", dead); name_of_weapon = 0; weapon_class = 0; } stop_wearing(name) { if(!head_armour) { /* This should not happen ! */ log_file("wearing_bug", "armour not worn!\n"); write("This is a bug, no head_armour\n"); return; } head_armour = call_other(head_armour, "remove_link", name); if(head_armour && objectp(head_armour)) armour_class = call_other(head_armour, "tot_ac"); else { armour_class = 0; head_armour = 0; } if (!is_npc) if(!dead) say(cap_name + " removes " + name + ".\n"); write("Ok.\n"); } query_level() { return level; } /* This object is not worth anything in the shop ! */ query_value() { return 0; } /* It is never possible to pick up a player ! */ get() { return 0; } /* * Return true if there still is a fight. */ attack() { int tmp; int whit; string name_of_attacker; if (!attacker_ob) { spell_cost = 0; return 0; } name_of_attacker = call_other(attacker_ob, "query_name", 0); if (!name_of_attacker || name_of_attacker == NAME_OF_GHOST || environment(attacker_ob) != environment(this_object())) { if (!hunter && name_of_attacker && !call_other(attacker_ob, "query_ghost", 0)) { tell_object(this_object(), "You are now hunting " + call_other(attacker_ob, "query_name", 0) + ".\n"); hunted = attacker_ob; hunting_time = 10; } attacker_ob = 0; if (!alt_attacker_ob) return 0; attacker_ob = alt_attacker_ob; alt_attacker_ob = 0; if (attack()) { if (attacker_ob) tell_object(this_object(), "You turn to attack " + attacker_ob->query_name() + ".\n"); return 1; } return 0; } if (spell_cost) { spell_points -= spell_cost; tell_object(attacker_ob, "You are hit by a " + spell_name + ".\n"); write("You cast a " + spell_name + ".\n"); } if(name_of_weapon) { whit = call_other(name_of_weapon,"hit",attacker_ob); if (!attacker_ob) { tell_object(this_object(), "CRACK!\nYour weapon broke!\n"); log_file("BAD_SWORD", name_of_weapon->short() + ", " + creator(name_of_weapon) + " XX !\n"); spell_cost = 0; spell_dam = 0; destruct(name_of_weapon); weapon_class = 0; return 1; } } if(whit != "miss") { tmp = ((weapon_class + whit) * 2 + Dex) / 3; if (tmp == 0) tmp = 1; tmp = call_other(attacker_ob, "hit_player", random(tmp) + spell_dam); } else tmp = 0; tmp -= spell_dam; if (!is_npc && name_of_weapon && tmp > 20 && random(100) < weapon_class - level * 2 / 3 - 14) { tell_object(this_object(), "CRACK!\nYour weapon broke!\n"); tell_object(this_object(), "You are too inexperienced for such a weapon.\n"); log_file("BAD_SWORD", name_of_weapon->short() + ", " + creator(name_of_weapon) + "\n"); spell_cost = 0; spell_dam = 0; destruct(name_of_weapon); weapon_class = 0; return 1; } tmp += spell_dam; if (tmp == 0) { tell_object(this_object(), "You missed.\n"); say(cap_name + " missed " + name_of_attacker + ".\n"); spell_cost = 0; spell_dam = 0; return 1; } experience += tmp; tmp -= spell_dam; spell_cost = 0; spell_dam = 0; /* Does the enemy still live ? */ if (attacker_ob && call_other(attacker_ob, "query_name", 0) != NAME_OF_GHOST) { string how, what; how = " to small fragments"; what = "massacre"; if (tmp < 30) { how = " with a bone crushing sound"; what = "smash"; } if (tmp < 20) { how = " very hard"; what = "hit"; } if (tmp < 10) { how = " hard"; what = "hit"; } if (tmp < 5) { how = ""; what = "hit"; } if (tmp < 3) { how = ""; what = "grazed"; } if (tmp == 1) { how = " in the stomach"; what = "tickled"; } tell_object(this_object(), "You " + what + " " + name_of_attacker + how + ".\n"); tell_object(attacker_ob, cap_name + " " + what + " you" + how + ".\n"); say(cap_name + " " + what + " " + name_of_attacker + how + ".\n", attacker_ob); return 1; } tell_object(this_object(), "You killed " + name_of_attacker + ".\n"); attacker_ob = alt_attacker_ob; alt_attacker_ob = 0; if (attacker_ob) return 1; } query_attack() { /* Changed by Herder */ return attacker_ob; /* OLD if (attacker_ob) return 1; return 0; */ } drop_all_money(verbose) { object mon; if (money == 0) return; mon = clone_object("obj/money"); call_other(mon, "set_money", money); move_object(mon, environment()); if (verbose) { say(cap_name + " drops " + money + " gold coins.\n"); tell_object(this_object(), "You drop " + money + " gold coins.\n"); } money = 0; } /* Wield a weapon. */ wield(w) { if (name_of_weapon) stop_wielding(); name_of_weapon = w; weapon_class = call_other(w, "weapon_class", 0); say(cap_name + " wields " + call_other(w, "query_name", 0) + ".\n"); write("Ok.\n"); } /* Wear some armour. */ wear(a) { object old; if(head_armour) { old = call_other(head_armour, "test_type", call_other(a, "query_type")); if(old) return old; old = head_armour; call_other(a, "link", old); } head_armour = a; /* Calculate new ac */ armour_class = call_other(head_armour, "tot_ac"); say(cap_name + " wears " + call_other(a, "query_name", 0) + ".\n"); write("Ok.\n"); return 0; } add_weight(w) { if (w + local_weight > Str + 10 && level < 20) return 0; local_weight += w; return 1; } heal_self(h) { if (h <= 0) return; hit_point += h; if (hit_point > max_hp) hit_point = max_hp; spell_points += h; if (spell_points > max_sp) spell_points = max_sp; } restore_spell_points(h) { spell_points += h; if (spell_points > max_sp) spell_points = max_sp; } can_put_and_get(str) { return str != 0; } attack_object(ob) { if (call_other(ob, "query_ghost", 0)) return; set_heart_beat(1); /* For monsters, start the heart beat */ if (attacker_ob == ob) { attack(); return; } if (alt_attacker_ob == ob) { alt_attacker_ob = attacker_ob; attacker_ob = ob; attack(); return; } if (!alt_attacker_ob) alt_attacker_ob = attacker_ob; attacker_ob = ob; call_other(attacker_ob, "attacked_by", this_object()); attack(); } /* Funny, why are there two attack_object() in this file ? */ /* attack_object(ob) { if (call_other(ob, "query_ghost", 0)) return; if (attacker_ob == ob) { attack(); return; } if (alt_attacker_ob == ob) { alt_attacker_ob = attacker_ob; attacker_ob = ob; attack(); return; } if (!alt_attacker_ob) alt_attacker_ob = attacker_ob; attacker_ob = ob; attack(); } */ query_ghost() { return ghost; } zap_object(ob) { call_other(ob, "attacked_by", this_object()); say(cap_name + " summons a flash from the sky.\n"); write("You summon a flash from the sky.\n"); experience += call_other(ob, "hit_player", 100000); write("There is a big clap of thunder.\n\n"); } missile_object(ob) { if (spell_points < 10) { write("Too low on power.\n"); return; } spell_name = "magic missile"; spell_dam = random(20); spell_cost = 10; attacker_ob = ob; } shock_object(ob) { if (spell_points < 15) { write("Too low on power.\n"); return; } spell_name = "shock"; spell_dam = random(30); spell_cost = 15; attacker_ob = ob; } fire_ball_object(ob) { if (spell_points < 20) { write("Too low on power.\n"); return; } spell_name = "fire ball"; spell_dam = random(40); spell_cost = 20; attacker_ob = ob; } /* * If no one is here (except ourself), then turn off the heart beat. */ test_if_any_here() { object ob; ob = environment(); if (!ob) return; ob = first_inventory(environment()); while(ob) { if (ob != this_object() && living(ob) && !call_other(ob, "query_npc")) return 1; ob = next_inventory(ob); } return 0; } show_age() { int i; write("age:\t"); i = age; if (i/43200) { write(i/43200 + " days "); i = i - (i/43200)*43200; } if (i/1800) { write(i/1800 + " hours "); i = i - (i/1800)*1800; } if (i/30) { write(i/30 + " minutes "); i = i - (i/30)*30; } write(i*2 + " seconds.\n"); } stop_hunter() { hunter = 0; tell_object(this_object(), "You are no longer hunted.\n"); } /* * This function remains only because of compatibility, as command() now * can be called with an object as argument. */ force_us(cmd) { if (!this_player() || this_player()->query_level() <= level || query_ip_number(this_player()) == 0) { tell_object(this_object(), this_player()->query_name() + " failed to force you to " + cmd + "\n"); return; } command(cmd); } /* This is used by the shop etc. */ add_money(m) { #ifdef LOG_EXP if (this_player() && this_player() != this_object() && query_ip_number(this_player()) && query_ip_number(this_object()) && level < 20 && m >= ROOM_EXP_LIMIT) log_file("EXPERIENCE", ctime(time()) + " " +name + "(" + level + ") " + m + " money by " + this_player()->query_real_name() + "(" + this_player()->query_level() + ")\n"); #endif money = money + m; if (level <= 19 && !is_npc) add_worth(m); } query_money() { return money; } query_exp() { return experience; } query_frog() { return frog; } frog_curse(arg) { if (arg) { if (frog) return 1; tell_object(this_object(), "You turn into a frog !\n"); frog = 1; return 1; } tell_object(this_object(), "You turn HUMAN again.\n"); frog = 0; return 0; } run_away() { object here; int i, j; here = environment(); i = 0; j = random(6); while(i<6 && here == environment()) { i += 1; j += 1; if (j > 6) j = 1; if (j == 1) command("east"); if (j == 2) command("west"); if (j == 3) command("north"); if (j == 4) command("south"); if (j == 5) command("up"); if (j == 6) command("down"); } if (here == environment()) { say(cap_name + " tried, but failed to run away.\n", this_object()); tell_object(this_object(), "Your legs tried to run away, but failed.\n"); } else { tell_object(this_object(), "Your legs run away with you!\n"); } } query_hp() { return hit_point; } query_wimpy() { return whimpy; } query_current_room() { return file_name(environment(this_object())); } query_spell_points() { return spell_points; } stop_fight() { attacker_ob = alt_attacker_ob; alt_attacker_ob = 0; } query_wc() { return weapon_class; } query_ac() { return armour_class; } reduce_hit_point(dam) { object o; if(this_player()!=this_object()) { log_file("REDUCE_HP",query_name()+" by "); if(!this_player()) log_file("REDUCE_HP","?\n"); else { log_file("REDUCE_HP",this_player()->query_name()); o=previous_object(); if (o) log_file("REDUCE_HP", " " + file_name(o) + ", " + o->short() + " (" + creator(o) + ")\n"); else log_file("REDUCE_HP", " ??\n"); } } /* this will detect illegal use of reduce_hit_point in weapons */ hit_point -= dam; if (hit_point <= 0) hit_point = 1; return hit_point; } query_age() { return age; } /*----------- Most of the gender handling here: ------------*/ query_gender() { return gender; } query_neuter() { return !gender; } query_male() { return gender == 1; } query_female() { return gender == 2; } set_gender(g) { if (g == 0 || g == 1 || g == 2) gender = g; } set_neuter() { gender = 0; } set_male() { gender = 1; } set_female() { gender = 2; } query_gender_string() { if (!gender) return "neuter"; else if (gender == 1) return "male"; else return "female"; } query_pronoun() { if (!gender) return "it"; else if (gender == 1) return "he"; else return "she"; } query_possessive() { if (!gender) return "its"; else if (gender == 1) return "his"; else return "her"; } query_objective() { if (!gender) return "it"; else if (gender == 1) return "him"; else return "her"; } /* * Flags manipulations. You are not supposed to do this arbitrarily. * Every wizard can allocate a few bits from the administrator, which * he then may use. If you mainpulate bits that you don't know what they * are used for, unexpected things can happen. */ set_flag(n) { if (flags == 0) flags = ""; #ifdef LOG_FLAGS log_file("FLAGS", name + " bit " + n + " set\n"); if (previous_object()) { if (this_player() && this_player() != this_object() && query_ip_number(this_player())) log_file("FLAGS", "Done by " + this_player()->query_real_name() + " using " + file_name(previous_object()) + ".\n"); } #endif flags = set_bit(flags, n); } test_flag(n) { if (flags == 0) flags = ""; return test_bit(flags, n); } clear_flag(n) { if (flags == 0) flags = ""; #ifdef LOG_FLAGS log_file("FLAGS", name + " bit " + n + " cleared\n"); if (previous_object()) { if (this_player() && this_player() != this_object() && query_ip_number(this_player())) log_file("FLAGS", "Done by " + this_player()->query_real_name() + " using " + file_name(previous_object()) + ".\n"); } #endif flags = clear_bit(flags, n); return 1; } query_stats() { return "str:\t" + Str + "\nint:\t" + Int + "\ncon:\t" + Con + "\ndex:\t" + Dex + "\n"; } query_str() { return Str; } query_int() { return Int; } query_con() { return Con; } query_dex() { return Dex; } /* Note that previous object is 0 if called from ourselves. */ set_str(i) { if (i<1 || i > 20) return; Str = i; } set_int(i) { if (i<1 || i > 20) return; Int = i; max_sp = 42 + Int * 8; } set_con(i) { if (i<1 || i > 20) return; Con = i; max_hp = 42 + Con * 8; } set_dex(i) { if (i<1 || i > 20) return; Dex = i; }