#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() */