#include <bit.h> #include <corpse.h> #include <move_failures.h> /* 200/5*30 seconds == 20 minutes */ #define DECAY_BASE 200 #define PLAYER_DECAY_BASE 600 #define RANDOM( x ) x[ random( sizeof( x ) ) ] inherit "/std/container"; inherit "/std/living/carrying"; inherit "/std/basic/virtual_quit_control"; private int _decay; private int _corpse_id; private string _owner; private string _race_ob; private string _race_name; private string *_bits_gone; private string *_removed; private string *_permitted; private object _weapon; private object *_armours; private object *_holding; private string _start_pos; private string _nationality; void set_decay_messages(); void remove_creator_corpse( object ob ); void set_race_ob(string _race_ob); int query_corpse() { return 1; } /** * This method returns the id associated with the corpse. * @return the corpse id */ int query_corpse_id() { return _corpse_id; } int query_decay() { return _decay; } void set_decay(int dec) { _decay = dec; } string query_owner() { return _owner; } string *query_permitted() { return _permitted; } void set_race_name(string str) { _race_name = str; } string query_race_name() { return _race_name; } object make_bit(string which_bit); object *make_bits(string *what_bits); void give_permission(string); string query_name() { if (!::query_name()) return "someone"; return::query_name(); } /* query_name() */ string *remove_array_parts(string *a1, string *a2) { int i; string a; if (sizeof(a2)) foreach(a in a2) { for (i = 0; i < sizeof(a1); i++) { if (a == a1[i]) { a1 = delete(a1, i, 1); break; } } } return a1; } void setup() { _bits_gone = ({ }); _removed = ({ }); add_property("cureable", 1); add_property("determinate", "the "); _owner = "noone"; _race_name = "womble"; _decay = DECAY_BASE; add_plural("corpses"); add_plural("bodies"); add_plural("carcasses"); add_alias(({ "corpse", "body", "carcass" })); set_short("corpse"); set_long("A corpse, it looks dead.\n"); set_weight(STD_CORPSE_WEIGHT); set_race_ob("/std/races/unknown"); _permitted = ({ }); _armours = ({ }); _holding = ({ }); add_extra_look(this_object()); } /* setup() */ /** @ignore yes */ string extra_look() { if(sizeof(_removed)) return "It appears to be missing its " + query_multiple_short(_removed) + ".\n"; return ""; } void give_permission(string words) { _permitted += ({ words }); } int get(mixed dest) { if (query_property("player") && dest) { if (!this_player()) return::get(dest); if (member_array((string) this_player()->query_name(), _permitted) == -1) return MOVE_INVALID_DEST; } return::get(dest); } /* * This checks if someone is removing something from their own, or someone * elses corpse. If it's someone elses and they're on the permit list then * it allows the removal. * If they aren't on the permit list it passes it to /std/container's checks * which will prevent removal if one or other is not a PK or allow it if * they're both PK and record a theft event. * */ int test_remove(object thing, int flag, mixed dest) { int i; if (base_name(environment()) == "/room/rubbish") { return 1; } if (thing->query_property("my corpse") == this_object()) { return 1; } if (!query_property("player")) { return 1; } if (!sizeof(_permitted) || !this_player()) { i =::test_remove(thing, flag, dest); } else if (member_array((string) this_player()->query_name(), _permitted) == -1) { i =::test_remove(thing, flag, dest); } else { write((string) thing->the_short() + " $V$0=buzzes,buzz$V$ for a " "moment.\n"); return 1; } // Record looting of quest weapons. This is useful for tracking purposes. if (i && this_player() && thing && sizeof(thing->effects_matching("mudlib.owned.weapon")) && this_player()->query_name() != thing->query_owner() && member_array((string) this_player()->query_name(), _permitted) == -1) { log_file("LOOT", "%s %s took %s [%s] from %s.\n", ctime(time()), this_player()->query_cap_name(), thing->query_short(), thing->query_owner(), this_object()->query_owner()); } if (i) { CORPSE_HANDLER->save_corpse(this_object()); } return i; } /* test_remove() */ string long(string words, int dark) { if (dark == 2 || dark == -2) { if (query_living_contents(0) != "") { return::long(words, dark) + "Carrying, wearing or holding some " "things you cannot make out.\n"; } } return::long(words, dark) + query_living_contents(0); } /* long() */ /** * This method initially sets up the corpse object. Generally * speaking words should be 0 and the 'thing' variable should be * set to the object which just died. * @param words the owner of the object * @param thing the object to get the ownership data from */ void set_owner(string words, object thing) { string det; string *tmp; if (stringp(words)) { _owner = words; } else { det = (string) thing->query_property("determinate"); if (stringp(det)) { _owner = det + (string) thing->short(); } else { _owner = add_a((string) thing->short()); } } if (thing && thing->query_property("player")) { set_decay(PLAYER_DECAY_BASE); // the owner should always be permitted. give_permission(thing->query_name()); catch(_corpse_id = CORPSE_HANDLER->query_next_corpse_id()); _start_pos = thing->query_start_pos(); set_ownership(thing->query_name()); add_property("player", 1); } set_name("corpse"); set_short("corpse of " + _owner); add_adjective(({ "corpse", "of" })); tmp = explode(lower_case(_owner), " "); if (sizeof(tmp) > 1) { add_alias(tmp[<1]); } add_adjective(tmp); add_adjective(tmp[<1] + "'s"); if (thing) { _nationality = thing->query_nationality(); if(userp(thing)) { set_main_plural("corpses of " + thing->short()); } else if (thing->query_main_plural() && !userp(thing)) { set_main_plural("corpses of " + (string) thing->query_main_plural()); } else { set_main_plural("corpses of " + pluralize((string) thing->short())); } } else { set_main_plural("corpses"); } set_long("This is the dead body of " + _owner + ".\n"); if (thing && thing->query_weight(1)) { set_weight((int) thing->query_weight(1)); } else { set_weight(STD_CORPSE_WEIGHT); } if ( thing && ( thing->query_creator() || thing->query_property( "test character" ) ) ) remove_creator_corpse( thing ); BIT_CONTROLLER->add_bit(this_object()); if (thing && thing->query_property("player")) { catch(CORPSE_HANDLER->register_corpse(this_object())); } } /* set_owner() */ //Fun - I think we still do that. void remove_creator_corpse( object ob ) { call_out( (: move( "/room/morgue", "$N lands in the room with a thud. The kind of thud a " "sack of steaks dropped from a great height would make.\n", "" ) :), 6 ); call_out( (: tell_room( environment( $(ob) ), "squeeek THUMP squeeeek THUMP " "squeeek THUMP squeeeek THUMP\n" ) :), 1 ); call_out( (: tell_room( environment( $(ob) ), "An igor pushing an old and " "battered wheelbarrow appears from somewhere.\n" ) :), 2 ); call_out( (: tell_room( environment( $(ob) ), "%^CYAN%^The igor says \"Well bugger " "me. I've been wanting another one " "of those. The mathter will be so pleased.\"%^RESET%^\n" ) :), 4 ); call_out( (: tell_room( environment( $(ob) ), "Igor cuts something off the corpse" " and stuffs it in his pocket before throwing the remains of the corpse into " "his barrow and shuffling away. \n" ) :), 6 ); call_out( (: CORPSE_HANDLER->save_corpse(this_object()) :), 8 ); return; } /** * This is called to decay the corpse. It is done in a continuous * call out loop until all of the decay has been completed. */ void do_decay() { int rate; if (!environment()) { return; } rate = 5 + (int) (environment()->query_property("decay rate")); if (rate > 0) { _decay -= rate; } set_decay_messages(); if (_decay > 0) { CORPSE_HANDLER->save_corpse(this_object()); } } /* do_decay() */ /** * This method sets up the various 'states; of the corpse to show * how decayed it is. */ void set_decay_messages() { if (!_race_name) { _race_name = (string) _race_ob->query_name(); } switch (_decay) { case 101..PLAYER_DECAY_BASE: break; case 51..100: if (!find_player(query_name())) { set_short("somewhat decayed remains of " + add_a(query_name())); set_main_plural("somewhat decayed remains of " + pluralize(query_name())); } break; case 1..50: set_short("decayed remains of " + add_a(_race_name)); set_main_plural("decayed remains of " + pluralize(_race_name)); set_long("This is the dead body of " + add_a(_race_name) + ".\n"); break; default: CORPSE_HANDLER->deregister_corpse(this_object()); set_ownership(0); all_inventory()->move(environment()); move("/room/rubbish"); } } /* do_decay() */ void set_race_ob(string s) { _race_ob = s; } string query_race_ob() { return _race_ob; } string query_bit_left(string s) { string *bits; bits = _race_ob->query_possible_bits(s); if (!bits || !sizeof(bits)) { return 0; } bits = remove_array_parts(bits, _bits_gone); if (!sizeof(bits)) { return 0; } return bits[0]; } /* query_bit_left() */ string *query_bit_left_pl(string s) { string *bits; bits = _race_ob->query_possible_plural_bits(s); if (!bits || !sizeof(bits)) { return 0; } bits = remove_array_parts(bits, _bits_gone); if (!sizeof(bits)) { return 0; } return bits; } /* query_bit_left() */ string *query_edible_bits_left() { string *bits; string *inedible; bits = _race_ob->query_possible_bits(); inedible = _race_ob->query_all_inedible(); if (!bits || !inedible) { return ({ }); } return remove_array_parts(bits, _bits_gone) - inedible; } /* query_edible_bits_left() */ varargs object *find_inv_match(string s, object looker) { string bit; string *bits; object *weap; object wep; int cut; if (undefinedp(s)) { return all_inventory(); } bit = query_bit_left(s); bits = query_bit_left_pl(s); if (!bit && !sizeof(bits)) { return all_inventory(); } cut = 0; if (looker) { weap = looker->query_weapons(); } if (sizeof(weap)) { foreach(wep in weap) { if (wep->id("dagger") || wep->id("knife")) { cut = 1; } } } if (bit) { if (cut || _race_ob->query_pluckable(bit)) { return ({ make_bit(bit) }); } if (sizeof(weap)) { tell_object(looker, "You can only cut things from a corpse " "with a knife or dagger.\n"); } else { tell_object(looker, "You can't cut bits from a corpse with your " "bare hands.\n"); } return ({ }); } if (sizeof(bits)) { if (cut) { if (sizeof(bits) > 5) { return make_bits(bits[0..4]); } else { return make_bits(bits); } } foreach(bit in bits) { if (!((string) _race_ob->query_pluckable(bit))) { bits -= ({ bit }); } } if (sizeof(bits)) { if (sizeof(bits) > 5) { return make_bits(bits[0..4]); } else { return make_bits(bits); } } if (sizeof(weap)) { tell_object(looker, "You can only cut things from a corpse " "with a knife or dagger.\n"); } else { tell_object(looker, "You can't cut bits from a corpse with your " "bare hands.\n"); } return ({ }); } } /* find_inv_match() */ object make_bit(string which_bit) { mixed *bit; object bitobj; bit = _race_ob->query_bit(which_bit); if ((sizeof(bit[2][2]) > 1) && stringp(bit[2][2][0])) { bitobj = clone_object(bit[2][2][0]); } else { bitobj = clone_object("/std/bit"); } bitobj->set_race_ob(_race_ob); if (_race_name) { bitobj->set_race_name(_race_name); } else { bitobj->set_race_name(_race_ob->query_name()); } bitobj->set_corpse_weight(query_weight()); if (!_race_ob->query_eat(bit[BIT_NAME])) { bitobj->set_bit(bit[0], 0); } else { bitobj->set_bit(bit[0], (_decay * 2) / 3); } _bits_gone |= bit[BIT_EXTRA][3..] + ({ bit[BIT_NAME] }); _removed += ({ bit[BIT_NAME] }); if (which_bit == "head") { //set_long(query_long() + "It is decapitated.\n"); set_short("decapitated corpse of " + _owner); add_adjective("decapitated"); } if (bitobj->move(this_object()) != MOVE_OK) { if (environment()) { bitobj->move(environment()); } } bitobj->add_property("my corpse", this_object()); return bitobj; } /* make_bit() */ object *make_bits(string *what_bits) { string bit; object *bits = ({ }); foreach(bit in what_bits) { bits += ({ make_bit(bit) }); } return bits; } /* make_bits() */ string *query_bits_gone() { return _bits_gone; } mixed *add_bit_gone(string bit) { string *poss_bits; string tempbit; mixed *bit_details; poss_bits = remove_array_parts(_race_ob->query_possible_bits(bit), _bits_gone); if (!sizeof(poss_bits)) { return 0; } bit_details = _race_ob->query_bit(poss_bits[0]); _bits_gone += ({ bit_details[BIT_NAME] }); foreach(tempbit in bit_details[BIT_EXTRA][3..sizeof(bit_details[BIT_EXTRA])]) { _bits_gone += ({ tempbit }); } return bit_details; } /* add_bit_gone() */ void set_bits_gone(string *bits) { int i; _bits_gone = ({ }); for (i = 0; i < sizeof(bits); i++) { add_bit_gone(bits[i]); } } /* set_bits_gone() */ string *query_bits_left() { int i; int j; string *all_bits; mixed *bits; bits = _race_ob->query_bits(); all_bits = ({ }); for (i = 0; i < sizeof(bits); i += 3) { if (bits[i + 2][2]) { for (j = 0; j < bits[i + 2][2][1]; j++) { all_bits += ({ bits[i] }); } } } return remove_array_parts(all_bits, _bits_gone); } /* query_bits_left */ /* this for formatting of objects sake */ object *query_armours() { int i; _armours -= ({ 0 }); for (i = 0; i < sizeof(_armours); i++) { if ((object) _armours[i]->query_worn_by() != this_object()) { _armours = delete(_armours, i, 1); i--; } } return _armours + ({ }); } /* query_armours() */ object *query_wearing() { return query_armours(); } void set_armours(object * things) { int i; _armours = ({ }); for (i = 0; i < sizeof(things); i++) { if (things[i]->query_no_limbs()) { _holding += ({ things[i] }); } else { _armours += ({ things[i] }); } } } /* set_armours() */ void remove_armour(object arm) { _armours -= ({ arm }); } object query_weapon() { return _weapon; } void set_weapon(object wpn) { _weapon = wpn; } void unwield_weapon(object wpn) { if (_weapon == wpn) _weapon = 0; } object *query_holding() { return _holding; } void set_holding(object * hold) { _holding += hold; } int *set_unhold(object ob) { int pos; if ((pos = member_array(ob, _holding)) == -1) { return ({ }); } if (!ob->set_holder(0)) { return ({ }); } _holding = _holding - ({ ob }); return ({ pos }); } /* set_hold() */ int *set_hold(object ob, int pos) { if (member_array(ob, _holding) != -1) { return ({ }); } _holding += ({ ob }); return ({ pos }); } /* set_hold() */ int move_or_destruct(object dest) { if (objectp(dest)) { move_object(dest); } else { move("/room/void"); } return 1; } /* * This was added so that people dont loose stuff just in case it gets * dested for weird reasons. * Pinkfish 3rd may 1993 */ void dest_me() { mixed *xp; object ob; // This hands out the rest of the death Xp when the object is destructed // hopefully by burial or ventisepelating or whatever. xp = query_property("XP"); if (xp && sizeof(xp) == 2) { foreach(ob in xp[0]) { if (ob) { ob->adjust_xp(xp[1], 1); } } } BIT_CONTROLLER->remove_bit(this_object()); if (environment()) { all_inventory()->move(environment()); } ::dest_me(); } /* dest_me() */ /* * Added in save code - Tue Feb 25 00:57:06 WST 1997 * to do wombly stuff by Pinkfish. * Only does a bit of wombley stuff though. */ mapping query_static_auto_load() { return int_query_static_auto_load(); } /* query_static_auto_load() */ mapping query_dynamic_auto_load() { mapping tmp; string pos; tmp = ([ "::" : ::query_dynamic_auto_load(), "decay" : _decay, "owner" : _owner, "race ob" : _race_ob, "id" : _corpse_id, "last pos" : pos, "start pos" : _start_pos, "nationality" : _nationality, "ownership" : query_ownership(), "race name" : _race_name, "bits gone" : _bits_gone, ]); return tmp; } /* query_dynamic_auto_load() */ void init_dynamic_arg(mapping map, object) { if (map["::"]) { ::init_dynamic_arg(map["::"]); } if (map["id"]) { _corpse_id = map["id"]; } if (map["decay"]) { _decay = map["decay"]; } if (map["owner"]) { _owner = map["owner"]; } if (map["race ob"]) { _race_ob = map["race ob"]; } _nationality = map["nationality"]; set_ownership(map["ownership"]); if (map["race name"]) { _race_name = map["race name"]; } if (map["bits gone"]) { _bits_gone = map["bits gone"]; } if (map["start pos"]) { _start_pos = map["start pos"]; } set_decay_messages(); } /* init_dynamic_arg() */ /** * This is used by the corpse handler to get the data used to save * the corpse. */ string query_save_data() { string pos; object env; env = environment(); while (env && environment(env)) { if (living(env) && env->query_property("player")) { return CORPSE_IN_PLAYER; } env = environment(env); } // First see if we are inside a living or something. pos = find_start_pos( this_object(), env ); return save_variable(({ query_dynamic_auto_load(), query_static_auto_load(), pos })); } /** * This method is called by the corpse handler to setup the corpse * properly after it loads. */ void setup_corpse_from_save(string str_data, string name, int id) { mixed data; int res; if (str_data == CORPSE_IN_PLAYER) { return ; } data = restore_variable(str_data); // Restore the data. init_static_arg(data[1]); init_dynamic_arg(data[0], 0); // Make sure we have decay called on us BIT_CONTROLLER->add_bit(this_object()); // Move the thing to the right spot. res = MOVE_NO_DROP; tell_creator("pinkfish", "Trying to restore to location %O [%O]\n", data[2], this_object()); catch(res = this_object()->move(data[2], "$N looks confused and pops out of the ground.\n", "$N looks confused and pops out of the ground.\n")); if (res != MOVE_OK) { tell_creator("pinkfish", "Trying to restore to location %O [%O]\n", _start_pos, this_object()); catch(res = this_object()->move(_start_pos, "$N looks confused and pops out of the ground.\n", "$N looks confused and pops out of the ground.\n")); } if (res != MOVE_OK) { tell_creator("pinkfish", "Trying to restore to location %O [%O]\n", "/room/rubbish", this_object()); move("/room/rubbish"); } } mixed *stats() { return::stats() + ({ ({ "decay", _decay, }), ({ "owner", _owner, }), ({ "race ob", _race_ob, }), ({ "race name", _race_name, }) }); } /* stat() */ /** * @ignore yes * This is added so that it acts like a living object and * things like non-movable signs cannot be added to it. */ int test_add(object ob, int flag) { // Allow bits in at any time... if (ob->query_bit_data()) { return 1; } return !flag; } /* test_add() */ /** * @ignore yes * Stop people form getting stuff out of containers in corpses. */ int can_find_match_reference_inside_object(object thing, object player) { if (!query_property("player")) { return 1; } if (member_array(player->query_name(), _permitted + ({ lower_case(_owner) })) == -1) { return 0; } return 1; } /* can_find_match_reference_inside_object() */ int can_find_match_recurse_into(object player) { return 0; } /* can_find_match_recurse_into() */