#include <bit.h> #include <corpse.h> #include <move_failures.h> #include <weapon.h> // 200/5*30 seconds == 20 minutes #define DECAY_BASE 200 #define PLAYER_DECAY_BASE 600 inherit "/std/container"; inherit "/std/living/carrying"; int decay, gender; string owner, race_ob, race_name, *bits_gone, *permitted; object weapon, *armours, *holding; /** * This method queries whether or not we are a corpse. * @return always returns 1 */ int query_corpse() { return 1; } /** * This method returns our decay value. * @return the decay value */ int query_decay() { return decay; } /** * This method sets the decay value for the corpse. * @param dec the decay value to set */ void set_decay( int dec ) { decay = dec; } /** * This returns the short desc or name of the owner of the corpse. * @return the short desc or name of the owner of the corpse */ string query_owner() { return owner; } /** * This method returns the array of people who are * permitted to take things from our corpse. * @return people permitted to take things from our corpse */ string *query_permitted() { return permitted; } /** * This method sets the race object of the corpse. * @param str the race object to set */ void set_race_ob( string str ) { race_ob = str; } /** * This method returns the corpse's race object. * @return the race object */ string query_race_ob() { return race_ob; } /** * This method sets the race name of the corpse. * @param str the race name to set */ void set_race_name( string str ) { race_name = str; } /** * This method returns the corpse's race name. * @return the race name */ string query_race_name() { return race_name; } /** * This method sets the gender of the corpse. * @param i the gender of the corpse */ void set_gender( int i ) { gender = i; } /** * This method returns the gender of the corpse. * @return the gender of the corpse */ int query_gender() { return gender; } /** * This method returns a descriptive string of the * gender of the corpse. * @return a string describing the gender of the corpse */ string query_gender_string() { switch( gender ) { case 1 : return "male"; case 2 : return "female"; default : return "neuter"; } } /* query_gender_string() */ void setup() { bits_gone = ({ }); owner = "noone"; race_name = "womble"; gender = 0; decay = DECAY_BASE; add_property("cureable", 1 ); add_property("determinate", "the "); set_name("corpse"); set_short("corpse"); add_alias( ({"corpse", "body", "carcass"}) ); set_long("This is a corpse. It looks dead.\n"); set_weight( STD_CORPSE_WEIGHT ); set_race_ob("/std/races/unknown"); permitted = ({ }); armours = ({ }); holding = ({ }); } /* setup() */ /** * This method adds a person to the array of people we are * allowing to take things from our corpse. * @param who the name of the person to permit */ void give_permission( string who ) { permitted += ({ who }); } /** @ignore yes */ int get( mixed dest ) { if( query_property("player") && dest && TP ) { if( TP->query_name() != query_ownership() && member_array( TP->query_name(), permitted ) == -1 ) return MOVE_INVALID_DEST; } return ::get(dest); } /* get() */ /** @ignore yes */ int test_remove( object thing, int flag, mixed dest ) { if( thing->query_property("my corpse") == TO ) return 1; if( query_property("player") ) { if( !sizeof( permitted ) || !TP ) return ::test_remove( thing, flag, dest ); if( member_array( TP->query_name(), permitted ) == -1 ) return ::test_remove( thing, flag, dest ); // Use add_message here so that verb expansion would work. TP->add_message( thing->the_short()+" $V$0=buzzes,buzz$V$ for a " "moment.\n"); } return 1; } /* test_remove() */ /** * This method sets the owner of the corpse. * It will also set up various other things such as adjectives, etc. * @param who the name or short desc of the owner * @param thing the object of the owner */ void set_owner( string who, object thing ) { string str; if( stringp( who ) ) { owner = who; } else { if( stringp( str = thing->query_property("determinate") ) ) owner = str + thing->short(); else owner = add_a( thing->short() ); } if( thing && thing->query_property("player") ) set_decay(PLAYER_DECAY_BASE); set_short("corpse of "+owner ); if( thing ) { if( thing->query_property("player") ) { set_main_plural("corpses of "+owner ); } else if( thing->query_main_plural() ) { set_main_plural("corpses of "+thing->query_main_plural() ); } else { set_main_plural("corpses of "+ pluralize( thing->short() ) ); } } else { set_main_plural("corpses"); } set_long("This is the dead body of "+owner+".\n"); if( thing && thing->query_actual_weight() ) set_weight( thing->query_actual_weight() ); else set_weight( STD_CORPSE_WEIGHT ); BITS_H->add_bit(TO); } /* set_owner() */ /** @ignore yes */ void set_decay_messages() { if( !race_name ) race_name = race_ob->query_name(); switch( decay ) { case 101 .. PLAYER_DECAY_BASE : break; case 51..100: if( !query_property("player") ) { set_short("somewhat decayed remains of a corpse"); set_short("somewhat decayed remains of "+( gender ? "a "+ query_gender_string()+" "+race_name : add_a(race_name) ) ); set_main_plural("somewhat decayed remains of "+ pluralize(race_name) ); } break; case 1..50: set_short("decayed remains of "+( gender ? "a "+ query_gender_string()+" "+race_name : add_a(race_name) ) ); set_main_plural("decayed remains of "+pluralize(race_name) ); set_long("This is the dead body of "+( gender ? "a "+ query_gender_string()+" "+race_name : add_a(race_name) )+".\n"); break; default : set_ownership( 0 ); all_inventory()->move( ENV(TO) ); move("/room/rubbish"); } } /* set_decay_messages() */ /** @ignore yes */ void do_decay() { int rate; if( !ENV(TO) ) return; rate = 5 + ENV(TO)->query_property("decay rate"); if( rate > 0 ) decay -= rate; set_decay_messages(); } /* do_decay() */ /** @ignore yes */ string *remove_array_parts( string *a1, string *a2 ) { if( sizeof(a2) ) { foreach( string a in a2 ) { for( int i = 0; i < sizeof(a1); i++ ) { if( a == a1[i] ) { a1 = delete( a1, i, 1 ); break; } } } } return a1; } /* remove_array_parts() */ /** * This method queries whether or not a specific bit is left in the corpse. * @param str the bit to check for * @return the bit */ string query_bit_left( string str ) { string *bits; bits = race_ob->query_possible_bits(str); if( !bits || !sizeof(bits) ) return 0; bits = remove_array_parts( bits, bits_gone ); if( !sizeof(bits) ) return 0; return bits[0]; } /* query_bit_left() */ /** * This method queries whether or not specific bits are left in the corpse. * This checks for the plural bits. * @param str the bit to check for * @return an array of bits */ string *query_bit_left_pl( string str ) { string *bits; bits = race_ob->query_possible_plural_bits(str); if( !bits || !sizeof(bits) ) return 0; bits = remove_array_parts( bits, bits_gone ); if( !sizeof(bits) ) return 0; return bits; } /* query_bit_left() */ /** * This method returns the edible bits that are still in the corpse. * @return the edible bits still in the corpse */ string *query_edible_bits_left() { string *bits, *inedible; if( !( bits = race_ob->query_possible_bits() ) || !( inedible = race_ob->query_all_inedible() ) ) return ({ }); return remove_array_parts( bits, bits_gone ) - inedible; } /* query_edible_bits_left() */ /** * This object creates a bit and returns its object, * if one is present in the corpse. * @param which_bit the name of the bit to create * @return the new bit */ object make_bit( string which_bit ) { object bitobj; string tmp; mixed bit; int i, j; 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 ); } for( i = 0; i < sizeof(bits_gone); i++ ) { j = member_array( bits_gone[i], bit[BIT_EXTRA] ); if( j != -1 ) bitobj->add_bit_gone( bits_gone[i] ); } bits_gone += ({ bit[ BIT_NAME ] }); foreach( tmp in bit[ BIT_EXTRA ][ 3 .. sizeof(bit[BIT_EXTRA]) ]) { if( arrayp( race_ob->query_bit( tmp )[2][2] ) && intp( race_ob->query_bit( tmp )[2][2][1] ) ) { for( i = 0; i < race_ob->query_bit( tmp )[2][2][1]; i++ ) bits_gone += ({ tmp }); } } if( which_bit == "head") { set_long( query_long()+"It is decapitated.\n"); set_short("decapitated corpse of "+owner ); } if( bitobj->move(TO) && ENV(TO) ) bitobj->move(ENV(TO)); bitobj->add_property("my corpse", TO ); return bitobj; } /* make_bit() */ /** * This method creates actual object from the required bits. * @param what_bits the bits to create objects from * @return an array of newly created bit objects */ object *make_bits( string *what_bits ) { string bit; object *bits; bits = ({ }); foreach( bit in what_bits ) bits += ({ make_bit(bit) }); return bits; } /* make_bits() */ /** @ignore yes */ varargs object *find_inv_match( string str, object looker ) { string bit, *bits; object *weps; mixed data; int cut; if( undefinedp( str ) ) return all_inventory(); bit = query_bit_left(str); bits = query_bit_left_pl(str); if( !bit && !sizeof(bits) ) return all_inventory(); if( looker ) weps = looker->query_weapons(); if( sizeof(weps) ) { foreach( object wep in weps ) { data = wep->query_attack_data(); for( int i = 0; i < sizeof(data); i += W_ARRAY_SIZE ) { if( data[i+W_TYPE] == "sharp") { cut = 1; break; } } // Break out of the foreach loop as well. if( cut ) break; } } if( bit ) { if( cut || race_ob->query_pluckable(bit) ) return ({ make_bit(bit) }); tell_object( looker, "You need a sharp implement to cut things from "+ the_short()+".\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( !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 ); } tell_object( looker, "You need a sharp implement to cut things from "+ the_short()+".\n"); return ({ }); } } /* find_inv_match() */ /** * This method returns the array of bits that have been * removed from the corpse. * @return the array of bits gone */ string *query_bits_gone() { return bits_gone; } /** * This method adds a bit to the array of bits gone in the corpse. * @param bit the bit to remove * @return the data of the newly removed bit */ mixed *add_bit_gone( string bit ) { string *poss_bits, tmp; mixed details; int i; if( !poss_bits = race_ob->query_possible_bits(bit) ) return 0; poss_bits = remove_array_parts( poss_bits, bits_gone ); if( !sizeof(poss_bits) ) return 0; details = race_ob->query_bit( poss_bits[ 0 ] ); bits_gone += ({ details[ BIT_NAME ] }); foreach( tmp in details[ BIT_EXTRA ][3..sizeof(details[BIT_EXTRA]) ] ) { if( arrayp( race_ob->query_bit( tmp )[2][2] ) && intp( race_ob->query_bit( tmp )[2][2][1] ) ) for( i = 0; i < race_ob->query_bit( tmp )[2][2][1]; i++ ) bits_gone += ({ tmp }); } return details; } /* add_bit_gone() */ /** @ignore yes */ 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() */ /** * This method returns all the bits left in the corpse. * @return all bits left in the corpse */ string *query_bits_left() { string *all_bits; mixed *bits; int i, j; 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 */ /** @ignore yes */ object *query_armours() { int i; armours -= ({ 0 }); for( i = 0; i < sizeof(armours); i++ ) { if( armours[ i ]->query_worn_by() != TO ) { armours = delete( armours, i, 1 ); i--; } } return copy(armours); } /* query_armours() */ /** @ignore yes */ object *query_wearing() { return query_armours(); } /** @ignore yes */ 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() */ /** @ignore yes */ void remove_armour( object arm ) { armours -= ({ arm }); } /** @ignore yes */ void set_weapon( object wep ) { weapon = wep; } /** @ignore yes */ object query_weapon() { return weapon; } /** @ignore yes */ void set_holding( object *hold ) { holding += hold; } /** @ignore yes */ object *query_holding() { return holding; } /** @ignore yes */ int *set_unhold( object ob ) { int pos; if( ( pos = member_array( ob, holding ) ) == -1 ) return ({ }); if( !ob->set_holder( 0 ) ) return ({ }); holding -= ({ ob }); return ({ pos }); } /* set_hold() */ /** @ignore yes */ int *set_hold( object ob, int pos ) { if( member_array( ob, holding ) != -1 ) return ({ }); holding += ({ ob }); return ({ pos }); } /* set_hold() */ /** @ignore yes */ private string pluralize_bit( string *arr ) { if( sizeof(arr) == 1 ) return add_a( arr[0] ); return query_num( sizeof(arr) )+" "+pluralize( arr[0] ); } /* do_plural_bit() */ /** @ignore yes */ string long( string str, int dark ) { str = ::long( str, dark ); if( sizeof( query_bits_gone() ) ) { string *arr; arr = map( unique_array( query_bits_gone(), (: $1 :) ), (: pluralize_bit($1) :) ); str += "It appears to be missing "+query_multiple_short(arr)+".\n"; } if( dark == 2 || dark == -2 ) { if( query_living_contents( 0 ) != "") return str + "Carrying, wearing or holding some things you " "cannot make out.\n"; } return str + query_living_contents( 0 ); } /* long() */ /** @ignore yes */ void dest_me() { object ob; mixed xp; // 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(TO); if( ENV(TO) ) all_inventory()->move( ENV(TO) ); ::dest_me(); } /* dest_me() */ mapping query_static_auto_load() { return int_query_static_auto_load(); } /* query_static_auto_load() */ /** @ignore yes */ mapping query_dynamic_auto_load() { mapping map; map = ([ "::" : ::query_dynamic_auto_load(), "decay" : decay, "owner" : owner, "race ob" : race_ob, "race name" : race_name, ]); if( gender ) map["gender"] = gender; if( sizeof(bits_gone) ) map["bits gone"] = bits_gone; if( query_ownership() ) map["ownership"] = query_ownership(); return map; } /* query_dynamic_auto_load() */ /** @ignore yes */ void init_dynamic_arg( mapping map ) { if( map["ownership"] ) set_ownership( map["ownership"] ); if( map["::"] ) ::init_dynamic_arg( map["::"] ); if( map["decay"] ) decay = map["decay"]; if( map["owner"] ) owner = map["owner"]; if( map["race ob"] ) race_ob = map["race ob"]; if( map["race name"] ) race_name = map["race name"]; if( map["gender"] ) gender = map["gender"]; if( map["bits gone"] ) bits_gone = map["bits gone"]; set_decay_messages(); } /* init_dynamic_arg() */ /** @ignore yes */ mixed *stats() { return ::stats() + ({ ({"decay", decay }), ({"owner", owner }), ({"ownership", query_ownership() }), ({"race ob", race_ob }), ({"race name", race_name }), ({"gender", query_gender_string() }), }); } /* 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 ) { if( !ENV(ob) ) return 1; return !flag; } /* test_add() */