roh/conf/area/
roh/game/talk/
roh/help/
roh/monsters/ocean/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.44b/
/*
 * die.cpp
 *	 New death code
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2009 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
// Mud includes
#include "mud.h"
#include "die.h"
#include "factions.h"
#include "effects.h"
#include "commands.h"
#include "guilds.h"
#include "unique.h"
#include <math.h>

//********************************************************************
//						isHardcore
//********************************************************************
// Return: true if player is flagged as Hardcore (death = permanent)

bool Player::isHardcore() const {
	return(flagIsSet(P_HARDCORE) && !isStaff());
}

//********************************************************************
//						hardcoreDeath
//********************************************************************
bool canDrop(const Player* player, const Object* object, const Property* p);
bool delete_drop_obj(const BaseRoom* room, const Object* object, bool factionCanRecycle);

void hardcoreDeath(Player* player) {
	if(!player->isHardcore())
		return;
	bool factionCanRecycle = !player->parent_rom || Faction::willDoBusinessWith(player, player->parent_rom->getFaction());


	for(int i=0; i<MAXWEAR; i++) {
		if(player->ready[i] && (!(player->ready[i]->flagIsSet(O_CURSED) && player->ready[i]->getShotscur() > 0))) {
			player->ready[i]->clearFlag(O_WORN);
			player->doRemove(i);
		}
	}
	player->computeAC();
	player->computeAttackPower();


	BaseRoom* room = player->getRoom();
	Object* object=0;
	otag *op = player->first_obj,*oprev=0;

	while(op) {
		oprev = op;
		op = op->next_tag;
		object = oprev->obj;

		if(delete_drop_obj(room, object, factionCanRecycle) || !canDrop(player, object, 0) || object->flagIsSet(O_STARTING))
			continue;

		player->delObj(object, false, true, true, false);
		object->addToRoom(room);
	}
	player->checkDarkness();

	if(player->coins[GOLD]) {
		object=0;
		loadObject(MONEY_OBJ, &object);
		object->nameCoin("gold", player->coins[GOLD]);
		object->value.set(player->coins[GOLD], GOLD);
		player->coins.sub(player->coins[GOLD], GOLD);
		object->addToRoom(room);
	}

	player->printColor("\n\n                 ^YYou have died.\n\n");
	player->printColor("^RAs a hardcore character, this death is permanent.\n");
	player->print("\n\n");
	player->statistics.display(player, true);
	player->print("\n");
	broadcast("^#^R### %s's soul is lost forever.", player->name);
	deletePlayer(player);
}

//********************************************************************
//						isHoliday
//********************************************************************
// if a string is returned, player will get bonus exp

bstring isHoliday() {
	bstring str = gConfig->getMonthDay();

	// see if today is a holiday
	if(str == "Oct 31")
		return("Happy Halloween!");

	if(str == "Dec 24" || str == "Dec 25")
		return("Merry Christmas!");

	if(str == "Dec 31" || str == "Jan  1")
		return("Happy New Year!");

	return("");
}


// TODO: Add in perm killing code

//********************************************************************
//						adjustAlignment
//********************************************************************

void Player::adjustAlignment(Monster *victim) {
	int adjust = victim->getAlignment() / 8;

	if(victim->getAlignment() < 0 && victim->getAlignment() > -8)
		adjust = -1;
	if(victim->getAlignment() > 0 && victim->getAlignment() < 8)
		adjust = 1;

	bool toGood = adjust < 0;
	bool toEvil = adjust > 0;
	bool toNeutral = ((alignment > 0 && toEvil) ||
					  (alignment < 0 && toGood) );

	// handle all the move-away-from-neutral cases
	if(!toNeutral) {
		// if they're gradius, an anti-gradius race can only help their alignment.
		// same goes with the paly/dk war.
		if(deity == GRADIUS) {
			if(victim->isAntiGradius())
				adjust = 0;
		 	if(cClass == PALADIN && victim->getClass() == DEATHKNIGHT)
		 		adjust = 0;
		}
		// werewolves and vampires can always kill each other
		if(isEffected("vampirism") && victim->isEffected("lycanthropy"))
		  	adjust = 0;
		if(isEffected("lycanthropy") && victim->isEffected("vampirism"))
		  	adjust = 0;
		if(deity == CERIS && victim->isUndead())
			adjust /= 2;
	}

	// paladin / deathknight war
	// gradius paladins are taken care of above
	if(deity != GRADIUS) {
		if(cClass == PALADIN && victim->getClass() == DEATHKNIGHT && toEvil)
			adjust = 0;
		if(cClass == DEATHKNIGHT && victim->getClass() == PALADIN && toGood)
			adjust = 0;
	}

	alignment -= adjust;
	alignment = MAX(-1000, MIN(1000, alignment));
}

//********************************************************************
//						dropCorpse
//********************************************************************
// Parameters:	<killer> The creature attacking
// Handles the dropping of items from creatures

void Monster::dropCorpse(Creature *killer) {
	BaseRoom* room = getRoom();
	bstring str = "", carry = "";
	otag		*op=0;
	Object		*object=0;
	Player*		player=0;
	Player*		pMaster = isPet() ? following->getPlayer() : 0;
	bool		destroy = room->flagIsSet(R_DESTROYS_ITEMS);

	if(killer)
		player = killer->getMaster();

	killDarkmetal();
	killUniques();

	// Drop weapons also
	if(ready[WIELD-1]) {
		Lore::remove(pMaster, ready[WIELD-1], true);
		if(destroy) {
			unequip(WIELD, UNEQUIP_DELETE);
		} else {
			if(player)
				player->printColor("%M dropped their weapon: %1P.\n", this, ready[WIELD - 1]);
			Object* drop = unequip(WIELD, UNEQUIP_NOTHING, false);
			if(drop)
				drop->addToRoom(room);
		}
	}


	// list all on death
	// check for player: for mob killing pet, player is null, and then we don't
	// care about listing items dropped
	if(!destroy && player)
		str = listObjects(player, first_obj, true);

	op = first_obj;
	while(op) {
		object = op->obj;
		op = op->next_tag;

		Lore::remove(pMaster, object, true);
		delObj(object, false, false, true, false);

		if(destroy)
			delete object;
		else if(!flagIsSet(M_TRADES) || !object->info.id) {
			object->clearFlag(O_BODYPART);
			object->addToRoom(room);
		}
	}
	checkDarkness();

	if(!destroy) {
		if(!coins.isZero()) {
			loadObject(MONEY_OBJ, &object);
			object->value.set(coins);

			object->nameCoin("gold", object->value[GOLD]);
			object->addToRoom(room);

			if(player) {
				if(str != "")
					str += ", ";
				str += object->name;
			}
		}

		if(player && str != "") {
			carry = crt_str(this, 0, CAP | INV);
			carry += " was carrying: ";
			carry += str;
			carry += ".\n";

			player->printColor("%s", carry.c_str());
			if(!player->flagIsSet(P_DM_INVIS))
				broadcastGroup(true, killer, "%s", carry.c_str());
		}
	}
}

//********************************************************************
//						die
//********************************************************************
// wrapper for die

void Creature::die(Creature *killer) {
	bool freeTarget=true;
	die(killer, freeTarget);
}

//********************************************************************
//						die
//********************************************************************
// Parameters:	<killer> The creature attacking
// Handles the death of people

void Creature::die(Creature *killer, bool &freeTarget) {
	Player*  pVictim = getPlayer();
	Monster* mVictim = getMonster();

	Player*  pKiller = killer->getPlayer();
	Monster* mKiller = killer->getMonster();
	bool duel = induel(pVictim, pKiller);

	if(pKiller) {
		pKiller->statistics.kill();
		if(mVictim)
			pKiller->statistics.monster(mVictim);
		if(pKiller->hasCharm(name))
			pKiller->delCharm(this);
	}

	if(pVictim)
		freeTarget = false;

	// Any time a level 7+ player dies, they are auto backed up.
	if(pVictim && pVictim->getLevel() >= 7 && !duel)
		pVictim->save(true, LS_BACKUP);

	if(mKiller && mKiller->isPet() && pVictim) {
		pVictim->dieToPet(mKiller);

	} else if(mKiller && mKiller->isPet() && mVictim) {
		mVictim->dieToPet(mKiller, freeTarget);

	} else if(mKiller && mVictim) {
		mVictim->dieToMonster(mKiller, freeTarget);

	} else if(mKiller && pVictim) {
		pVictim->dieToMonster(mKiller);

	} else if(pKiller && mVictim) {
		mVictim->dieToPlayer(pKiller, freeTarget);

	} else if(pKiller && pVictim) {
		pVictim->dieToPlayer(pKiller);

	}

	// 10% chance to get porphyria/lycanthropy when killed by a vampire/werewolf
	if(pVictim && !duel) {
		pVictim->addPorphyria(killer, 10);
		pVictim->addLycanthropy(killer, 10);
	}
}

//********************************************************************
//						dieToMonster
//********************************************************************
// Parameters:	<killer> The creature attacking
// Handles monsters killing players

void Player::dieToMonster(Monster *killer) {
	// Some sanity checks here -- Should never fail these
	ASSERTLOG( !killer->isPet() );

	// no real penalties for staff dying
	if(isStaff()) {
		printColor("^r*** You just died ***\n");
		broadcast(::isStaff, "^G### Sadly, %s just died.", name);

		clearAsEnemy();

		hp.restore();
		mp.restore();

		return;
	}

	print("%M killed you!\n", killer);

	if(flagIsSet(P_OUTLAW) && killer->getLevel() > 10)
		clearFlag(P_OUTLAW);

	setFlag(P_KILLED_BY_MOB);

	// Stop level 13+ players from suiciding right after dieing
	if(level >= 13) {
		lasttime[LT_MOBDEATH].ltime = time(0);
		lasttime[LT_MOBDEATH].interval = 60*60*24L;
		setFlag(P_NO_SUICIDE);
	}

	unsigned long oldxp = experience;
	unsigned short oldlvl = level;

	broadcast("### Sadly, %s was killed by %1N.", name, killer);
	killer->delEnmCrt(name);
	clearAsEnemy();
	clearEnemyPlayer();
	loseExperience(killer);
	dropEquipment(false);
	logDeath(killer);

	// more info for staff
	broadcast(::isCt, "^rOld Exp: %u Lost Exp: %d, Old Lvl: %u, Lvl: %u, Room: %s",
		oldxp, (int)(oldxp - experience), oldlvl, level, getRoom()->fullName().c_str());

	resetPlayer(killer);
}

//********************************************************************
//						dieToPet
//********************************************************************
// Parameters:	<killer> The creature attacking
// Handles pets killing players

void Player::dieToPet(Monster *killer) {
	Player	*master=0;

	if(killer->following)
		master = killer->following->getPlayer();
	else {
		broadcast(::isCt, "^y*** Pet %s has no master and is trying to kill a player. Room %s",
			killer->name, killer->room.str().c_str());
		return;
	}

	bool dueling = induel(this, master);

	if(dueling)
		broadcast("^R### Sadly, %s was killed by %s's %s in a duel.", name, master->name, killer->name);
	else {
		if(level > 2)
			broadcast("### Sadly, %s was killed by %s's %s.", name, master->name, killer->name);
		else {
			broadcast(::isCt, "^y### Sadly, %s was killed by %s's %s.", name, master->name, killer->name);
			print("### Sadly, %s was killed by %s's %s.", name, master->name, killer->name);
		}
	}

	killer->delEnmCrt(name);
	getPkilled(master, dueling);
}

//********************************************************************
//						dieToPet
//********************************************************************
// Parameters:	<killer> The creature attacking
// Handles pets killing monsters

void Monster::dieToPet(Monster *killer, bool &freeTarget) {
	Creature* petKiller=0;
	Player*	pKiller=0;
	logDeath(killer);

	if(killer->following) {
		petKiller = killer;
		pKiller = killer->following->getPlayer();
	} else {
		broadcast(::isCt, "^y*** Pet %s has no master and is trying to kill a mob. Room %s",
			killer->name, killer->room.str().c_str());
		return;
	}


	broadcast(NULL, pKiller->getRoom(), "%M's %s killed %N.", pKiller, petKiller->name, this);

	mobDeath(pKiller, freeTarget);
}


//********************************************************************
//						dieToMonster
//********************************************************************
// Parameters:	<this> The creature being attacked
//				<killer> The creature attacking

// Handles monsters killing monsters
void Monster::dieToMonster(Monster *killer, bool &freeTarget) {
	if(this != killer)
		broadcast(NULL, killer->getRoom(), "%M killed %N.", killer, this);
	mobDeath(killer, freeTarget);
}



//********************************************************************
//						dieToPlayer
//********************************************************************
// Parameters:	<killer> The creature attacking
// Handles players killing monsters

void Monster::dieToPlayer(Player *killer, bool &freeTarget) {
	logDeath(killer);
	if(following != killer) {
		killer->print("You killed %N.\n", this);
		broadcast(killer->getSock(), killer->getRoom(), "%M killed %N.", killer, this);
	}
	mobDeath(killer, freeTarget);
}

//********************************************************************
//						mobDeath
//********************************************************************
// Common routine for all mob's dying.

void Monster::mobDeath(Creature *killer) {
	bool freeTarget=true;
	mobDeath(killer, freeTarget);
}
void Monster::mobDeath(Creature *killer, bool &freeTarget) {
	if(killer)
		killer->checkDoctorKill(this);

	distributeExperience(killer);
	dropCorpse(killer);
	cleanFollow(killer);
	clearAsEnemy();

	// does the calling function want us to free the target?
	if(freeTarget)
		finishMobDeath();

	// freeTarget now means: do I (calling function) need to free the target?
	freeTarget = !freeTarget;
}

//********************************************************************
//						finishMobDeath
//********************************************************************

void Monster::finishMobDeath() {
	deleteFromRoom();
	gServer->delActive(this);
	free_crt(this);
}


//********************************************************************
//						dieToPlay
//********************************************************************
// Parameters:	<killer> The creature attacking
// Handles players killing players

void Player::dieToPlayer(Player *killer) {
	char	deathstring[80];

	bool dueling = induel(this, killer);

	killer->print("You killed %N.\n", this);
	print("%M killed you.\n", killer);
	broadcast(killer->getSock(), getSock(), killer->getRoom(), "%M killed %N.", killer, this);

	if(killer->isEffected("lycanthropy") && killer->getLevel() >= 13)
		strcpy(deathstring, "eaten");
	else if(killer->getClass() == ASSASSIN && killer->getLevel() >= 13)
		strcpy(deathstring, "assassinated");
	else
		strcpy(deathstring, "killed");

	if(dueling)
		broadcast("^R### Sadly, %s was %s by %s in a duel.", name, deathstring, killer->name);
	else// if(level > 2)
		broadcast("### Sadly, %s was %s by %1N.", name, deathstring, killer);
	//else {
	//	broadcast(::isWatcher, "^C### Sadly, %s was %s by %1N.", name, deathstring, killer);
	//	print("### Sadly, %s was %s by %1N.\n", name, deathstring, killer);
	//}

	getPkilled(killer, dueling);
}

//********************************************************************
//						getPkilled
//********************************************************************

void Player::getPkilled(Player *killer, bool dueling, bool reset) {
	unsigned long oldxp = experience;
	unsigned short oldlvl = level;

	if(isGuildKill(killer))
		guildKill(killer);

	if(isGodKill(killer))
		godKill(killer);
	else if(isClanKill(killer))
		clanKill(killer);

	// make the pets stop attacking the players
	clearAsPetEnemy();
	killer->clearAsPetEnemy();

	if(!dueling) {
		updatePkill(killer);

		dropBodyPart(killer);

		if(!killer->isStaff())
			dropEquipment(true, killer->getSock());

		logDeath(killer);

		// more info for staff
		broadcast(::isCt, "^rOld Exp: %u Lost Exp: %d, Old Lvl: %u, Lvl: %u, Room: %s",
			oldxp, (int)(oldxp - experience), oldlvl, level, getRoom()->fullName().c_str());

	}

	if(reset)
		resetPlayer(killer);
}

//********************************************************************
//						isGuildKill
//********************************************************************

bool Player::isGuildKill(const Player *killer) const {
	if(induel(killer, this))
		return(false);

	if(killer->getLevel() < 7 || level < 7)
		return(false);

	if(!killer->getGuild() || killer->getGuild() > gConfig->maxGuilds || guild > gConfig->maxGuilds)
		return(false);

	if(!(killer->getGuild() && killer->getGuildRank() >= GUILD_PEON) ||
	        !(guild && guildRank >= GUILD_PEON) ||
	        (killer->isStaff()) )
		return(false);

	return(true);
}

//********************************************************************
//						isGodKill
//********************************************************************

bool Player::isGodKill(const Player *killer) const {
	if(induel(killer, this))
		return(false);

	if(killer->getLevel() < 7 || level < 7)
		return(false);

	if(!killer->getDeity() || !deity)
		return(false);

	if(killer->isStaff())
		return(false);

	return(true);
}

//********************************************************************
//						isClanKill
//********************************************************************

bool Player::isClanKill(const Player *killer) const {

	if(flagIsSet(P_OUTLAW_WILL_LOSE_XP))
		return(true);

	if(induel(killer, this))
		return(false);

	if(killer->getLevel() < 7 || level < 7)
		return(false);

	if(clan && killer->getClan())
		return(true);


	// Dk vs Paly, Paly vs Dk, or Dk vs Dk
	if((killer->getClass() == DEATHKNIGHT && cClass == PALADIN) ||
	        (killer->getClass() == DEATHKNIGHT && (cClass == PALADIN || cClass == DEATHKNIGHT)))
		return(true);

	if( ((killer->getClass() == CLERIC && killer->flagIsSet(P_CHAOTIC) && killer->getLevel() >= 7) && flagIsSet(P_PLEDGED)) ||
	        ((cClass == CLERIC && flagIsSet(P_CHAOTIC) && level >= 7) && killer->flagIsSet(P_PLEDGED)) )
		return(true);

	return(false);
}

//********************************************************************
//						guildKill
//********************************************************************

int Player::guildKill(Player *killer) {
	int		bns=0, penalty=0, levelDiff=0;
	Guild *killerGuild, *thisGuild;
	int		same=0, base=0;
	long	total=0;

	killerGuild = gConfig->getGuild(killer->getGuild());
	thisGuild = gConfig->getGuild(guild);

	base = level * 50;

	levelDiff = level - killer->getLevel();

	if(levelDiff < 0)
		bns += 5 * level * levelDiff;
	else
		bns += levelDiff * 50;

	if(guildRank == GUILD_MASTER)
		bns *= 3;
	else if(guildRank ==  GUILD_BANKER)
		bns *= 2;
	else if(guildRank == GUILD_OFFICER)
		bns = (bns*3)/2;

	total = MAX(1, mrand((base + bns)/2, base + bns));

	if(killer->halftolevel())
		total = 0;

	if(killer->getSecondClass())
		total = total * 3 / 4;

	penalty = MIN(mrand(1000,1500), (bns*3)/2);

	if(killer->getLevel() > level + 6)
		penalty = 100;

	if(killerGuild == thisGuild) {
		total = -1000;
		penalty = 1000;
		same = 1;
	} else {
		killerGuild->pkillsIn ++;
		killerGuild->pkillsWon++;
		thisGuild->pkillsIn++;

		killerGuild->bank.add(coins);
		guildBankLog(killer->getGuild(), "Guild PKILL: %s by %s [Balance: %s]\n",
			coins.str().c_str(), killer->name, killerGuild->bank.str().c_str());
		coins.zero();
	}


	if(!same) {
		killer->printColor("^gYou have defeated a member of a rival guild!\n");
		killer->print("Your guild honors you with %d experience.\n", abs(total));
		killer->printColor("You grab %N's coins and put them in your guild bank.^x\n", this);

		printColor("^gYou have been defeated by a member of a rival guild!\n");
		print("Your guild shames you by taking %d experience.\n", abs(penalty));
		printColor("%M grabs all of your coins!\n", killer);
	} else {
		killer->printColor("^gYou have shamelessly defeated a member of your own guild!\n");
		killer->printColor("Your guild penalizes you for %d experience.\n", abs(total));

		printColor("^gYou have been defeated during senseless guild infighting!\n");
		printColor("Your guild penalizes you for %d experience.\n", abs(penalty));
	}

	killer->addExperience(total);
	killer->checkLevel();
	experience -= penalty;
	checkLevel();

	return(1);
}

//********************************************************************
//						godKill
//********************************************************************

int Player::godKill(Player *killer) {
	int		bns=0, penalty=0, levelDiff=0;
	int		same=0, base=0;
	Guild *killerGuild, *thisGuild;
	long	total=0;

	killerGuild = gConfig->getGuild(killer->getGuild());
	thisGuild = gConfig->getGuild(guild);

	base = level * 50;
	levelDiff = level - killer->getLevel();

	if(levelDiff < 0)
		bns += 5 * level * levelDiff;
	else
		bns += levelDiff * 50;


	total = MAX(1, mrand((base + bns)/2, base + bns));

	if(killer->halftolevel())
		total = 0;

	if(killer->getSecondClass())
		total = total * 3 / 4;

	penalty = MIN(mrand(1000,1500), (bns*3)/2);

	if(killer->getLevel() > level + 6)
		penalty = 100;

	if(killer->getDeity() == deity) {
		total = -50;
		penalty = 50;
		same = 1;
	}
	if(killerGuild != 0 && thisGuild != 0) {
		if(killerGuild != thisGuild) {
			killerGuild->pkillsIn ++;
			killerGuild->pkillsWon ++;
			thisGuild->pkillsIn ++;
		}
	}


	if(!same) {
		killer->printColor("^cYou have defeated a member of an enemy religion!\n");
		killer->printColor("Your order honors you with %d experience.\n", abs(total));

		printColor("^cYou have been defeated by a member of a rival religion!\n");
		printColor("Your order shames you by taking %d experience.\n", abs(penalty));
	} else {
		killer->printColor("^cYou have shamelessly defeated a member of your own religion!\n");
		killer->printColor("Your order penalizes you for %d experience.\n", abs(total));

		printColor("^cYou have been defeated during senseless religious infighting!\n");
		printColor("Your order penalizes you for %d experience.\n", abs(penalty));
	}

	killer->addExperience(total);
	killer->checkLevel();
	experience -= penalty;
	checkLevel();

	return(1);
}

//********************************************************************
//						clanKill
//********************************************************************

int Player::clanKill(Player *killer) {
	bool penalty = false; // Do we give a penalty for killing this person?
	int expGain = 0, expLoss = 0;

	// Paly's shouldn't be killing each other
	if(cClass == PALADIN && killer->getClass() == PALADIN && deity == killer->getDeity())
		penalty = true;
	// Clan members should not be killing members of their own clan
	if(clan == killer->getClan())
		penalty = true;
	// Never a penalty for killing an outlaw!
	if(flagIsSet(P_OUTLAW_WILL_LOSE_XP))
		penalty = false;

	// No penalty
	if(!penalty) {
		expGain = (level * killer->getLevel()) * 10;
		expLoss = expGain * 2;

		if(killer->halftolevel())
			expGain = 0;
		if(killer->getSecondClass())
			expGain = expGain * 3 / 4;

		print("You have been bested!\nYou lose %d experience.\n", expLoss);
		experience -= expLoss;
		if(experience <= 0)
			experience = 0;
		if(!killer->flagIsSet(P_OUTLAW)) {
			killer->print("You have vanquished %s.\n", name);
			killer->print("You %s %d for your heroic deed.\n", gConfig->isAprilFools() ? "lose" : "gain", expGain);
			killer->addExperience(expGain);
			killer->checkLevel();
		}
	}
	return(1);
}

//********************************************************************
//						checkDoctorKill
//********************************************************************
// Parameters:	<player>
//				<killer> The creature attacking
// Checks for doctor killers

void Creature::checkDoctorKill(Creature *victim) {
	if(!strcmp(victim->name, "doctor")) {
		if(	(isPlayer() && !isStaff()) ||
			(isMonster() && isPet() && !following->isStaff())
		) {
			Creature* target = isPlayer() ? this : following;

			target->setFlag(P_DOCTOR_KILLER);
			if(!target->flagIsSet(P_DOCTOR_KILLER))
				broadcast("### %s is a doctor this!", target->name);
			target->lasttime[LT_KILL_DOCTOR].ltime = time(0);
			target->lasttime[LT_KILL_DOCTOR].interval = 72000L;
		}
	}
}


//********************************************************************
//						checkLevel
//********************************************************************
// Parameters:	<player>
// Checks if a player has deleveled or releveled
void doTrain(Player* player);

int Player::checkLevel() {
	int n = exp_to_lev(experience);
	// De-Level!
	if(level > n) {
		print("You have deleveled to level %s!\n", int_to_text(n));
		while(level > n)
			downLevel();

		negativeLevels = 0;

		return(-1); // Delevel
	}

	// Re-Level!
	if(level < n && level < actual_level && n <= actual_level && !negativeLevels) {
		if(parent_rom && parent_rom->getHighLevel() && level == parent_rom->getHighLevel()) {
			print("You have enough experience to relevel, but cannot do so in this room.\n");
			return(0);
		}
		print("You have releveled to level %s!\n", int_to_text(n));
		logn("log.relevel", "%s just releveled to level %d from level %d in room %s.\n",
			name, n, level, getRoom()->fullName().c_str());
		if(!flagIsSet(P_DM_INVIS))
			broadcast("### %s just releveled to %s!", name, int_to_text(n));
		while(level < n)
			upLevel();
		return(1); // Relevel
	}

	// TODO: Dom: HC
	//if(isHardcore() && level < n && !negativeLevels) {
	//	doTrain(this);
	//	return(2); // Level
	//}

	return(0); // No change
}

//********************************************************************
//						updatePkill
//********************************************************************

void Player::updatePkill(Player *killer) {
	// No pkill changes if either party is staff
	if(isStaff() || killer->isStaff())
		return;

	statistics.losePk();
	killer->statistics.winPk();
}

//********************************************************************
//						dropEquipment
//********************************************************************
// Parameters:	<killer> The creature attacking
// Function to make players drop the equipment they are currently wearing
// via a pkill

void Player::dropEquipment(bool dropAll, Socket* killerSock) {
	bstring dropString = "";
	int		i=0;

	// be nice to lowbies
	if(!dropAll || level < 4) {
		dropWeapons(true);
	} else {

		// dropping weapons is handled separately from equipment, but we still need their
		// names for dropping later
		Object* main = ready[WIELD-1];
		Object* held = ready[HELD-1];

		dropWeapons();


		// don't make them lose all their inventory in a drop-destroy room
		if(!getRoom()->flagIsSet(R_DESTROYS_ITEMS)) {

			// we reassign these for the purposes of having them printed in the list;
			// they will be reset afterwards
			Object* rMain = ready[WIELD-1];
			Object* rHeld = ready[HELD-1];
			ready[WIELD-1] = main;
			ready[HELD-1] = held;

			for(i=0; i<MAXWEAR; i++) {
				if(	!ready[i] ||
					ready[i]->flagIsSet(O_CURSED) ||
					ready[i]->flagIsSet(O_NO_DROP) ||
					ready[i]->flagIsSet(O_STARTING)
				)
					continue;
				// 20% chance each item will be dropped (ignore this chance for wielded items
				// as we want them added to the list of stuff dropped)
				if((i != WIELD-1 && i != HELD-1) && mrand(1,5) > 1)
					continue;

				if(dropString != "")
					dropString += ", ";
				dropString += ready[i]->name;

				if(i != WIELD-1 && i != HELD-1) {
					// I is wearloc-1, so add one to it
					Object* temp = unequip(i+1, UNEQUIP_NOTHING, false);
					if(temp) {
						Limited::deleteOwner(this, temp);
						temp->addToRoom(getRoom());
					}
				}
			}

			// reset these wear locations
			ready[WIELD-1] = rMain;
			ready[HELD-1] = rHeld;

			if(dropString != "") {
				if(killerSock)
					killerSock->print("%s dropped: %s.\n", name, dropString.c_str());

				print("You dropped your inventory where you died!\n");
			}
		}
	}

	checkDarkness();
	computeAC();
	computeAttackPower();
}


//********************************************************************
//						dropBodyPart
//********************************************************************
// Parameters:	<killer> The creature attacking
// Makes a player drop a body part

void Player::dropBodyPart(Player *killer) {
	ASSERTLOG( killer->isPlayer());

	if(getRoom()->flagIsSet(R_DESTROYS_ITEMS))
		return;

	bool nopart = false;
	int dueling = 0, num = 0;
	Object *body_part;
	char part[12], partName[25];

	dueling = induel(this,killer);

	if(mrand (1,100) > 5 && !killer->isDm() && !flagIsSet(P_OUTLAW))
		nopart = true;

	if(level <= 4 && !killer->isDm() && !flagIsSet(P_OUTLAW))
		nopart = true;

	if(	isStaff() || (
			!killer->isPet() &&
			!killer->isDm() &&
			(killer->getLevel() > level + 5) &&
			!flagIsSet(P_OUTLAW)
		) || (
			killer->isPet() &&
			!killer->following->isDm() &&
			(killer->following->getLevel() > level + 5) &&
			!flagIsSet(P_OUTLAW)
		)
	) {
		nopart = true;
	}

	if(!nopart && !dueling) {

		if(loadObject(BODYPART_OBJ, &body_part)) {
			num = mrand(1,14);

			switch(num) {
			case 1:
				strcpy(part, "skull");
				break;
			case 2:
				strcpy(part, "head");
				break;
			case 3:
				strcpy(part, "ear");
				break;
			case 4:
				strcpy(part, "scalp");
				break;
			case 5:
				strcpy(part, "hand");
				break;
			case 6:
				strcpy(part, "spleen");
				break;
			case 7:
				strcpy(part, "nose");
				break;
			case 8:
				strcpy(part, "arm");
				break;
			case 9:
				strcpy(part, "foot");
				break;
			case 10:
				strcpy(part, "spine");
				break;
			case 11:
				strcpy(part, "heart");
				break;
			case 12:
				strcpy(part, "brain");
				break;
			case 13:
				strcpy(part, "eyeball");
				break;
			case 14:
				if(isEffected("vampirism"))
					strcpy(part, "fangs");
				else
					strcpy(part, "teeth");
				break;
			default:
				strcpy(part, "skull");
				break;
			}

			strcpy(partName, name);
			sprintf(body_part->name, "%s's %s", partName, part);

			lowercize(partName, 0);
			strncpy(body_part->key[0], partName, 20);
			strncpy(body_part->key[1], part, 20);
			strncpy(body_part->key[2], part, 20);

			body_part->setAdjustment(mrand(1,2));
			if(mrand(1,100) == 1)
				body_part->setAdjustment(3);

			body_part->addToRoom(getRoom());
		}
	}

}

//********************************************************************
//						logDeath
//********************************************************************

void Player::logDeath(Creature *killer) {
	char	file[16], killerName[80];
	Player*	pKiller = killer->getPlayer();

	if(!killer->isStaff())
		statistics.die();
	strcpy(killerName, "");

	if(killer->isPet())
		sprintf(killerName, "%s's %s.", killer->following->name, killer->name);
	else
		strcpy(killerName, killer->name);

	if(pKiller)
		strcpy(file, "log.pkill");
	else
		strcpy(file, "log.death");

	if(!(!pKiller && killer->flagIsSet(M_NO_EXP_LOSS))) {
		logn(file, "%s(%d) was killed by %s(%d) in room %s.\n", name, level,
		     killer->name, killer->getLevel(), killer->getRoom()->fullName().c_str());

	}

	if(pKiller && pKiller->isStaff()) {
		log_immort(false, pKiller, "%s(%d) was killed by %s(%d) in room %s.\n", name, level,
			pKiller->name, pKiller->getLevel(), pKiller->getRoom()->fullName().c_str());
	}
}

//********************************************************************
//						resetPlayer
//********************************************************************
// Parameters:	<killer> The creature attacking
// Reset a player after death -- Move them to limbo, broadcast
// they were killed, clear deterimental effects, etc

void Player::resetPlayer(Creature *killer) {
	int		duel=0;
	bool	same=false;
	Player* pKiller = killer->getPlayer();
	BaseRoom *newRoom = getLimboRoom().loadRoom(this);

	duel = induel(this, pKiller);

	if(	inJail() ||
		duel ||
		!newRoom
	)
		same = true;

	if(cClass == BUILDER) {
		same = true;
		print("*** You died ***\n");
		broadcast(::isStaff, "^G### Sadly, %s was killed by %s.", name, killer->name);
	}

	// a hardcore player about to die doesnt need to worry about room movement
	if(isHardcore() && !killer->isStaff())
		same = true;

	if(!same) {
		deleteFromRoom();
		addToRoom(newRoom);
		doPetFollow();
	}
	//doClearPetEnemy(this, killer->name);

	curePoison();
	cureDisease();
	tickDmg = 0;
	removeCurse();
	removeEffect("hold-person");
	removeEffect("petrification");
	removeEffect("confusion");
	removeEffect("drunkenness");
	unhide();

	if(killer->isPlayer() || duel) {
		hp.setCur( MAX(1, MAX(hp.getMax()/2, hp.getCur())));
		mp.setCur(MAX(mp.getCur(),(mp.getMax())/10));
	} else {
		hp.restore();
		mp.restore();
	}

	if(duel) {
		setFlag(P_DIED_IN_DUEL);
		knockUnconscious(10);
		broadcast(getSock(), getRoom(), "%s is knocked unconscious!", name);

		updateAttackTimer(true, 300);
		pKiller->delDueling(name);
		delDueling(killer->name);
	} else if(isHardcore() && !killer->isStaff()) {
		hardcoreDeath(this);
	} else {
		courageous();
	}
}

//void clearMobEnemies(Creature *monster) {
//	etag		*ep=0;
//
//	if(monster->isPlayer())
//		return;
//
//	ep = monster->first_enm;
//	while(ep) {
//		del_enm_crt(ep->enemy, monster);
//		ep = ep->next_tag;
//	}
//}

//********************************************************************
//						hearMobDeath
//********************************************************************

bool hearMobDeath(Socket* sock) {
	if(!sock->getPlayer() || !isCt(sock))
		return(false);
	return(!sock->getPlayer()->flagIsSet(P_NO_DEATH_MSG));
}

//********************************************************************
//						logDeath
//********************************************************************

void Monster::logDeath(Creature *killer) {
	int			solo=0, logType=0;
	ctag		*fp=0;
	Creature *leader=0, *pet=0;
	BaseRoom* room = killer->getRoom();
	char		file[80], killerString[1024];
	char		logStr[2096];


	strcpy(file, "");
	strcpy(killerString, "");

	if(flagIsSet(M_WILL_BE_LOGGED)) {
		strcpy(file, "log.mdeath");
		logType = 1;
	} else if(flagIsSet(M_PERMENANT_MONSTER) && (killer->isPlayer() || killer->isPet())) {
		strcpy(file, "log.perm");
		logType = 2;
	} else if(killer->isPlayer() && killer->flagIsSet(P_BUGGED) ) {
		sprintf(file, "%s/%s", BUGPATH, killer->name);
		logType = 3;
	} else if(killer->isPlayer() && killer->flagIsSet(P_KILLS_LOGGED) ) {
		sprintf(file, "%s/%s.kills", BUGPATH, killer->name);
		logType = 4;
	} else
		return; //Mob's death not logged

	// There are 4 cases for perm broadcast, otherwise we do single broadcast
	// (which includes the pet message).

	// group broadcast (aka solo = 1)
	//		killer is pet
	//			pet's leader has a player follower
	//			pet's leader is following
	//		killer is a player
	//			killer has a player follower
	//			killer is following

	if(killer->isPlayer() || killer->isPet()) {
		solo = 1;

		// we have the leader of the pet
		if(killer->isPet())
			leader = killer->following;
		else
			leader = killer;

		// see if they have a leader or followers
		if(leader->following)
			solo = 0;

		// we always need to check for a pet
		fp = leader->first_fol;
		while(fp) {

			if(fp->crt != leader) {
				if(fp->crt->isPet() && fp->crt->following == leader)
					pet = fp->crt;
				if(!(fp->crt->isPet() && fp->crt->following == leader))
					solo = 0;
			}
			fp = fp->next_tag;
		}
	}

	if(killer->isPet() || pet) {
		sprintf(killerString, "%s and %s %s",
			leader->name,
			leader->hisHer(),
			pet->name);
	} else {
		sprintf(killerString, "%s", killer->name);
	}


	switch(logType) {
	case 1:	// unsaved mob
		sprintf(logStr, "%s(L%d) was killed by %s(L%d) in room %s for %lu experience.",
		     name, level, killerString, killer->getLevel(), room->fullName().c_str(), experience);
		broadcast(hearMobDeath, "^g*** %s(L%d) was killed by %s(L%d) in room %s for %lu experience.",
			name, level, killerString, killer->getLevel(), room->fullName().c_str(), experience);
		break;
	case 2: // Mob is a perm

		// if the killer was a pet, leader is pointing to the pet's leader
		// make it point to the leader of the pet's leader
		if(leader->following)
			leader = leader->following;

		if(!solo)
			sprintf(logStr, "%s(L%d) was killed by %s(L%d) in %s(L%d)'s group in room %s.",
			     name, level, killerString, killer->getLevel(),
			     leader->name, leader->getLevel(), room->fullName().c_str());
		else
			sprintf(logStr, "%s(L%d) was killed by %s(L%d) in room %s [SOLO].",
			     name, level, killerString, killer->getLevel(), room->fullName().c_str());


		if(!killer->isStaff() && flagIsSet(M_NO_PREFIX)) {
			if(!solo)
				broadcast(wantsPermDeaths, "^m### Sadly, %s was killed by %s and %s followers.", name, leader->name, leader->hisHer());
			else
				broadcast(wantsPermDeaths, "^m### Sadly, %s was killed by %s.", name, killerString);
		}

		break;
	case 3: // bugged player
	case 4: // all player's kills logged
		sprintf(logStr, "%s(L%d) was killed by %s(L%d) in room %s for %lu experience%s.",
		     name, level, killerString, killer->getLevel(),
		     room->fullName().c_str(), experience, solo == 1 ? "[SOLO]" : "");
		break;
	}

	logn(file, "%s\n", logStr);
	// I don't want to see bugged kill logs...too much spam
	if((killer->isPlayer() || killer->isPet()) && !killer->isCt() && logType != 4)
		broadcast(hearMobDeath, "^y*** %s", logStr);
}

//********************************************************************
//						loseExperience
//********************************************************************
// Parameters:	<killer> The creature attacking
// Handles experience/realms loss & death effects

void Player::loseExperience(Monster *killer) {
	float	xploss=0.0;
	long	n=0;
	int		count=0;


	// No exp loss from possesed monsters
	if(killer->flagIsSet(M_DM_FOLLOW))
		return;

	if(killer->flagIsSet(M_NO_EXP_LOSS) || (killer->isPet() && killer->following->isStaff()))
		return;

	if(level >= 7)
		addEffect("death-sickness");

	if(level < 10) {
		// Under level 10, 10% exp loss
		xploss = ((float)experience / 10.0);
		lostExperience += (long)xploss;
		experience -= (long)xploss;

	} else {
		// Level 10 and over, 2% exp loss with a minimum of 10k
		xploss = MAX((long)( (float)experience * 0.02), 10000);
		print("You have lost %ld experience.\n", (long)xploss);
		lostExperience += (long)xploss;
		experience -= (long)xploss;
	}

	n = level - exp_to_lev(experience);

	if(n > 1) {
		if(level < (MAXALVL+2))
			experience = needed_exp[level-3];
		else
			experience = (long)((needed_exp[MAXALVL-1]*(level-2)));
	}

	checkLevel();
//	n = exp_to_lev(experience);
//	while(level > n)
//		down_level(this);

	for(count=0; count < 6; count++) {		// Random loss to saving throws due to death.
		if(mrand(1,100) <= 25) {
			saves[count].chance -= 1;
			saves[count].gained -= 1;
			saves[count].chance = MAX(1, saves[count].chance);
			saves[count].gained = MAX(1, saves[count].gained);
		}
	}
}

//********************************************************************
//						giveExperience
//********************************************************************
// Parameters:	<killer> The creature attacking
// Handles experience gain on monster death

void Monster::distributeExperience(Creature *killer) {
	etag	*ep=0;
	long	expGain = 0;

	Monster* pet=0;
	Player*	player=0, *follower=0;
	Player* leader=0;

	if(isPet())
		return;

	if(flagIsSet(M_PERMENANT_MONSTER))
		diePermCrt();

	if(killer) {
		if(killer->isPet())
			player = killer->following->getPlayer();
		else
			player = killer->getPlayer();

		if(player) {
			if(player->isGroupLeader(this) && player->flagIsSet(P_XP_DIVIDE))
				leader = player;
			else if(player->following && player->following->flagIsSet(P_XP_DIVIDE) && player->inSameRoom(player->following))
				leader = player->following->getPlayer();
		}

		std::list<Player*> groupExpList;
		// If we have a leader, means we've got group exp set, so lets distribute it
		if(leader) {
			// Split exp evenly amounsgt the group
			int numGroupMembers=0, totalGroupLevel=0, totalGroupDamage=0;
			Creature* crt=0;
			ctag* cp = leader->first_fol;

			// Calculate how many people are in the group, see how much damage they have done
			// and remove them from the enemy list
			if(	isEnmCrt(leader->name) &&
				inSameRoom(leader) &&
				leader->getsGroupExperience(this)
			) {
				numGroupMembers++;
				totalGroupLevel += leader->getLevel();
				totalGroupDamage += delEnmCrt(leader->name);
				groupExpList.push_back(leader);
			}
			while(cp) {
				crt = cp->crt;
				cp = cp->next_tag;
				if(isEnmCrt(crt->name) && inSameRoom(crt)) {
					if(crt->getsGroupExperience(this)) {
						// Group member
						follower = crt->getPlayer();
						numGroupMembers ++;
						totalGroupLevel += follower->getLevel();
						totalGroupDamage += delEnmCrt(follower->name);
						groupExpList.push_back(follower);
						pet = follower->getPet();
						if(pet)
							totalGroupDamage += delEnmCrt(pet->name, pet->following->name);
					} else if(crt->isPet()) {
						// Leader's pet
						totalGroupDamage += delEnmCrt(crt->name, crt->following->name);
					}
				}
			}
			float xpPercent = (float)MIN(totalGroupDamage, hp.getMax())/(float)hp.getMax();
			long adjustedExp = (long)((xpPercent*experience) * (1.0 + (.25*numGroupMembers)));

			// Exp is split amoungst the group based on their level,
			// since we split it evenly, in this case pets do NOT give their master
			// any extra experience.
			foreach(Player * ply, groupExpList) {
				if(ply) {
					expGain = (long)(((float)ply->getLevel()/(float)totalGroupLevel) * adjustedExp);
					expGain = MAX(1, expGain);
					ply->gainExperience(this, killer, expGain, true);
				}
			}
		}
	}

	// Now handle everyone else on the list
	ep = first_enm;
	while(ep) {
		pet = 0;
		if(ep->owner[0]) {
			player = gServer->findPlayer(ep->owner);
			if(!player)
				player = gServer->findPlayer(ep->enemy);
			if(player) {
				// take monster off of pet enemy list
				pet = player->getPet();
				if(pet)
					pet->delEnmCrt(name);
			}
		} else {
			player = gServer->findPlayer(ep->enemy);
		}


		if(player) {
			expGain = (experience * ep->damage) / MAX(hp.getMax(), 1);
			expGain = MIN(MAX(0,expGain), experience);

			if(pet)
				pet->gainExperience(this, killer, expGain);
			else
				player->gainExperience(this, killer, expGain);
			// Adjust monk/wolf ac and thac0 for extreme aligment
			// **************************************************
			player->alignAdjustAcThaco();
			// **************************************************
		}
		ep = ep->next_tag;
	}
}

//********************************************************************
//						adjustExperience
//********************************************************************

void Creature::adjustExperience(Monster* victim, int& expAmount, int& holidayExp) {
	Player* player;
	if(isPet())
		player = following->getPlayer();
	else
		player = getPlayer();

	if(player->halftolevel()) {
		expAmount = 0;
		holidayExp = 0;
		return;
	}

	if(player->getRace() == HUMAN && expAmount)
		expAmount += MAX(mrand(4,6),expAmount/3/10);

        if(player->getSecondClass()) {
                // Penalty is 12.5% at level 30 and above
                if(player->level >= 30)
                        expAmount = (expAmount*7)/8;
                else // and 25% below 30
                        expAmount = (expAmount*3)/4;
        }
//	// All experience is multiplied by 3/4 for a multi-classed player
//	if(player->getSecondClass())
//		expAmount = (expAmount*3)/4;

	int levelDiff = abs((int)player->getLevel() - (int)victim->getLevel());
	float multiplier=1.0;

	if(levelDiff <= 5)
		multiplier = 1.0;
	// 6 - 8 levels = 90%
	else if(levelDiff >= 6 && levelDiff <= 8) {
		multiplier = 0.9;
	} // 8 - 10 = 70%
	else if(levelDiff >= 8 && levelDiff <= 10 ) {
		multiplier = 0.70;
	} // 10 - 15 = 50%
	else if(levelDiff >= 10 && levelDiff <= 15) {
		multiplier = 0.50;
	} // 15 - 25 = 25%
	else if(levelDiff >= 15 && levelDiff <= 25) {
		multiplier = 0.25;
	} // 26+ = 10%
	else
		multiplier = 0.10;
	if(multiplier < 1.0) {
//		player->printColor("^YExp Adjustment: %d%% (%d level difference) %d -> %d\n", (int)(multiplier*100), levelDiff, expAmount, (int)(expAmount*multiplier));
		expAmount = (int)(expAmount * multiplier);
		expAmount = MAX(1, expAmount);
	}

	// Add in holiday experience
	bstring holidayStr = isHoliday();

	if(holidayStr != "")
		holidayExp = MAX(1, (int)(expAmount * 0.1));

	if(victim->flagIsSet(M_PERMENANT_MONSTER))
		holidayExp = 0;
}

//********************************************************************
//						gainExperience
//********************************************************************

void Player::gainExperience(Monster* victim, Creature* killer, int expAmount, bool groupExp) {
	int holidayExp = 0;
	adjustExperience(victim, expAmount, holidayExp);
	bool notlocal = false, af = gConfig->isAprilFools();

	if(!inSameRoom(victim))
		notlocal = true;

	bstring holidayStr = isHoliday();
	if(	groupExp ||
		this == killer ||
		!killer || !(notlocal &&
					!(killer->isPlayer() && killer->flagIsSet(P_DM_INVIS)) &&
					!(killer->isPet() && killer->following->flagIsSet(P_DM_INVIS)))
	) {
		printColor("You %s ^y%d^x %sexperience for the death of %N.\n", af ? "lose" : "gain", expAmount, groupExp ? "group " : "", victim);
	} else {
		if(killer->isPet())
			print("%M's %s %s you %d experience for the death of %N.\n", killer->following, killer->name, af ? "cost" : "gained", expAmount, victim);
		else
			print("%M %s you %d experience for the death of %N.\n", killer, af ? "cost" : "gained", expAmount, victim);
	}
	if(holidayExp > 0)
		printColor("%s You %s an extra ^y%d^x experience!\n", holidayStr.c_str(), af ? "lost" : "gained", holidayExp);

	adjustFactionStanding(victim->factions);
	adjustAlignment(victim);
	updateMobKills(victim);

	experience += expAmount;
	experience += holidayExp;

	checkLevel();

	if(victim->flagIsSet(M_WILL_BE_LOGGED))
		logn("log.mdeath", "%s was killed by %s, for %d experience.\n", victim->name, name, expAmount);

	ctag* fp = first_fol;
	while(fp) {
		if(fp->crt->isPet()) {
			fp->crt->getMonster()->delEnmCrt(victim->name);
			break;
		}
		fp = fp->next_tag;
	}
}

//********************************************************************
//						gainExperience
//********************************************************************

void Monster::gainExperience(Monster* victim, Creature* killer, int expAmount, bool groupExp) {
	Creature* master = following;
	if(!master || !isPet())
		return;
	bool af = gConfig->isAprilFools();

	int holidayExp = 0;
	adjustExperience(victim, expAmount, holidayExp);

	master->printColor("Your %s %s you ^y%d^x experience for the death of %N.\n", this->name, af ? "cost" : "earned", expAmount, victim);

	bstring holidayStr = isHoliday();
	if(holidayExp > 0)
		master->printColor("%s You %s an extra ^y%d^x experience!\n", holidayStr.c_str(), af ? "lost" : "gained", holidayExp);

	master->addExperience(expAmount);
	master->addExperience(holidayExp);
	delEnmCrt(victim->name);
}


//********************************************************************
//						checkDie
//********************************************************************
// wrapper for checkDie
// Return: true if they died, false if they didn't

bool Creature::checkDie(Creature *killer) {
	bool freeTarget=true;
	return(checkDie(killer, freeTarget));
}

//********************************************************************
//						checkDie
//********************************************************************
// Parameters:	<killer> The creature attacking
// If the victim has 0 hp or less, kill them off
// Return: true if they died, false if they didn't

bool Creature::checkDie(Creature *killer, bool &freeTarget) {
	if(hp.getCur() < 1) {
		die(killer, freeTarget);
		return(true);
	}
	return(false);
}

//********************************************************************
//						checkDieRobJail
//********************************************************************
// wrapper for checkDieRobJail

int Creature::checkDieRobJail(Monster *killer) {
	bool freeTarget=true;
	return(checkDieRobJail(killer, freeTarget));
}

//********************************************************************
//						checkDieRobJail
//********************************************************************
// Parameters:	<killer> The creature attacking
// Similar to checkDie, but this function will see if the attacker
// is a mugger, or a jailer and handle appropriately.
// Return Value:  1 <Death>
//				  2 <Unconscious/Jail/Mug>
//				  0 <Nothing>

int Creature::checkDieRobJail(Monster *killer, bool &freeTarget) {
	BaseRoom* room = getRoom();
	Player	*pVictim = getPlayer();
	ASSERTLOG(killer->isMonster());

	// Less than 1 hp & not police/greedy monster or it's monster, then die
	if(hp.getCur() < 1
		&& ((!killer->flagIsSet(M_POLICE) && ! killer->flagIsSet(M_GREEDY)) || isMonster() )
	) {
		die(killer, freeTarget);
		return(1);
	}
	// Police/Greedy & less than 1/10th hp & pVictim
	else if((killer->flagIsSet(M_GREEDY) || killer->flagIsSet(M_POLICE)) &&
		pVictim && pVictim->hp.getCur() < pVictim->hp.getMax()/10)
	{
		freeTarget = false;
		ctag *rcp;
		pVictim->printColor("^r%M knocks you unconscious.\n",killer);
		pVictim->knockUnconscious(39);
		broadcast(pVictim->getSock(), room, "%M knocked %N unconscious.", killer, pVictim);
		pVictim->hp.setCur( pVictim->hp.getMax()/20);
		rcp = room->first_mon;
		while(rcp) {
			rcp->crt->getMonster()->delEnmCrt(pVictim->name);
			rcp = rcp->next_tag;
		}
		killer->delEnmCrt(pVictim->name);
		if(killer->flagIsSet(M_POLICE)) {
			broadcast(pVictim->getSock(), room, "%M picks %N up and hauls %s off.",killer, pVictim, pVictim->himHer());
			if(!killer->jail.id && !pVictim->isStaff())
				broadcast("### Unfortunately, %N was hauled off to jail by %N.",pVictim, killer);
			killer->toJail(pVictim);
			return(2);
		}
		if(killer->flagIsSet(M_GREEDY)) {
			broadcast(pVictim->getSock(), room, "%M rummages through %N's inventory.", killer, pVictim);
			broadcast("### Unfortunately, %N was mugged by %N.", pVictim, killer);
			killer->grabCoins(pVictim);
			return(2);
		}
		return(2);
	}

	freeTarget = false;
	return(0);
}


//********************************************************************
//						die
//********************************************************************
// Parameters:	<killer> The creature attacking
// This function is called when a player dies to something other then a
// player or a monster. -- TC

void Player::die(DeathType dt) {
	bstring death = "";
	Player* killer=0;
	Object* statue=0;
	char	deathStr[2048];
	unsigned long oldxp=0;
	int		n=0;
	unsigned short oldlvl=0;
	bool killedByPlayer = dt == POISON_PLAYER || dt == CREEPING_DOOM;

	deathtype = DT_NONE;
	statistics.die();

	if(isStaff()) {
		printColor("^r*** You just died ***\n");
		broadcast(::isStaff, "^G### Sadly, %s just died.", name);

		clearAsEnemy();

		hp.restore();
		mp.restore();
		return;
	}

	// Any time a level 7+ this dies, they are backed up.
	if(level >= 7)
		save(true, LS_BACKUP);
	if(!killedByPlayer) {
		setFlag(P_KILLED_BY_MOB);
		if(level >= 13) {
			lasttime[LT_MOBDEATH].ltime = time(0);
			lasttime[LT_MOBDEATH].interval = 60*60*24L;
			setFlag(P_NO_SUICIDE);
		}
	}

	switch(dt) {
	case POISON_PLAYER:
	case CREEPING_DOOM:
		// these are player-caused death types
		switch(dt) {
		case POISON_PLAYER:
			sprintf(deathStr, "### Sadly, %s was poisoned to death.", name);
			logn("log.death", "%s was poisoned to death by %s.\n", name, poisonedBy.c_str());
			break;
		case CREEPING_DOOM:
			removeEffect("creeping-doom", false);
			sprintf(deathStr, "### Sadly, %s was killed by cursed spiders.", name);
			logn("log.death", "%s was poisoned to death by %s.\n", name, poisonedBy.c_str());
			break;
		default:
			break;
		}

		if(poisonedBy != "") {
			switch(dt) {
			case POISON_PLAYER:
				sprintf(deathStr, "### Sadly, %s was poisoned to death by %s.", name, poisonedBy.c_str());
				break;
			case CREEPING_DOOM:
				sprintf(deathStr, "### Sadly, %s was killed by %s's cursed spiders.", name, poisonedBy.c_str());
				break;
			default:
				break;
			}

			killer = gServer->findPlayer(poisonedBy.c_str());
			if(killer)
				getPkilled(killer, false, false);
		}

		break;
	case POISON_MONSTER:
		sprintf(deathStr, "### Sadly, %s was poisoned to death by %s.", name, poisonedBy.c_str());
		logn("log.death", "%s was poisoned to death by %s.\n", name, poisonedBy.c_str());
		death = "poison";
		break;
	case POISON_GENERAL:
		sprintf(deathStr, "### Sadly, %s was poisoned to death.", name);
		logn("log.death", "%s was poisoned to death.\n", name);
		death = "poison";
		break;
	case FALL:
		sprintf(deathStr, "### Sadly, %s fell to %s death.", name, hisHer());
		logn("log.death", "%s was killed by a fall.\n", name);
		death = "a fall";
		break;
	case PETRIFIED:
		sprintf(deathStr, "### Sadly, %s was turned to stone.\n### %s statue crumbles and breaks.",
			name, upHisHer());

		// all inventory, equipment, and gold are destroyed
		lose_all(this);
		coins.set(0, GOLD);
		printColor("^rAll your possessions were lost!\n");

		if(level >= 10) {
			if(loadObject(STATUE_OBJ, &statue)) {
				sprintf(statue->name, "broken statue of %s", name);
				statue->description = name;
				statue->description += " is forever frozen in stone.";
				strncpy(statue->key[0], "broken", 20);
				strncpy(statue->key[1], "statue", 20);
				strncpy(statue->key[2], name, 20);

				statue->setWeight(100 + getWeight());
				statue->setBulk(50);
				statue->setFlag(O_NO_BREAK);

				statue->addToRoom(getRoom());
			}
		}

		logn("log.death", "%s died from petrification.\n", name);
		death = "petrification";

		removeEffect("petrification", false);

		break;
	case DISEASE:
		sprintf(deathStr, "### Sadly, %s died from disease.", name);
		logn("log.death", "%s was killed by disease.\n", name);
		death = "disease";
		break;
	case WOUNDED:
		logn("log.death", "%s was killed by festering wounds.\n", name);
		sprintf(deathStr, "### Sadly, %s has bled to death.", name);
		death = "festering wounds";
		break;
	case ELVEN_ARCHERS:
		logn("log.death", "%s was shot to death by elven archers.\n", name);
		sprintf(deathStr, "### Sadly, %s was shot to death by elven archers.", name);
		death = "elven archers";
		break;
	case DEADLY_MOSS:
		logn("log.death", "%s was choked to death by deadly underdark moss.\n", name);
		sprintf(deathStr, "### Sadly, %s was choked to death by deadly underdark moss.", name);
		death = "deadly underdark moss";
	case PIERCER:
		sprintf(deathStr, "### Sadly, %s was impaled to death by a piercer.", name);
		logn("log.death", "%s was killed by a piercer.\n", name);
		death = "a piercer";
		break;
	case SMOTHER:
		sprintf(deathStr, "### Sadly, %s was engulfed by the earth.", name);
		logn("log.death", "%s was killed by an earth damage room.\n", name);
		death = "engulfing earth";
		break;
	case FROZE:
		sprintf(deathStr, "### Sadly, %s froze to death.", name);
		logn("log.death", "%s was killed by an air damage room.\n", name);
		death = "hypothermia";
		break;
	case LIGHTNING:
		sprintf(deathStr, "### Sadly, %s was blasted to bits by electricity.", name);
		logn("log.death", "%s was killed by an electricity damage room.\n", name);
		death = "electricity";
		break;
	case WINDBATTERED:
		sprintf(deathStr, "### Sadly, %s was ripped apart by the wind.", name);
		logn("log.death", "%s was killed by an air damage room.\n", name);
		death = "battering winds";
		break;
	case BURNED:
		sprintf(deathStr, "### Sadly, %s was burned alive.", name);
		logn("log.death", "%s was killed by a fire damage room or effect.\n", name);
		death = "a raging fire";
		break;
	case THORNS:
		sprintf(deathStr, "### Sadly, %s was killed by a wall of thorns.", name);
		logn("log.death", "%s was killed by a wall of thorns.\n", name);
		death = "a wall of thorns";
		break;
	case DROWNED:
		sprintf(deathStr, "### Sadly, %s drowned.", name);
		logn("log.death", "%s was killed by drowning.\n", name);
		death = "drowning";
		break;
	case DRAINED:
		sprintf(deathStr, "### Sadly, %s's life force was completely drained.", name);
		logn("log.death", "%s was killed by a pharm room.\n", name);
		death = "life-drain";
		break;
	case ZAPPED:
		sprintf(deathStr, "### Sadly, %s was zapped to death.", name);
		logn("log.death", "%s was killed by a combo lock.\n", name);
		death = "a fatal shock";
		break;
	case SHOCKED:
		sprintf(deathStr, "### Sadly, %s was shocked to death.", name);
		logn("log.death", "%s was killed by shocking.\n", name);
		death = "a fatal shock";
		break;
	case PIT:
		sprintf(deathStr, "### Sadly, %s fell into a pit and died.", name);
		logn("log.death", "%s was killed by a pit trap.\n", name);
		death = "a fall";
		break;
	case BLOCK:
		sprintf(deathStr, "### Sadly, %s was crushed to death by a giant stone block.", name);
		logn("log.death", "%s was killed by a stone block trap.\n", name);
		death = "a giant stone block";
		break;
	case DART:
		sprintf(deathStr, "### Sadly, %s was killed by a poisoned dart.", name);
		logn("log.death", "%s was killed by a poison dart trap.\n", name);
		death = "a poisoned dart";
		break;
	case ARROW:
		sprintf(deathStr, "### Sadly, %s was killed by a flight of arrows.", name);
		logn("log.death", "%s was killed by an arrow trap.\n", name);
		death = "a flight of arrows";
		break;
	case SPIKED_PIT:
		sprintf(deathStr, "### Sadly, %s fell into a pit and was impaled by spikes.", name);
		logn("log.death", "%s was killed by a spiked pit trap.\n", name);
		death = "a fall";
		break;
	case FIRE_TRAP:
		sprintf(deathStr, "### Sadly, %s was engulfed by flames and died.", name);
		logn("log.death", "%s was killed by a fire trap.\n", name);
		death = "a raging fire";
		break;
	case FROST:
		sprintf(deathStr, "### Sadly, %s was frozen alive, and died.", name);
		logn("log.death", "%s was killed by a frost trap.\n", name);
		death = "hypothermia";
		break;
	case ELECTRICITY:
		sprintf(deathStr, "### Sadly, %s was killed by electrocution.", name);
		logn("log.death", "%s was killed by an electricity trap.\n", name);
		death = "electricity";
		break;
	case ACID:
		sprintf(deathStr, "### Sadly, %s was dissolved by acid.", name);
		logn("log.death", "%s was killed by an acid trap.\n", name);
		death = "acid";
		break;
	case ROCKS:
		sprintf(deathStr, "### Sadly, %s was crushed to death in a rockslide.", name);
		logn("log.death", "%s was killed by a rockslide trap.\n", name);
		death = "a rockslide";
		break;
	case ICICLE_TRAP:
		sprintf(deathStr, "### Sadly, %s was impaled by a giant icicle.", name);
		logn("log.death", "%s was killed by a falling icicle trap.\n", name);
		death = "a giant icicle";
		break;
	case SPEAR:
		sprintf(deathStr, "### Sadly, %s was killed by a giant spear.", name);
		logn("log.death", "%s was killed by a spear trap.\n", name);
		death = "a spear";
		break;
	case CROSSBOW_TRAP:
		sprintf(deathStr, "### Sadly, %s was killed by a crossbow trap.", name);
		logn("log.death", "%s was killed by a crossbow trap.\n", name);
		death = "a crossbow bolt";
		break;
	case VINES:
		sprintf(deathStr, "### Sadly, %s was ripped apart by crawling vines.", name);
		logn("log.death", "%s was killed by deadly vines.\n", name);
		death = "deadly vines";
		break;
	case COLDWATER:
		sprintf(deathStr, "### Sadly, %s died from hypothermia.", name);
		logn("log.death", "%s was killed from hypothermia.\n", name);
		death = "hypothermia";
		break;
	case EXPLODED:
		sprintf(deathStr, "### Sadly, %s exploded.", name);
		logn("log.death", "%s was killed by exploding.\n", name);
		death = "self-combustion";
		break;
	case SPLAT:
		sprintf(deathStr, "### Sadly, %s tumbled to %s death. (SPLAT!)", name, hisHer());
		logn("log.death", "%s tumbled to %s death.\n", name, hisHer());
		death = "a fall";
		break;
	case BOLTS:
		sprintf(deathStr, "### Sadly, %s was blasted to death by energy bolts.", name);
		logn("log.death", "%s was killed by energy bolts.\n", name);
		death = "energy bolts";
		break;
	case BONES:
		sprintf(deathStr, "### Sadly, %s was crushed to death under an avalanche of bones.", name);
		logn("log.death", "%s was killed by a bone avalanche.\n", name);
		death = "an avalanche of bones";
		break;
	case EXPLOSION:
		sprintf(deathStr, "### Sadly, %s was vaporized in a magical explosion.", name);
		logn("log.death", "%s was killed by an exploding wand.\n", name);
		death = "an exploding wand";
		break;
	case SUNLIGHT:
		sprintf(deathStr, "### Sadly, %s was disintegrated by sunlight.", name);
		logn("log.death", "%s was disintegrated by sunlight.\n", name);
		death = "sunlight";
		break;
	default:
		sprintf(deathStr, "### Sadly, %s died.", name);
		logn("log.death", "%s was killed by %s.\n", name, name);
		death = "misfortune";
		break;
	}
	//if(level > 2)
	//	broadcast(deathStr);
	//else {
	//	broadcast(::isWatcher, "^C%s", deathStr);
	//	print(deathStr);
	//}
	broadcast(deathStr);


	oldxp = experience;
	oldlvl = level;

	if(!killedByPlayer) {
		cureDisease();
		removeCurse();
	}
	if(!killedByPlayer || dt == POISON_PLAYER)
		curePoison();

	// only drop all if killed by player
	dropEquipment(killedByPlayer && (!killer || !killer->isStaff()), killer ? killer->getSock() : 0);

	/*
	if(ready[WIELD - 1] && !ready[WIELD - 1]->flagIsSet(O_CURSED))
		unequip(WIELD);

	if(	ready[HELD - 1] &&
		ready[HELD - 1]->getWearflag() == WIELD &&
		!ready[HELD - 1]->flagIsSet(O_CURSED)
	) {
		Object* held = unequip(HELD, UNEQUIP_NOTHING);
		if(held) {
			held->addToRoom(getRoom());
			held->tempPerm();
		}
	}

	Object* temp=0;
	for(i=0; i<MAXWEAR; i++) {
		if(ready[i] && !ready[i]->flagIsSet(O_CURSED)) {
			if(!killedByPlayer) {
				// i is wearloc-1 so add 1
				unequip(i+1);
			}
			else {
				temp = unequip(i+1, UNEQUIP_NOTHING);
				if(temp)
					temp->addToRoom(getRoom());
			}
		}
	}
	*/
	checkDarkness();
	computeAC();
	computeAttackPower();
	courageous();


	int xploss = 0;
	if(!killedByPlayer) {
		if(level >= 5)
			addEffect("death-sickness");

		if(level < 10) {
			// Under level 10, 10% exp loss
			xploss = (int)((float)experience / 10.0);
			lostExperience += (long)xploss;
		} else {
			// Level 10 and over, 2% exp loss with a minimum of 10k
			xploss = MAX((long)( (float)experience * 0.02), 10000);
			print("You have lost %ld experience.\n", (long)xploss);
			lostExperience += (long)xploss;
		}
		subExperience((long)xploss);

	}


	n = level - exp_to_lev(experience);

	if(n > 1) {
		if(level < (MAXALVL+2))
			experience = needed_exp[level-3];
		else
			experience = (long)((needed_exp[MAXALVL-1]*(level-2)));
	}


	if(level >= 7)
		logn("log.death", "L:%dXP:%dE:%luA:%luF:%luW:%luE:%luC:%lu\n",
		     oldlvl, oldxp,
		     getRealm(EARTH), getRealm(WIND), getRealm(FIRE), getRealm(WATER),
		     getRealm(ELEC), getRealm(COLD));



	if(!killedByPlayer) {
		n = exp_to_lev(experience);
		while(level > n)
			downLevel();

		negativeLevels = 0;
	}

	hp.restore();
	mp.restore();

	unhide();

	// more info for staff
	broadcast(::isCt, "^rOld Exp: %u Lost Exp: %d, Old Lvl: %u, Lvl: %u, Room: %s",
		oldxp, xploss, oldlvl, level, getRoom()->fullName().c_str());

	if(isHardcore()) {
		hardcoreDeath(this);
	// if you die in jail, you stay in jail
	} else if(!inJail()) {
		BaseRoom *newRoom = getLimboRoom().loadRoom(this);
		if(newRoom) {
			deleteFromRoom();
			addToRoom(newRoom);
			doPetFollow();
		}
	}
}

//********************************************************************
//						clearAsPetEnemy
//********************************************************************

void Creature::clearAsPetEnemy() {
	ctag *cp = getRoom()->first_mon;
    while(cp) {
        if(cp->crt->isPet())
			cp->crt->getMonster()->delEnmCrt(name);
        cp = cp->next_tag;
    }
}

//********************************************************************
//						cleanFollow
//********************************************************************

void Monster::cleanFollow(Creature *killer) {
	if(isMonster() && following) {
		Player* player = following->getPlayer();

		// This should fix the bug with having a dm's pet killed while possessing a mob
		if(flagIsSet(M_DM_FOLLOW)) {
			player->setAlias(0);
			player->clearFlag(P_ALIASING);
		}
		if(following != killer) {
			player->printColor("^r%M's body has been destroyed.\n", this);
			if(killer)
				broadcast(killer->getSock(), getRoom(), "%M was killed by %N.", this, killer);
		}
		doStopFollowing(this, FALSE);
	}
}

//*********************************************************************
//						dropWeapons
//*********************************************************************
// handle dropping of weapons when you flee or die
// Return: true if any weapons were dropped, false if none were dropped

bool Player::dropWeapons(bool noDrop) {
	BaseRoom *room = getRoom();
	Object* weapon;
	bool	dropMain=false, dropSec=false, fumbleMain=false, fumbleSec=false;
	bool	jump=false;

	if(isStaff())
		return(false);

	noDrop = room->flagIsSet(R_DESTROYS_ITEMS);

	// check main weapons
	weapon = ready[WIELD-1];
	if(weapon && !weapon->flagIsSet(O_CURSED)) {
		if(weapon->flagIsSet(O_NO_DROP) || weapon->flagIsSet(O_STARTING) || noDrop) {
			// Add to inventory
			unequip(WIELD);
			fumbleMain = true;
		} else {
			dropMain = true;
			// Unequip, and add to the room
			unequip(WIELD, UNEQUIP_NOTHING, false);
			Limited::deleteOwner(this, weapon);
			weapon->addToRoom(room);
		}
	}

	// check secondary weapons
	weapon = ready[HELD-1];
	if(weapon && weapon->getWearflag() == WIELD) {
		if(weapon->flagIsSet(O_CURSED)) {
			if(!ready[WIELD-1]) {
				ready[WIELD-1] = ready[HELD-1];
				ready[HELD-1] = 0;
				jump = true;
			}
		} else {
			if(weapon->flagIsSet(O_NO_DROP) || weapon->flagIsSet(O_STARTING) || noDrop) {
				fumbleSec = true;
				// Add to inventory
				unequip(HELD);
			} else {
				dropSec = true;
				// Unequip and add to the room
				unequip(HELD, UNEQUIP_NOTHING, false);
				Limited::deleteOwner(this, weapon);
				weapon->addToRoom(room);
			}
		}
	}

	if(dropMain || dropSec || fumbleMain || fumbleSec) {
		// only fumble
		if(!dropMain && !dropSec)
			if(fumbleMain && fumbleSec)
				print("You fumble your weapons.\n");
			else
				print("You fumble your %sweapon.\n", fumbleSec ? "secondary " : "");

		// only drop
		else if(!fumbleMain && !fumbleSec)
			if(dropMain && dropSec)
				print("You drop your weapons.\n");
			else
				print("You drop your %sweapon.\n", dropSec ? "secondary " : "");

		// one of each
		else
			print("You drop your %s weapon and fumble your %s weapon.\n",
				dropMain ? "main" : "secondary", fumbleMain ? "main" : "secondary");

		if(jump)
			print("%s%s jumped to your primary hand! It's cursed!\n",
				!ready[WIELD-1]->flagIsSet(O_NO_PREFIX) ? "The " : "", ready[WIELD-1]->name);

		// checkDarkness(), computeAttackPower(), computeAC() should be handled outside this function
		return(true);
	}

	return(false);
}

//********************************************************************
//						effectDeathSickness
//********************************************************************

bool effectDeathSickness(EffectInfo *effect, Creature* target, EffectAction action, void* applier, ApplyFrom aFrom) {
	Player*  pTarget = target->getPlayer();

	// Only on players
	if(!pTarget)
		return(false);

	switch(action) {
	case EFFECT_COMPUTE:
		{
			int duration = 0;
			int strength = 1;

			// Two minutes per level
			duration = pTarget->getLevel() * 2 * 60;
			// We'll start the strength out at 100 and reduce it each pulse
			strength = 100;

			effect->setStrength(strength);
			effect->setDuration(duration);
		}
		break;
	case EFFECT_APPLY:
		break;
	case EFFECT_UNAPPLY:
		break;
	case EFFECT_PULSE:
		{
			int strength = effect->getStrength();
			int duration = effect->getDuration();

			if(!target->isEffected("petrification") && mrand(1,100) < (strength/2)) {
				target->wake("^DA strong urge to vomit wakes you!");
				target->printColor("^GYour death-sickness causes you to vomit. EWWW.\n");
				target->unhide();

				// Stun then half of the time for 0-2 seconds
				if(mrand(1,100) < 50) {
					target->stun(mrand(0,2));
					target->printColor("^DYou become disoriented.\n");
				}

				if(target->getRoom())
					broadcast(target->getSock(), target->getRoom(), "^G%M vomits all over the ground.", target);
			}
			int newStrength = 0;
			if(strength != 0)
				newStrength = (int)nearbyint((double)strength - (((double)strength/(double)duration)*20.0));

			if(strength > 75 && newStrength <= 75) {
				pTarget->printColor("^cYou feel a little better.\n");
				if(target->getRoom())
					broadcast(target->getSock(), target->getRoom(), "^c%M looks a little better.", target);
			} else if(strength > 50 && newStrength <= 50) {
				pTarget->printColor("^cYou feel better.\n");
				if(target->getRoom())
					broadcast(target->getSock(), target->getRoom(), "^c%M looks better.", target);
			} else if(strength > 25 && newStrength <= 25) {
				pTarget->printColor("^cYou are nearly recovered.\n");
				if(target->getRoom())
					broadcast(target->getSock(), target->getRoom(), "^c%M looks nearly recovered.", target);
			}

			strength = MIN(MAX(newStrength,0), 100);
			//target->print("DEATH SICKNESS: New Strength %d, Duration %d.\n", strength, duration);
			effect->setStrength(strength);
		}

		break;
	default:
		break;
	}
	return(true);
}