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/
/*
 * player.c
 *	 Player routines.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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
 *
 */
// C includes
#include <math.h>
#include <arpa/telnet.h> // IAC

// Mud includes
#include "mud.h"
#include "guilds.h"
#include "login.h"
#include "commands.h"
#include "move.h"
#include "effects.h"
#include "clans.h"
#include "property.h"
#include "unique.h"
#include "magic.h"

#include <iomanip>
#include <locale>

//********************************************************************
//				fixLts
//********************************************************************

void Creature::fixLts() {
	long tdiff=0, t = time(0);
	int i=0;
	if(isPet())
		tdiff = t - following->lasttime[LT_AGE].ltime;
	else
		tdiff = t - lasttime[LT_AGE].ltime;
	for(i=0; i<MAX_LT; i++) {
		if(i == LT_JAILED)
			continue;
		lasttime[i].ltime += tdiff;
		lasttime[i].ltime = MIN(t, lasttime[i].ltime);
	}
}

//********************************************************************
//				init
//********************************************************************
// This function initializes a player's stats and loads up the room he
// should be in. This should only be called when the player first
// logs on.

void Player::init() {
	char	file[80], str[50], watchers[128];
	BaseRoom *newRoom=0;
	Room	*uRoom=0;
	ctag	*cp;
	long	t = time(0);
	int		watch=0;

	statistics.setParent(this);

	// always make sure size matches up with race
	if(size == NO_SIZE) {
		size = gConfig->getRace(race)->getSize();

		EffectInfo *e=0;
		if(isEffected("enlarge"))
			e = getEffect("enlarge");
		else if(isEffected("reduce"))
			e = getEffect("reduce");

		if(e)
			changeSize(0, e->getStrength(), e->getName() == "enlarge");

		if(!size)
			size = SIZE_MEDIUM;
	}


	setFlag(P_KILL_AGGROS);

	clearFlag(P_LINKDEAD);
	clearFlag(P_SPYING);
	clearFlag(P_READING_FILE);
	clearFlag(P_AFK);
	clearFlag(P_DIED_IN_DUEL);
	clearFlag(P_VIEW_ZONES);

	if(cClass == CLERIC && level >= 7 && flagIsSet(P_CHAOTIC))
		setFlag(P_PLEDGED);

	if(level <= ALIGNMENT_LEVEL && !flagIsSet(P_CHOSEN_ALIGNMENT))
		clearFlag(P_CHAOTIC);

	if(level > ALIGNMENT_LEVEL)
		setFlag(P_CHOSEN_ALIGNMENT);

	if((cClass == CLERIC && level < 7) || (cClass == CLERIC && !flagIsSet(P_CHAOTIC)))
		clearFlag(P_PLEDGED);

	setFlag(P_SECURITY_CHECK_OK);

	if(isdm(name))
		cClass = DUNGEONMASTER;
	else if(isDm())
		cClass = CARETAKER;


	/* change this later
	   strength.getCur() = strength.max;
	dexterity.getCur() = dexterity.max;
	constitution.getCur() = constitution.max;
	intelligence.getCur() = intelligence.max;
	piety.getCur() = piety.max;
	*/
	if(!isStaff()) {
		daily[DL_ENCHA].max = 3;
		daily[DL_FHEAL].max = MAX(3, 3 + (level) / 3);
		daily[DL_TRACK].max = MAX(3, 3 + (level) / 3);
		daily[DL_DEFEC].max = 1;
		//	daily[DL_TELEP].max = MAX(3, MAX(2, level/4));
		daily[DL_TELEP].max = 3;
		if(level < 13)
			daily[DL_TELEP].max = 1;
		//	daily[DL_ETRVL].max = MAX(3, MAX(2, level/4));
		daily[DL_RCHRG].max = MAX(7, level / 2);
		daily[DL_HANDS].max = 3;

		daily[DL_RESURRECT].max = 1;
		daily[DL_SILENCE].max = 3;
		daily[DL_HARM].max = 2;
		daily[DL_SCARES].max = 3;
	} else {
		daily[DL_DEFEC].max = 100;
	}

	if(!gServer->isRebooting())
		initBuilder();


	if(flagIsSet(P_MISTED) && isDay())
		unmist();

	if(!isStaff()) {
		clearFlag(P_DM_INVIS);
		clearFlag(P_INCOGNITO);
	} else {
		// staff logs on with dmInvis
		setFlag(P_DM_INVIS);
	}


	if(isDm())
		clearFlag(P_BUGGED);



	//if((race != KATARAN && race != TROLL))
	learnLanguage(LCOMMON);	// All races speak common but troll and kataran.


	if(!current_language) {
		initLanguages();
		current_language = LCOMMON;
		setFlag(P_LANGUAGE_COLORS);
	}



	//	Paladins get auto know-aur
	if(cClass==PALADIN)
		addPermEffect("know-aura");
	//	Mages get auto d-m
	if((cClass==MAGE || cClass2 == MAGE) &&
	        !(cClass == MAGE && cClass2 == ASSASSIN)) {
		addPermEffect("detect-magic");

	}
	if(cClass == DRUID && level >= 7)
		addPermEffect("pass-without-trace");

	if(cClass == THIEF && cClass2 == MAGE)
		addPermEffect("detect-magic");

	if((cClass == MAGE || cClass2 == MAGE) && level >= 7)
		learnSpell(S_ARMOR);

	if(cClass == CLERIC && deity == CERIS && level >= 13)
		learnSpell(S_REJUVENATE);


	//  Werewolves get auto Detect-Invisibility at level 7
	if(isEffected("lycanthropy") && level >= 7)
		addPermEffect("detect-invisible");

	//  Rangers level 13+ can detect misted vampires.
	if(cClass == RANGER && level >= 13)
		addPermEffect("true-sight");

	// clear ignore flags
	clearFlag(P_IGNORE_CLASS_SEND);
	clearFlag(P_IGNORE_RACE_SEND);
	clearFlag(P_NO_BROADCASTS);
	clearFlag(P_IGNORE_NEWBIE_SEND);
	clearFlag(P_IGNORE_GOSSIP);
	clearFlag(P_IGNORE_CLAN);
	clearFlag(P_NO_TELLS);


	if(!flagIsSet(P_OUTLAW))
		setFlag(P_NO_SUMMON);

	clearFlag(P_LAG_PROTECTION_OPERATING);
	clearFlag(P_ALIASING);
	if(!isStaff())
		setFlag(P_PROMPT);

	strcpy(str, "");

	lasttime[LT_PLAYER_SAVE].ltime = t;
	lasttime[LT_PLAYER_SAVE].interval = SAVEINTERVAL;

	// SEARCH FOR ME
	lasttime[LT_TICK].ltime = t+30;
	lasttime[LT_TICK_SECONDARY].ltime = t+30;
	lasttime[LT_TICK_HARMFUL].ltime = t+30;

	// spam check
	lasttime[LT_PLAYER_SEND].ltime = t;
	lasttime[LT_PLAYER_SEND].misc = 0;


	// load up parent_rom for the broadcast below, but don't add the
	// player to the room or the messages will be out of order
	if(!area_room) {
		Property *p = gConfig->getProperty(room);
		if(	p &&
			p->getType() == PROP_STORAGE &&
			!p->isOwner(name) &&
			!p->isPartialOwner(name)
		) {
			// default to bound location
			Location l = bound;
			// but go to previous room, if possible
			if(previousRoom.room.id || previousRoom.mapmarker.getArea())
				l = previousRoom;

			if(l.room.id)
				room = l.room;
			else
				gConfig->areaInit(this, l.mapmarker);
		}
	}

	// area_room might get set by areaInit, so check again
	if(!area_room) {
		if(!loadRoom(room, &uRoom)) {
			loge("%s: %s (%s) Attempted logon to bad or missing room!\n", name,
				getSock()->getHostname().c_str(), room.str().c_str());
			// NOTE: Using ::isCt to use the global function, not the local function
			broadcast(::isCt, "^y%s: %s (%s) Attempted logon to bad or missing room (normal)!", name,
				getSock()->getHostname().c_str(), room.str().c_str());
			newRoom = abortFindRoom(this, "init_ply");
			uRoom = newRoom->getUniqueRoom();
		}


		if(uRoom && !isStaff() && !gServer->isRebooting()) {
			if(	(	uRoom->flagIsSet(R_LOG_INTO_TRAP_ROOM) ||
					uRoom->flagIsSet(R_SHOP_STORAGE)
				) &&
				uRoom->getTrapExit().id &&
				!loadRoom(uRoom->getTrapExit(), &uRoom)
			) {
				broadcast(::isCt, "^y%s: %s (%s) Attempted logon to bad or missing room!", name,
			    	getSock()->getHostname().c_str(), uRoom->getTrapExit().str().c_str());
				newRoom = abortFindRoom(this, "init_ply");
				uRoom = newRoom->getUniqueRoom();
			}

			if(	uRoom &&
				(	uRoom->isFull() ||
					uRoom->flagIsSet(R_NO_LOGIN) ||
					(!isStaff() && uRoom->flagIsSet(R_CONSTRUCTION))
				)
			) {
				newRoom = getRecallRoom().loadRoom(this);
				if(!newRoom) {
					broadcast(::isCt, "^y%s: %s (%s) Attempted logon to bad or missing room!", name,
						getSock()->getHostname().c_str(), getRecallRoom().str().c_str());
					newRoom = abortFindRoom(this, "init_ply");
				}
				uRoom = newRoom->getUniqueRoom();
			}
		}

		if(uRoom) {
			uRoom->killMortalObjects();
			// this gets assigned just for the sake of broadcast_login;
			// it doesnt actually do anything
			parent_rom = uRoom;
		} else {
			area_room = newRoom->getAreaRoom();
		}
	}

	//	str[0] = 0;
	if(!isDm()) {
		loge("%s(L:%d) (%s) %s. Room - %s (Port-%d)\n", name, level,
		     getSock()->getHostname().c_str(), gServer->isRebooting() ? "reloaded" : "logged on",
		     getRoom()->fullName().c_str(), Port);
	}
	if(isStaff())
		logn("log.imm", "%s  (%s) %s.\n",
		     name, getSock()->getHostname().c_str(),
		     gServer->isRebooting() ? "reloaded" : "logged on");

	// broadcast
	if(!gServer->isRebooting()) {
		defineColors();
		broadcast_login(this, 1);
	}

	checkDarkness();

	// don't do the actual adding until after broadcast
	if(area_room)
		addToRoom(area_room);
	else
		addToRoom(uRoom);


	fixLts();

	// pet code
	cp = first_fol;
	while(cp) {
		Monster* pet = cp->crt->getMonster();
		pet->following = this;
		pet->fixLts();

		pet->updateAttackTimer();
		pet->lasttime[LT_SPELL].interval = 3;
		pet->lasttime[LT_SPELL].ltime =
		pet->lasttime[LT_MOB_THIEF].ltime = t;

		pet->addToRoom(getRoom());
		gServer->addActive(pet);

		cp = cp->next_tag;
	}


	if(!gServer->isRebooting())
		bug("%s logged into room %s.\n", name, getRoom()->fullName().c_str());


	wearCursed();
	if(!flagIsSet(P_NO_AUTO_WEAR))
		wearAll(this, true);

	computeLuck();
	update();

	Socket* sock = getSock();

	if(!gServer->isRebooting()) {
		sprintf(file, "%snews.txt", DOCPATH);
		viewLoginFile(sock, file);

		sprintf(file, "%snewbie_news.txt", DOCPATH);
		viewLoginFile(sock, file);

		if(isCt()) {
			sprintf(file, "%snews.txt", DMHSUBDIR);
			viewLoginFile(sock, file);
		}
		if(isStaff() && strcmp(name, "Bane")) {
			sprintf(file, "%snews.txt", BHSUBDIR);
			viewLoginFile(sock, file);
		}
		if(isCt() || flagIsSet(P_WATCHER)) {
			sprintf(file, "%swatcher_news.txt", DMHSUBDIR);
			viewLoginFile(sock, file);
		}

		sprintf(file, "%slatest_post.txt", DOCPATH);
		viewLoginFile(sock, file, false);

		hasNewMudmail();
	}

	if(!gServer->isRebooting()) {
		strcpy(watchers, "");
		printColor("^yWatchers currently online: ");
		std::pair<bstring, Player*> p;
		Player* ply;
		foreach(p, gServer->players) {
			ply = p.second;

			if(!ply->isConnected())
				continue;
			if(!ply->isPublicWatcher())
				continue;
			if(!canSee(ply))
				continue;

			strcat(watchers, ply->name);
			strcat(watchers, ", ");
			watch++;
		}

		if(watch) {
			watchers[strlen(watchers) - 2] = '.';
			watchers[strlen(watchers) - 1] = 0;
			printColor("%s\n", watchers);
		} else
			printColor("None.\n");
	}

	if(isCt())
		showGuildsNeedingApproval(this);

	if(hp.getCur() < 0)
		hp.setCur(1);


	// only players and builders are effected
	if(!isCt()) {
		// players cant set eavesdropper flag anymore
		clearFlag(P_EAVESDROPPER);
		clearFlag(P_SUPER_EAVESDROPPER);
		// DM/CT only flag
		clearFlag(P_NO_COMPUTE);

		setMonkDice();

		if(lasttime[LT_AGE].ltime > time(0) ) {
			lasttime[LT_AGE].ltime = time(0);
			lasttime[LT_AGE].interval = 0;
			broadcast(::isCt, "^yPlayer %s had negative age and is now validated.\n", name);
			logn("log.validate", "Player %s had negative age and is now validated.\n", name);
		}

	}

	// Make sure everything is in order with their guild.
	if(guild) {
		if(!gConfig->getGuild(guild)) {
			if(guildRank >= GUILD_PEON)
				print("You are now guildless because your guild has been disbanded.\n");
			guild = guildRank = 0;
		} else if(!gConfig->getGuild(guild)->isMember(name)) {
			guild = guildRank = 0;
		}
	}


	if(actual_level < level)
		actual_level = MAX(level, actual_level);

	killDarkmetal();

	if(weaponTrains != 0)
		print("You have %d weapon train(s) available!\n", weaponTrains);

	computeInterest(t, true);
	resetObjectIds();
}

//*********************************************************************
//						uninit
//*********************************************************************
// This function de-initializes a player who has left the game. This
// is called whenever a player quits or disconnects, right before save
// is called.

void Player::uninit() {
	Creature* target=0;
	ctag	*cp=0, *prev=0;
	int		i=0;
	long	t=0;
	char	str[50];

	// Save storage rooms
	if(parent_rom && parent_rom->info.isArea("stor"))
		gConfig->resaveRoom(parent_rom->info);

	courageous();
	clearMaybeDueling();

	cp = first_fol;
	while(cp) {
		// pet code
		if(cp->crt->isPet()) {
			Monster* pet = cp->crt->getMonster();
			gServer->delActive(pet);
			pet->deleteFromRoom();
			free_crt(pet);
			cp->crt = 0;
			cp = cp->next_tag;
			continue;
		}
		cp->crt->following = 0;
		if(cp->crt->isPlayer() && !gServer->isRebooting())
			cp->crt->print("You stop following %s.\n", name);
		prev = cp->next_tag;
		cp->crt = NULL;
		cp = prev;
	}
	while(first_fol && first_fol->crt == NULL) {
		prev = first_fol;
		first_fol = first_fol->next_tag;
		delete prev;
	}
	cp = first_fol;
	while(cp) {
		if(cp->next_tag == NULL)
			break;
		if(cp->next_tag->crt == NULL) {
			prev = cp->next_tag;
			cp->next_tag = cp->next_tag->next_tag;
			delete prev;
			continue;
		}
		cp = cp->next_tag;
	}
	if(following) {
		target = following;
		cp = target->first_fol;
		if(cp->crt == this) {
			target->first_fol = cp->next_tag;
			delete cp;
		} else
			while(cp) {
				if(cp->crt == this) {
					prev->next_tag = cp->next_tag;
					delete cp;
					break;
				}
				prev = cp;
				cp = cp->next_tag;
			}
		following = 0;

		if(!isStaff() && !gServer->isRebooting())
			target->print("%s stops following you.\n", name);
	}

	for(i=0; i<MAXWEAR; i++) {
		if(ready[i]) {
			// i is wearloc-1, so add 1
			unequip(i+1);
		}
	}

	if(!gServer->isRebooting())
		broadcast_login(this, 0);

	if(parent_rom || area_room)
		deleteFromRoom();

	t = time(0);
	strcpy(str, (char *)ctime(&t));
	str[strlen(str)-1] = 0;
	if(!isDm() && !gServer->isRebooting())
		loge("%s logged off.\n", name);

	// Clean up the old "Extra" struct
	etag	*crm, *ctemp;

	crm = first_charm;
	while(crm) {
		ctemp = crm;
		crm = crm->next_tag;
		delete ctemp;
	}
	first_charm = 0;

	if(alias_crt) {
		alias_crt->clearFlag(M_DM_FOLLOW);
		alias_crt = 0;
	}
}

//*********************************************************************
//						courageous
//*********************************************************************

void Player::courageous() {
	int	**scared;

	scared = &(scared_of);
	if(*scared) {
		free(*scared);
		*scared = NULL;
	}
}

//*********************************************************************
//						checkTempEnchant
//*********************************************************************

void Player::checkTempEnchant( Object* object) {
	long i=0, t=0;
	if(object && object->flagIsSet(O_TEMP_ENCHANT)) {
		t = time(0);
		i = LT(object, LT_ENCHA);
		if(i < t) {
			object->setArmor(MAX(0, object->getArmor() - object->getAdjustment()));
			object->setAdjustment(0);
			object->clearFlag(O_TEMP_ENCHANT);
			object->clearFlag(O_RANDOM_ENCHANT);
			if(isEffected("detect-magic"))
				printColor("The enchantment on your %s fades.\n", object->name);
		}
	}
}

//*********************************************************************
//						checkEnvenom
//*********************************************************************

void Player::checkEnvenom( Object* object) {
	long i=0, t=0;
	if(object && object->flagIsSet(O_ENVENOMED)) {
		t = time(0);
		i = LT(object, LT_ENVEN);
		if(i < t) {
			object->clearFlag(O_ENVENOMED);
			object->clearEffect();
			printColor("The poison on your %s deludes.\n", object->name);
		}
	}
}

//*********************************************************************
//						checkInventory
//*********************************************************************
// Check inventory for temp enchant or envenom

void Player::checkInventory( ) {
	otag *op = first_obj, *cop=0;
	int i=0;

	// Check for temp enchant items carried/inventory/in containers
	while(op) {
		if(op->obj->getType() == CONTAINER) {
			cop=op->obj->first_obj;
			while(cop) {
				checkTempEnchant(cop->obj);
				cop=cop->next_tag;
			}
		}
		checkTempEnchant(op->obj);
		op=op->next_tag;
	}
	for(i=0; i<MAXWEAR; i++) {
		if(i==(HELD-1) && ready[i] && ready[i]->getType()==CONTAINER) {
			cop=ready[i]->first_obj;
			while(cop) {
				checkTempEnchant(cop->obj);
				cop=cop->next_tag;
			}
		}
		checkTempEnchant(ready[i]);
	}
}

//*********************************************************************
//						checkEffectsWearingOff
//*********************************************************************

void Player::checkEffectsWearingOff() {
	long t = time(0);
	int staff = isStaff();

	// Added P_STUNNED and LT_PLAYER_STUNNED stun for dodge code.
	if(flagIsSet(P_STUNNED)) {
		if(t > LT(this, LT_PLAYER_STUNNED)) {
			clearFlag(P_STUNNED);
		}
	}

	if(flagIsSet(P_FRENZY)) {
		if(t > LT(this, LT_FRENZY)) {
			loseFrenzy();
		}
	}
	if(flagIsSet(P_FOCUSED) && (cClass == MONK || staff)) {
		if(t > LT(this, LT_FOCUS)) {
			printColor("^cYou lose your concentration.\n");
			clearFlag(P_FOCUSED);
			//computeAC();
			computeAttackPower();
		}
	}
	if(flagIsSet(P_MISTBANE)) {
		if(t > LT(this, LT_FOCUS)) {
			printColor("^bYour mistbane is ended.\n");
			clearFlag(P_MISTBANE);
		}
	}

	if(flagIsSet(P_OUTLAW)) {
		if(t > LT(this, LT_OUTLAW)) {
			printColor("^yYou are no longer an outlaw.\n");
			clearFlag(P_OUTLAW);
			clearFlag(P_OUTLAW_WILL_BE_ATTACKED);
			setFlag(P_NO_SUMMON);
			clearFlag(P_OUTLAW_WILL_LOSE_XP);
			clearFlag(P_NO_GET_ALL);
		}
	}

	// only force them to wake from sleeping unconsciousness when they're
	// completely healed.
	if(flagIsSet(P_UNCONSCIOUS)) {
		if(	(	!flagIsSet(P_SLEEPING) &&
				t > LT(this, LT_UNCONSCIOUS)
			) ||
			(	flagIsSet(P_SLEEPING) && (
				(hp.getCur() >= hp.getMax() && mp.getCur() >= mp.getMax()) ||
				(cClass == VAMPIRE && !getRoom()->vampCanSleep(getSock()))
			) )
		) {
			printColor("^cYou wake up.\n");
			clearFlag(P_UNCONSCIOUS);
			wake();

			clearFlag(P_DIED_IN_DUEL);
			broadcast(getSock(), getRoom(), "%M wakes up.", this);
		}
	}
	if(flagIsSet(P_RUNNING)) {
		if(t > LT(this, LT_ENDURANCE)) {
			clearFlag(P_RUNNING);
		}
	}
	if(flagIsSet(P_PRAYED)) {
		if(t > LT(this, LT_PRAY)) {
			losePray();
		}
	}
	if(flagIsSet(P_BLOODSAC)) {
		if(t > LT(this, LT_BLOOD_SACRIFICE)) {
			printColor("^mYour demonic power leaves you.\n");
			clearFlag(P_BLOODSAC);
			 hp.setCur(hp.getMax());
		}
	}
	if(flagIsSet(P_NO_SUICIDE)) {
		if(t > LT(this, LT_MOBDEATH)) {
			printColor("^yYour cooling-off period has ended.\n");
			clearFlag(P_NO_SUICIDE);
		}
	}
	if(flagIsSet(P_ANCHOR)) {
		if(t > LT(this, LT_ANCHOR)) {
			printColor("^cYou are no longer magically anchored.\n");
			clearFlag(P_ANCHOR);
		}
	}

	if(flagIsSet(P_BERSERKED)) {
		if(t > LT(this, LT_BERSERK) || (LT(this, LT_BERSERK) - t <=0)) {
			loseRage();
		}
	}
	if(flagIsSet(P_HIDDEN) && !staff) {
		if(t - lasttime[LT_HIDE].ltime > 300L) {
			printColor("^cShifting shadows expose you.\n");
			unhide(false);
		}
	}
	if(flagIsSet(P_FREE_ACTION)) {
		if(t > LT(this, LT_FREE_ACTION)) {
			printColor("^c^#You no longer magically move freely.\n");
			clearFlag(P_FREE_ACTION);
			computeAC();
			computeAttackPower();
		}
	}

	if(flagIsSet(P_NO_PKILL)) {
		if(t > LT(this, LT_NO_PKILL)) {
			printColor("^c^#You can now be pkilled again.\n");
			clearFlag(P_NO_PKILL);
		}
	}



	if(flagIsSet(P_DOCTOR_KILLER)) {
		if(t > LT(this, LT_KILL_DOCTOR) || staff) {
			printColor("^y^#The doctors have forgiven you.\n");
			clearFlag(P_DOCTOR_KILLER);
		}
	}


	if(flagIsSet(P_NO_TICK_MP)) {
		if(t > LT(this, LT_NOMPTICK) || staff) {
			printColor("^cYour magical vitality has returned.\n");
			clearFlag(P_NO_TICK_MP);
		}

	}

	if(flagIsSet(P_NO_TICK_HP)) {
		if(t > LT(this, LT_NOHPTICK) || staff) {
			printColor("^gYou now heal normally again.\n");
			clearFlag(P_NO_TICK_HP);
		}
	}

	if(flagIsSet(P_CANT_BROADCAST)) {
		if(t > LT(this, LT_NO_BROADCAST)) {
			printColor("^rYou can broadcast again, now don't abuse it this time.\n");
			clearFlag(P_CANT_BROADCAST);
		}
	}

	if(flagIsSet(P_MISTED)) {
		if(isDay() && !staff)
			unmist();
	}
	if(flagIsSet(P_CHARMED)) {
		if(t > LT(this, LT_CHARMED) || staff) {
			printColor("^yYou are again in control of your actions.\n");
			clearFlag(P_CHARMED);
		}
	}

	if(negativeLevels) {
		if(t > LT(this, LT_LEVEL_DRAIN) || staff) {
			long expTemp=0;
			negativeLevels--;
			if(negativeLevels) {
				printColor("^WYou have regained a lost level.\n");
				expTemp = experience;
				upLevel();
				experience = expTemp;

				lasttime[LT_LEVEL_DRAIN].ltime = t;
				lasttime[LT_LEVEL_DRAIN].interval = 60L + 5*bonus((int)constitution.getCur());
			} else {
				printColor("^WYou have recovered all your lost levels.\n");
				expTemp = experience;
				upLevel();
				experience = expTemp;
			}
		}
	}

	if(t > LT(this, LT_JAILED) && flagIsSet(P_JAILED)) {
		printColor("^rA demonic jailer just arrived.\n");
		printColor("The demonic jailer says, \"You have been released from your torment.\"\n");
		printColor("The demonic jailer casts word of recall on you.\n");

		broadcast(getSock(), getRoom(), "A demonic jailer just arrived.\nThe demonic jailer casts word of recall on %s.", name);
		broadcast(getSock(), getRoom(), "The demonic jailer sneers evilly and spits on you.\nThe demonic jailer vanishes.");
		broadcast("^R### Cackling demons shove %s from the Dungeon of Despair.", name);
		doRecall();

		clearFlag(P_JAILED);
	}


	if(	t > LT(this, LT_JAILED) &&
		parent_rom && parent_rom->flagIsSet(R_MOB_JAIL) &&
		!staff
	) {
		printColor("A jailer just arrived.\n");
		printColor("The jailer says, \"You're free to go...get out!\"\n");
		printColor("The jailer opens the cell door and shoves you out.\n");
		printColor("The jailer goes back to napping.\n");

		doRecall();
	}
}

//*********************************************************************
//						doPetrificationDmg
//*********************************************************************

bool Creature::doPetrificationDmg() {
	if(!isEffected("petrification"))
		return(false);

	wake("Terrible nightmares disturb your sleep!");
	printColor("^c^#Petrification spreads toward your heart.\n");
	hp.decrease(MAX(1,(hp.getMax()/15 - bonus((int)constitution.getCur()))));

	if(hp.getCur() < 1) {
		Player* pThis = getPlayer();
		if(pThis)
			pThis->die(PETRIFIED);
		else
			die(this);
		return(true);
	}
	return(false);
}



//*********************************************************************
//						update
//*********************************************************************
// This function checks up on all a player's time-expiring flags to see
// if some of them have expired.  If so, flags are set accordingly.

void Player::update() {
	BaseRoom* room=0;
	long 	t=0;
	int		item=0;
	bool	fighting = inCombat();

	t = time(0);
	lasttime[LT_AGE].interval += (t - lasttime[LT_AGE].ltime);
	lasttime[LT_AGE].ltime = t;

	checkInventory();

	if(flagIsSet(P_LAG_PROTECTION_SET) && flagIsSet(P_LAG_PROTECTION_ACTIVE) && level > 1) {
		// Suspends lag protect if this not in battle.
		if(!fighting)
			clearFlag(P_LAG_PROTECTION_ACTIVE);
	}

	// All mobs will not attack a petrified opponent.
	if(isEffected("petrification"))
		clearAsEnemy();

	if(flagIsSet(P_UNIQUE_TO_DECAY) && !fighting) {
		gConfig->uniqueDecay(this);
		clearFlag(P_UNIQUE_TO_DECAY);
	}


	checkEffectsWearingOff();

	if(isDiseased() && immuneToDisease())
		cureDisease();
	if(isPoisoned() && immuneToPoison())
		curePoison();

	//pulseEffects(t);

	if(mp.getCur() < 0)
		mp.setCur(0);

	room = getRoom();
	if(room && !flagIsSet(P_LINKDEAD))
		doRoomHarms(room, this);

	if(t > LT(this, LT_PLAYER_SAVE)) {
		lasttime[LT_PLAYER_SAVE].ltime = t;
		cmdSave(this, 0);
	}

	item = getLight();
	if(item && item != MAXWEAR+1) {
		if(ready[item-1]->getType() == LIGHTSOURCE) {
			ready[item-1]->decShotscur();
			if(ready[item-1]->getShotscur() < 1) {
				print("Your %s died out.\n", ready[item-1]->name);
				broadcast(getSock(), room, "%M's %s died out.", this, ready[item-1]->name);
			}
		}
	}
	// Fix funky stuff
	setMonkDice();
	computeAC();
	computeAttackPower();
}

//*********************************************************************
//						checkWeaponSkillGain
//*********************************************************************

void Player::checkWeaponSkillGain() {
	int numWeapons = 0;
	// Everyone gets a new weapon skill every title
	if(((level+2)%3) == 0)
		numWeapons ++;
	switch(cClass) {
		case FIGHTER:
			if(!cClass2) {
				if(level%4 == 0)
					numWeapons++;
			} else {
				// Mutli fighters get weapon skills like other fighting classes
				if(level%8 == 8)
					numWeapons++;
			}
			break;
		case BERSERKER:
			if(level%6)
				numWeapons++;
			break;
		case THIEF:
		case RANGER:
		case ROGUE:
		case BARD:
		case PALADIN:
		case DEATHKNIGHT:
		case ASSASSIN:
			if(level/8)
				numWeapons++;
			break;
		case CLERIC:
		case DRUID:
		case VAMPIRE:
			if(cClass2) {
				// Cle/Ass
				if(level/8)
					numWeapons++;
			} else {
				if(level/12)
					numWeapons++;
			}
			break;
		case MAGE:
			if(cClass2) {
				if(level/12)
					numWeapons++;
			}
			break;
		default:
			break;

	}
	if(numWeapons != 0) {
		weaponTrains += numWeapons;
		print("You can now learn %d more weapon skill(s)!\n", numWeapons);
	}
}

//*********************************************************************
//						upLevel
//*********************************************************************
// This function should be called whenever a player goes up a level.
// It raises there hit points and magic points appropriately, and if
// it is initializing a new character, it sets up the character.

// TODO: Make this function use the information loaded from classes.xml for hp/mp/saves etc
void Player::upLevel() {
	int	a=0;
	int	switchNum=0;
	bool relevel=false;

	if(isStaff()) {
		level++;
		return;
	}

	PlayerClass *pClass = gConfig->classes[getClassString()];
	const RaceData* rData = gConfig->getRace(race);
	LevelGain *lGain = 0;

	if(level == actual_level)
		actual_level++;
	else
		relevel = true;

	level++;

	// Check for level info
	if(!pClass) {
		print("Error: Can't find your class!\n");
		if(!isStaff()) {
			bstring errorStr = "Error: Can't find class: " + getClassString();
			merror(errorStr.c_str(), FATAL);
		}
		return;
	} else {
		print("Checking Leveling Information for [%s:%d].\n", pClass->getName().c_str(), level);
		lGain = pClass->getLevelGain(level);
		if(!lGain && level != 1) {
			print("Error: Can't find any information for your level!\n");
			if(!isStaff()) {
				bstring errorStr = "Error: Can't find level info for " + getClassString() + level;
				merror(errorStr.c_str(), FATAL);
			}
			return;
		}
	}

	if(!relevel) {
		// Only check weapon gains on a real level
		checkWeaponSkillGain();
	}

	if(level == 1) {
		hp.setMax(pClass->getBaseHp());
		if(cClass == FIGHTER && flagIsSet(P_PTESTER))
			focus.setMax(2);
		else if(cClass != BERSERKER && cClass != LICH)
			mp.setMax(pClass->getBaseMp());

		// TODO: Dom: HC: hardcore characters get double at level 1
		//if(isHardcore()) {
		//	hp.setMax(hp.getMax()*2);
		//	mp.setMax(mp.getMax()*2);
		//}

		hp.restore();
		mp.restore();
		damage = pClass->damage;

		if(!rData) {
			print("Error: Can't find your race, no saving throws for you!\n");
		} else {
			saves[POI].chance = rData->getSave(POI);
			saves[DEA].chance = rData->getSave(DEA);
			saves[BRE].chance = rData->getSave(BRE);
			saves[MEN].chance = rData->getSave(MEN);
			saves[SPL].chance = rData->getSave(SPL);
			setFlag(P_SAVING_THROWSPELL_LEARN);

			checkSkillsGain(rData->getSkillBegin(), rData->getSkillEnd());
		}
		// Base Skills

		if(!pClass) {
			print("Error: Can't find your class, no skills for you!\n");
		} else {
			checkSkillsGain(pClass->getSkillBegin(), pClass->getSkillEnd());
		}

	} else {
		hp.increaseMax(lGain->getHp());
		if(cClass == FIGHTER && flagIsSet(P_PTESTER))
			focus.increaseMax(2);
		else if(cClass != BERSERKER && cClass != LICH)
			mp.increaseMax(lGain->getMp());

//		idx = (level - 2) % 10;

		switchNum = lGain->getStat();
//		if(!cClass2)
//			switchNum = level_cycle[(int) cClass][idx];
//		else
//			switchNum = multiStatCycle[getMultiClassID(cClass, cClass2)][idx];

		switch(switchNum) {
		case STR:
			strength.addCur(10);
			strength.addMax(10);
			print("You have become stronger.\n");
			break;
		case DEX:
			dexterity.addCur(10);
			dexterity.addMax(10);
			print("You have become more dextrous.\n");
			break;
		case CON:
			constitution.addCur(10);
			constitution.addMax(10);
			print("You have become healthier.\n");
			break;
		case INT:
			intelligence.addCur(10);
			intelligence.addMax(10);
			print("You have become more intelligent.\n");
			break;
		case PTY:
			piety.addCur(10);
			piety.addMax(10);
			print("You have become more pious.\n");
			break;
		}

		switchNum=0;


		switchNum = lGain->getSave();
//		idx = (level - 2) % 10;
//		if(!cClass2)
//			switchNum = saving_throw_cycle[(int) cClass][idx];
//		else
//			switchNum = multiSaveCycle[getMultiClassID(cClass, cClass2)][idx];

		switch (switchNum) {
		case POI:
			saves[POI].chance += 3;
			print("You are now more resistant to poison.\n");
			break;
		case DEA:
			saves[DEA].chance += 3;
			print("You are now more able to resist traps and death.\n");
			break;
		case BRE:
			saves[BRE].chance += 3;
			print("You are now more resistant to breath weapons and explosions.\n");
			break;
		case MEN:
			saves[MEN].chance += 3;
			print("You are now more resistant to mind dominating attacks.\n");
			break;
		case SPL:
			saves[SPL].chance += 3;
			print("You are now more resistant to magical spells.\n");
			break;
		}

		if(!relevel) {
			// Saving throw bug fix: Spells and mental saving throws will now be
			// properly reset so they can increase like the other ones  -Bane
			for(a=POI; a<= SPL;a++)
				saves[a].gained = 0;
		}

		// Give out skills here
		if(lGain->hasSkills()) {
			print("Ok you should have some skills, seeing what they are.\n");
			checkSkillsGain(lGain->getSkillBegin(), lGain->getSkillEnd());
		} else {
			print("No skills for you at this level.\n");
		}

	}

	// Make constitution actualy worth something
	if(cClass != LICH) {
		if(cClass == BERSERKER && constitution.getCur() >= 70)
			hp.increaseMax(1);
		if(constitution.getCur() >= 130)
			hp.increaseMax(1);
		if(constitution.getCur() >= 210)
			hp.increaseMax(1);
		if(constitution.getCur() >= 250)
			hp.increaseMax(1);
	}


	if(!negativeLevels) {
		hp.restore();
		mp.restore();
	}

	if(cClass == LICH && !relevel) {
		if(level == 7)
			print("Your body has deteriorated slightly.\n");
		if(level == 13)
			print("Your body has decayed immensely.\n");
		if(level == 19)
			print("What's left of your flesh hangs on your bones.\n");
		if(level == 25)
			print("You are now nothing but a dried up and brittle set of bones.\n");
	}


	if((cClass == MAGE || cClass2 == MAGE) && level == 7 && !relevel) {
		print("You have learned the armor spell.\n");
		learnSpell(S_ARMOR);
	}

	if(cClass == CLERIC && level >= 13 &&
	        deity == CERIS && !spellIsKnown(S_REJUVENATE)) {
		print("%s has granted you the rejuvinate spell.\n", gConfig->getDeity(deity)->getName().c_str());
		learnSpell(S_REJUVENATE);
	}


	if(	cClass == CLERIC &&
		level >= 19 &&
	    deity == CERIS &&
		!spellIsKnown(S_RESURRECT)
	) {
		print("%s has granted you the resurrect spell.\n", gConfig->getDeity(deity)->getName().c_str());
		learnSpell(S_RESURRECT);
	}

	if(	cClass == CLERIC &&
	    !cClass2 &&
		level >= 22 &&
	    deity == ARAMON &&
		!spellIsKnown(S_BLOODFUSION)
	) {
		print("%s has granted you the bloodfusion spell.\n", gConfig->getDeity(deity)->getName().c_str());
		learnSpell(S_BLOODFUSION);
	}

	updateGuild(this, GUILD_LEVEL);
	update();

	// getting [custom] as a title lets you pick a new one,
	// even if you have already picked one.
	if(!relevel) {

	}
	/*	if(cClass == BARD)
	pick_song(player);*/
}

//*********************************************************************
//						downLevel
//*********************************************************************
// This function is called when a player loses a level due to dying or
// for some other reason. The appropriate stats are downgraded.

void Player::downLevel() {

	if(isStaff()) {
		level--;
		return;
	}


	int		switchNum=0;

	PlayerClass *pClass = gConfig->classes[getClassString()];
	LevelGain *lGain = 0;

	// Check for level info
	if(!pClass) {
		print("Error: Can't find your class!\n");
		if(!isStaff()) {
			return;
			//bstring errorStr = "Error: Can't find class: " + getClassString();
			//merror(errorStr.c_str(), FATAL);
		}
		return;
	} else {
		//print("Checking Leveling Information for [%s:%d].\n", pClass->getName().c_str(), level);
		lGain = pClass->getLevelGain(level);
		if(!lGain) {
			return;
			//print("Error: Can't find any information for your level!\n");
			//bstring errorStr = "Error: Can't find level info for " + getClassString() + level;
			//merror(errorStr.c_str(), FATAL);
		}
	}

	level--;

	hp.decreaseMax(lGain->getHp());
	if(cClass == FIGHTER && flagIsSet(P_PTESTER))
		focus.decreaseMax(2);
	else if(cClass != BERSERKER && cClass != LICH)
		mp.decreaseMax(lGain->getMp());

//	if(!cClass2) {
//		hp.decreaseMax(class_stats[(int) cClass].hp);
//
//		if(cClass == FIGHTER && flagIsSet(P_PTESTER))
//			focus.decreaseMax(2);
//		else if(cClass != BERSERKER && cClass != LICH)
//			mp.decreaseMax(class_stats[(int) cClass].mp);
//
//	} else {
//		hp.decreaseMax(multiHpMpAdj[getMultiClassID(cClass, cClass2)][0]);
//		mp.decreaseMax(multiHpMpAdj[getMultiClassID(cClass, cClass2)][1]);
//	}
//
//
//	if(((level % 2) !=0) && cClass == LICH) // liches lose a HP at every odd level.
//		hp.decreaseMax(1);

	// Make constitution actualy worth something
	if(cClass != LICH) {

		if(cClass == BERSERKER && constitution.getCur() >= 70)
			hp.decreaseMax(1);

		if(constitution.getCur() >= 130)
			hp.decreaseMax(1);
		if(constitution.getCur() >= 210)
			hp.decreaseMax(1);
		if(constitution.getCur() >= 250)
			hp.decreaseMax(1);
	}

	hp.restore();

	if(cClass != LICH)
		mp.restore();


//	idx = (level - 1) % 10;
//	if(!cClass2)
//		switchNum = level_cycle[(int) cClass][idx];
//	else
//		switchNum = multiStatCycle[getMultiClassID(cClass, cClass2)][idx];

	switchNum = lGain->getStat();

	switch (switchNum) {
	case STR:
		strength.addCur(-10);
		strength.addMax(-10);
		print("You have lost strength.\n");
		break;
	case DEX:
		dexterity.addCur(-10);
		dexterity.addMax(-10);
		print("You have lost dexterity.\n");
		break;
	case CON:
		constitution.addCur(-10);
		constitution.addMax(-10);
		print("You have lost constitution.\n");
		break;
	case INT:
		intelligence.addCur(-10);
		intelligence.addMax(-10);
		print("You have lost intelligence.\n");
		break;
	case PTY:
		piety.addCur(-10);
		piety.addMax(-10);
		print("You have lost piety.\n");
		break;
	}


	//	if(isCt() || flagIsSet(P_PTESTER)) {
//	idx = (level - 1) % 10;
//	if(!cClass2)
//		switchNum = saving_throw_cycle[(int) cClass][idx];
//	else
//		switchNum = multiSaveCycle[getMultiClassID(cClass, cClass2)][idx];

	switchNum = lGain->getSave();

	switch (switchNum) {
	case POI:
		saves[POI].chance -= 3;
		if(!negativeLevels)
			saves[POI].gained = 0;
		print("You are now less resistant to poison.\n");
		break;
	case DEA:
		saves[DEA].chance -= 3;
		if(!negativeLevels)
			saves[DEA].gained = 0;
		print("You are now less able to resist traps and death.\n");
		break;
	case BRE:
		saves[BRE].chance -= 3;
		if(!negativeLevels)
			saves[BRE].gained = 0;
		print("You are now less resistant to breath weapons and explosions.\n");
		break;
	case MEN:
		saves[MEN].chance -= 3;
		if(!negativeLevels)
			saves[MEN].gained = 0;
		print("You are now less resistant to mind dominating attacks.\n");
		break;
	case SPL:
		saves[SPL].chance -= 3;
		if(!negativeLevels)
			saves[SPL].gained = 0;
		print("You are now less resistant to magical spells.\n");
		break;
	}
	//	}

	updateGuild(this, GUILD_DIE);
	setMonkDice();

}

//*********************************************************************
//						addObj
//*********************************************************************
// This function adds the object pointer to by the first parameter to
// the inventory of the player pointed to by the second parameter.

void Creature::addObj(Object* object, bool resetUniqueId) {
	otag	*op=0, *temp=0, *prev=0;
	Player* pPlayer = getPlayer();

	if(resetUniqueId && pPlayer)
		pPlayer->setObjectId(object);
	hooks.execute("beforeAddObject", object);
	object->hooks.execute("beforeAddObject", this);

	object->parent_crt = this;
	object->parent_obj = 0;
	object->parent_room = 0;
	object->clearFlag(O_JUST_LOADED);

	// players have big inventories; to keep the mud from searching them when it
	// doesnt need to, record a flag on the player
	if(pPlayer && object->flagIsSet(O_DARKMETAL))
		setFlag(P_DARKMETAL);
	if(object->flagIsSet(O_DARKNESS))
		setFlag(pPlayer ? P_DARKNESS : M_DARKNESS);

	op = new otag;
	if(!op)
		merror("add_obj_crt", FATAL);
	op->obj = object;
	op->next_tag = 0;

	if(!first_obj) {

		first_obj = op;

	} else {

		temp = first_obj;
		if(	strcmp(temp->obj->cmpName(), object->cmpName()) > 0 ||
			(!strcmp(temp->obj->name, object->name) &&
			temp->obj->getAdjustment() > object->getAdjustment())
		) {

			op->next_tag = temp;
			first_obj = op;

		} else {

			while(temp) {
				if(	strcmp(temp->obj->cmpName(), object->cmpName()) > 0 ||
					(!strcmp(temp->obj->name, object->name) &&
					temp->obj->getAdjustment() > object->getAdjustment())
				)
					break;
				prev = temp;
				temp = temp->next_tag;
			}
			op->next_tag = prev->next_tag;
			prev->next_tag = op;

		}
	}

	if(pPlayer)
		pPlayer->updateItems(object);

	hooks.execute("afterAddObject", object);
	object->hooks.execute("afterAddObject", this);

	killDarkmetal();
}

//*********************************************************************
//				delObj
//*********************************************************************
// This function removes the object pointer to by the first parameter
// from the player pointed to by the second. This does NOT DELETE THE
// OBJECT. You will have to do that yourself, if desired.

// we put in a choice to do darkmetal or not so we wont have
// recursion problems
// if you pass false to darkness, you MUST run checkDarkness() unless
// you are certain the item you are deleted isn't flagged O_DARKNESS

void Creature::finishDelObj(Object* object, bool breakUnique, bool removeUnique, bool darkmetal, bool darkness, bool keep) {
	if(darkmetal)
		killDarkmetal();
	if(breakUnique || removeUnique) {
		Player* player = getMaster();
		if(player) {
			if(breakUnique)
				Limited::remove(player, object);
			else if(removeUnique)
				Limited::deleteOwner(player, object);
		}
	}
	if(darkness)
		checkDarkness();
	if(!keep)
		object->clearFlag(O_KEEP);

	hooks.execute("afterDeleteFromObject", object);
	object->hooks.execute("afterDeleteFromObject", this);
}

void Creature::delObj(Object* object, bool breakUnique, bool removeUnique, bool darkmetal, bool darkness, bool keep) {
	otag 	*temp=0, *prev=0;

	hooks.execute("beforeDeleteFromObject", object);
	object->hooks.execute("beforeDeleteFromObject", this);

	// dont run checkDarkness if this isnt a dark item
	if(!object->flagIsSet(O_DARKNESS))
		darkness = false;
	object->clearFlag(O_BEING_PREPARED);
	object->clearFlag(O_HIDDEN);
	object->clearFlag(O_JUST_LOADED);

	// if it doesnt have a parent_crt, it's either being worn or is in a bag
	if(!object->parent_crt) {
		// the object is being worn
		if(object->getWearflag() && ready[object->getWearflag()-1] == object) {
			ready[object->getWearflag()-1] = 0;
			finishDelObj(object, breakUnique, removeUnique, darkmetal, darkness, keep);
		} else {
			// the object is in a bag somewhere
			// problem is, we don't know which bag
			temp = first_obj;
			while(temp) {
				if(temp->obj->getType() == CONTAINER) {
					prev = temp->obj->first_obj;
					while(prev) {
						if(prev->obj == object) {
							del_obj_obj(object, temp->obj);
							finishDelObj(object, breakUnique, removeUnique, darkmetal, darkness, keep);
							return;
						}
						prev = prev->next_tag;
					}
				}
				temp = temp->next_tag;
			}

			// not in their inventory? they must be wearing a bag
			for(int i=0; i < MAXWEAR; i++) {
				if(!ready[i])
					continue;
				if(ready[i]->getType() == CONTAINER) {
					prev = ready[i]->first_obj;
					while(prev) {
						if(prev->obj == object) {
							del_obj_obj(object, ready[i]);
							finishDelObj(object, breakUnique, removeUnique, darkmetal, darkness, keep);
							return;
						}
						prev = prev->next_tag;
					}
				}
			}
		}
		return;
	}

	object->parent_crt = 0;
	if(first_obj->obj == object) {
		temp = first_obj->next_tag;
		delete first_obj;
		first_obj = temp;
		finishDelObj(object, breakUnique, removeUnique, darkmetal, darkness, keep);
		return;
	}

	prev = first_obj;
	temp = prev->next_tag;
	while(temp) {
		if(temp->obj == object) {
			prev->next_tag = temp->next_tag;
			delete temp;
			finishDelObj(object, breakUnique, removeUnique, darkmetal, darkness, keep);
			return;
		}
		prev = temp;
		temp = temp->next_tag;
	}
}

//*********************************************************************
//						computeAC
//*********************************************************************
// This function computes a player's armor class by
// examining their stats and the items they are holding.

void Player::computeAC() {
	int		ac=0, i;

	// Monks are a little more impervious to damage than other classes due to
	// their practice in meditation
	if(cClass == MONK)
		ac += level * 10;

	// Wolves have a tough skin that grows tougher as they level up
	if(isEffected("lycanthropy"))
		ac += level * 20;

	// Vamps have a higher armor at night
	if(isEffected("vampirism") && !isDay())
		ac += level * 5 / 2;


	for(i=0; i<MAXWEAR; i++) {
		if(ready[i] && ready[i]->getType() == ARMOR) {
			ac += ready[i]->getArmor();
			// penalty for wearing armor of the wrong size
			if(size && ready[i]->getSize() && size != ready[i]->getSize())
				ac -= ready[i]->getArmor()/2;
			if(ready[i]->getWearflag() < FINGER || ready[i]->getWearflag() > HELD)
				ac += (int)(ready[i]->getAdjustment()*4.4);
		}
	}

	if((cClass == DRUID || isCt()) && isEffected("barkskin")) {
		EffectInfo* barkskin = getEffect("barkskin");
		ac += (int)(((level+bonus(constitution.getCur())) * barkskin->getStrength())*4.4);
	}

	if(isEffected("armor"))
		ac += 200 + 200 * level /30;

// TODO: Possibly add in +armor for higher constitution?

	if(flagIsSet(P_BERSERKED))
		ac -= 100;
	if(isEffected("fortitude"))
		ac += 100;
	if(isEffected("weakness"))
		ac -= 100;

	armor = MAX(0, ac);
}


//*********************************************************************
//						alignAdjustAcThaco
//*********************************************************************
// This function will be called whenever alignment changes,
// primarily in combat after killing a mob. It is initially
// designed to be only for monks and werewolves, but it can
// be applied to any class by altering the function. -TC

void Player::alignAdjustAcThaco() {
	if(cClass != MONK && cClass != WEREWOLF)
		return;

	computeAC();
	computeAttackPower();
}

//*********************************************************************
//						getArmorWeight
//*********************************************************************

int Player::getArmorWeight() const {
	int weight=0;

	for(int i=0; i<MAXWEAR; i++) {
		if(	ready[i] && ready[i]->getType() == ARMOR &&
			(	(ready[i]->getWearflag() < FINGER) ||
				(ready[i]->getWearflag() > HELD)
			)
		)
			weight += ready[i]->getActualWeight();
	}

	return(weight);
}


//*********************************************************************
//						mprofic
//*********************************************************************
// This function returns the magical realm proficiency as a percentage

int mprofic(const Creature* player, int index) {
	const Player *pPlayer = player->getConstPlayer();
	long	prof_array[12];
	int	i=0, n=0, prof=0;

	switch(player->getClass()) {
	case MAGE:
		if(pPlayer && (pPlayer->getSecondClass() == ASSASSIN || pPlayer->getSecondClass() == THIEF)) {
			prof_array[0] = 0L;
			prof_array[1] = 1024L;
			prof_array[2] = 4092L;
			prof_array[3] = 8192L;
			prof_array[4] = 16384L;
			prof_array[5] = 32768L;
			prof_array[6] = 70536L;
			prof_array[7] = 119000L;
			prof_array[8] = 226410L;
			prof_array[9] = 709410L;
			prof_array[10] = 2973307L;
			prof_array[11] = 500000000L;
		} else {
			prof_array[0] = 0L;
			prof_array[1] = 1024L;
			prof_array[2] = 2048L;
			prof_array[3] = 4096L;
			prof_array[4] = 8192L;
			prof_array[5] = 16384L;
			prof_array[6] = 35768L;
			prof_array[7] = 85536L;
			prof_array[8] = 140000L;
			prof_array[9] = 459410L;
			prof_array[10] = 2073306L;
			prof_array[11] = 500000000L;

		}
		break;
	case LICH:
		prof_array[0] = 0L;
		prof_array[1] = 1024L;
		prof_array[2] = 2048L;
		prof_array[3] = 4096L;
		prof_array[4] = 8192L;
		prof_array[5] = 16384L;
		prof_array[6] = 35768L;
		prof_array[7] = 85536L;
		prof_array[8] = 140000L;
		prof_array[9] = 459410L;
		prof_array[10] = 2073306L;
		prof_array[11] = 500000000L;
		break;
	case CLERIC:
		if(pPlayer && (pPlayer->getSecondClass() == ASSASSIN || pPlayer->getSecondClass() == FIGHTER)) {
			prof_array[0] = 0L;
			prof_array[1] = 1024L;
			prof_array[2] = 8192L;
			prof_array[3] = 16384L;
			prof_array[4] = 32768L;
			prof_array[5] = 65536L;
			prof_array[6] = 105000L;
			prof_array[7] = 165410L;
			prof_array[8] = 287306L;
			prof_array[9] = 809410L;
			prof_array[10] = 3538232L;
			prof_array[11] = 500000000L;
		} else {
			prof_array[0] = 0L;
			prof_array[1] = 1024L;
			prof_array[2] = 4092L;
			prof_array[3] = 8192L;
			prof_array[4] = 16384L;
			prof_array[5] = 32768L;
			prof_array[6] = 70536L;
			prof_array[7] = 119000L;
			prof_array[8] = 226410L;
			prof_array[9] = 709410L;
			prof_array[10] = 2973307L;
			prof_array[11] = 500000000L;
		}
		break;
	case THIEF:
		if(pPlayer && pPlayer->getSecondClass() == MAGE) {
			prof_array[0] = 0L;
			prof_array[1] = 1024L;
			prof_array[2] = 8192L;
			prof_array[3] = 16384L;
			prof_array[4] = 32768L;
			prof_array[5] = 65536L;
			prof_array[6] = 105000L;
			prof_array[7] = 165410L;
			prof_array[8] = 287306L;
			prof_array[9] = 809410L;
			prof_array[10] = 3538232L;
			prof_array[11] = 500000000L;
		} else {
			prof_array[0] = 0L;
			prof_array[1] = 1024L;
			prof_array[2] = 40000L;
			prof_array[3] = 80000L;
			prof_array[4] = 120000L;
			prof_array[5] = 160000L;
			prof_array[6] = 205000L;
			prof_array[7] = 222000L;
			prof_array[8] = 380000L;
			prof_array[9] = 965410L;
			prof_array[10] = 5495000;
			prof_array[11] = 500000000L;
		}
		break;

	case FIGHTER:
		if(pPlayer && pPlayer->getSecondClass() == MAGE) {
			prof_array[0] = 0L;
			prof_array[1] = 1024L;
			prof_array[2] = 8192L;
			prof_array[3] = 16384L;
			prof_array[4] = 32768L;
			prof_array[5] = 65536L;
			prof_array[6] = 105000L;
			prof_array[7] = 165410L;
			prof_array[8] = 287306L;
			prof_array[9] = 809410L;
			prof_array[10] = 3538232L;
			prof_array[11] = 500000000L;
		} else {
			prof_array[0] = 0L;
			prof_array[1] = 1024L;
			prof_array[2] = 40000L;
			prof_array[3] = 80000L;
			prof_array[4] = 120000L;
			prof_array[5] = 160000L;
			prof_array[6] = 205000L;
			prof_array[7] = 222000L;
			prof_array[8] = 380000L;
			prof_array[9] = 965410L;
			prof_array[10] = 5495000;
			prof_array[11] = 500000000L;
		}
		break;
	case PALADIN:
	case BARD:
	case VAMPIRE:
	case DRUID:
		prof_array[0] = 0L;
		prof_array[1] = 1024L;
		prof_array[2] = 4092L;
		prof_array[3] = 8192L;
		prof_array[4] = 16384L;
		prof_array[5] = 32768L;
		prof_array[6] = 70536L;
		prof_array[7] = 119000L;
		prof_array[8] = 226410L;
		prof_array[9] = 709410L;
		prof_array[10] = 2973307L;
		prof_array[11] = 500000000L;
		break;
	case DEATHKNIGHT:
	case MONK:
	case RANGER:
		prof_array[0] = 0L;
		prof_array[1] = 1024L;
		prof_array[2] = 8192L;
		prof_array[3] = 16384L;
		prof_array[4] = 32768L;
		prof_array[5] = 65536L;
		prof_array[6] = 105000L;
		prof_array[7] = 165410L;
		prof_array[8] = 287306L;
		prof_array[9] = 809410L;
		prof_array[10] = 3538232L;
		prof_array[11] = 500000000L;
		break;
	default:
		prof_array[0] = 0L;
		prof_array[1] = 1024L;
		prof_array[2] = 40000L;
		prof_array[3] = 80000L;
		prof_array[4] = 120000L;
		prof_array[5] = 160000L;
		prof_array[6] = 205000L;
		prof_array[7] = 222000L;
		prof_array[8] = 380000L;
		prof_array[9] = 965410L;
		prof_array[10] = 5495000;
		prof_array[11] = 500000000L;
		break;
	}

	n = player->getRealm((Realm)index);
	for(i=0; i<11; i++)
		if(n < prof_array[i+1]) {
			prof = 10*i;
			break;
		}

	prof += ((n - prof_array[i])*10) / (prof_array[i+1] - prof_array[i]);

	return(prof);
}

//*********************************************************************
//						getFallBonus
//*********************************************************************
// This function computes a player's bonus (or susceptibility) to falling
// while climbing.

int Player::getFallBonus() const {
	int	fall = bonus((int)dexterity.getCur())*5;

	for(int j=0; j<MAXWEAR; j++)
		if(ready[j])
			if(ready[j]->flagIsSet(O_CLIMBING_GEAR))
				fall += ready[j]->damage.getPlus()*3;
	return(fall);
}



//*********************************************************************
//						lowest_piety
//*********************************************************************
// This function finds the player with the lowest piety in a given room.
// The pointer to that player is returned. In the case of a tie, one of
// them is randomly chosen.

Player* lowest_piety(BaseRoom* room, bool invis) {
	Creature* player=0;
	ctag	*cp = room->first_ply;
	int		totalpiety=0, pick=0;

	if(!cp)
		return(0);

	while(cp) {
		if(cp->crt->flagIsSet(P_HIDDEN) ||
		        (cp->crt->isInvisible() && !invis) ||
		        cp->crt->flagIsSet(P_DM_INVIS) ) {
			cp = cp->next_tag;
			continue;
		}
		totalpiety += MAX(1, (25 - cp->crt->piety.getCur()));
		cp = cp->next_tag;
	}

	if(!totalpiety)
		return(0);
	pick = mrand(1, totalpiety);

	cp = room->first_ply;
	totalpiety = 0;
	while(cp) {
		if(cp->crt->flagIsSet(P_HIDDEN) ||
		        (cp->crt->isInvisible() && !invis) ||
		        cp->crt->flagIsSet(P_DM_INVIS) ) {
			cp = cp->next_tag;
			continue;
		}
		totalpiety += MAX(1, (25 - cp->crt->piety.getCur()));
		if(totalpiety >= pick) {
			player = cp->crt;
			break;
		}
		cp = cp->next_tag;
	}

	return(player->getPlayer());
}

//*********************************************************************
//						hasLight
//*********************************************************************
// This function returns true if the player in the first parameter is
// holding or wearing anything that generates light.

int Player::getLight() const {
    int i=0, light=0;

    for(i = 0; i < MAXWEAR; i++) {
        if (!ready[i])
            continue;
        if (ready[i]->flagIsSet(O_LIGHT_SOURCE)) {
            if ((ready[i]->getType() == LIGHTSOURCE &&
                ready[i]->getShotscur() > 0) ||
                ready[i]->getType() != LIGHTSOURCE) {
                light = 1;
                break;
            }
        }
    }

    if(light)
        return (i + 1);
    return(0);
}

//***********************************************************************
//						sendPrompt
//***********************************************************************
// This function returns the prompt that the player should be seeing

void Player::sendPrompt() {
	bstring toPrint;

	if(fd < 0)
		return;

	// no prompt in this situation
	if(flagIsSet(P_SPYING) || flagIsSet(P_READING_FILE))
		return;

	if(flagIsSet(P_PROMPT) || flagIsSet(P_ALIASING)) {
		std::ostringstream promptStr;

		if(!flagIsSet(P_NO_EXTRA_COLOR))
			promptStr << alignColor();

		if(flagIsSet(P_ALIASING) && alias_crt) {
			promptStr << "(" << alias_crt->hp.getCur() << " H " << alias_crt->mp.getCur() << " M): ";
		} else {
			promptStr << "(" << hp.getCur() << " H";
			if(cClass != LICH && cClass != BERSERKER
					&& (cClass != FIGHTER || !flagIsSet(P_PTESTER)))
				promptStr << " " << mp.getCur() << " M";
			else if(cClass == FIGHTER && flagIsSet(P_PTESTER))
				promptStr << " " << focus.getCur() << " F";

			if(flagIsSet(P_SHOW_XP_IN_PROMPT))
				promptStr << " " << expToLevel(true);
			promptStr << "):^x ";

		}
		toPrint = promptStr.str();
	} else
		toPrint = ": ";

	if(flagIsSet(P_AFK))
		toPrint += "^r[AFK]^x ";
	if(flagIsSet(P_NEWLINE_AFTER_PROMPT))
		toPrint += "\n";

	if(getSock()->getEor() == 1) {
		unsigned char eor_str[] = {IAC, EOR, '\0' };
		toPrint += eor_str;
	}
	toPrint = colorize((char*)toPrint.c_str(), flagIsSet(P_ANSI_COLOR));
	mySock->write(toPrint);
}

//*********************************************************************
//						computeLuck
//*********************************************************************
// This sets the luck value for a given player

int Player::computeLuck() {
	int		num=0, alg=0, con=0, smrt=0;

	alg = abs(alignment);
	alg = alg + 1;

	alg /= 10;


	// alignment only matters for these classes
	if(cClass != PALADIN && cClass != CLERIC && cClass != DEATHKNIGHT && cClass != LICH)
		alg = 0;


	if(	!alg ||
		(cClass == PALADIN && deity != GRADIUS && getAdjustedAlignment() > NEUTRAL) ||
		(cClass == DEATHKNIGHT && getAdjustedAlignment() < NEUTRAL) ||
		(cClass == LICH && alignment <= -500) ||
		(cClass == CLERIC && (deity == ENOCH || deity == LINOTHAN || deity == KAMIRA) && getAdjustedAlignment() >= LIGHTBLUE) ||
		(cClass == CLERIC && (deity == ARAMON || deity == ARACHNUS) && getAdjustedAlignment() <= PINKISH)
	)
		alg = 1;

	if(cClass != LICH)  // Balances mages with liches for luck.
		con = constitution.getCur()/10;
	else
		con = piety.getCur()/10;

	smrt = intelligence.getCur()/10;

	num = 100*(smrt+con);
	num /= alg;

	if(ready[HELD-1] && ready[HELD-1]->flagIsSet(O_LUCKY))
		num += ready[HELD-1]->damage.getPlus();

	// Carrying around alot of gold isn't very lucky!
	if(!isStaff())
		num -= (coins[GOLD] / 20000);

	num = MAX(1, MIN(99, num));

	luck = num;
	return(num);
}

//*********************************************************************
//						getStatusStr
//*********************************************************************
// returns a status string that describes the hp condition of the creature

const char* Creature::getStatusStr(int dmg) const {
	int health = hp.getCur() - dmg;

	if(health < 1)
		return "'s dead!";

	switch(MIN(health * 10 / (hp.getMax() ? hp.getMax() : 1), 10)) {
	case 10:
		return("'s unharmed.");
	case 9:
		return("'s relatively unscathed.");
	case 8:
		return("'s a little battered.");
	case 7:
		return("'s getting bruised.");
	case 6:
		return("'s noticeably bleeding.");
	case 5:
		return("'s having some trouble.");
	case 4:
		return(" doesn't look too good.");
	case 3:
		return("'s beginning to stagger.");
	case 2:
		return(" has some nasty wounds.");
	case 1:
		return(" isn't going to last much longer.");
	case 0:
		return(" is about to die!");
	}
	return("");
}


//*********************************************************************
//						checkForSpam
//*********************************************************************

bool Player::checkForSpam() {
	int		t = time(0);

	if(!isCt()) {
		if(lasttime[LT_PLAYER_SEND].ltime == t) {
			// 4 in one second is spam
			if(++lasttime[LT_PLAYER_SEND].misc > 3) {
				silenceSpammer();
				return(true);
			}
		} else if(lasttime[LT_PLAYER_SEND].ltime + 1 == t) {
			// 6 in 2 seconds is spam
			if(++lasttime[LT_PLAYER_SEND].misc > 5) {
				silenceSpammer();
				return(true);
			}
		} else if(lasttime[LT_PLAYER_SEND].ltime + 2 == t) {
			// 7 in 3 seconds is spam
			if(++lasttime[LT_PLAYER_SEND].misc > 6 ) {
				silenceSpammer();
				return(true);
			}
		} else {
			// reset spam counter
			lasttime[LT_PLAYER_SEND].ltime = t;
			lasttime[LT_PLAYER_SEND].misc = 1;
		}
	}


	return(false);
}


//*********************************************************************
//						silenceSpammer
//*********************************************************************

void Player::silenceSpammer() {
	if(isEffected("silence")) {
		// already spamming
		EffectInfo* silenceEffect = getEffect("silence");
		silenceEffect->setDuration(silenceEffect->getDuration() + 300);
		printColor("^rYou have been given an additional 5 minutes of silence for spamming again!\n");
	} else {
		// first spam
		addEffect("silence", 120, 1);

		printColor("^rYou have been silenced for 2 minutes for spamming!\n");
		broadcast(getSock(), getRoom(), "%s has been silenced for spamming!\n",name);
	}
}

//*********************************************************************
//						setMonkDice
//*********************************************************************

void Player::setMonkDice() {
	int	nLevel = MAX(0, MIN(level, MAXALVL));

	// reset monk dice?
	if(cClass == MONK) {
		damage = monk_dice[nLevel];
	} else if(cClass == WEREWOLF) {
		damage = wolf_dice[nLevel];
	}
}

//*********************************************************************
//						getMultiClassID
//*********************************************************************

int	getMultiClassID(char cls, char cls2) {
	int		id=0;

	if(!cls2)
		return(0);

	switch(cls) {
	case FIGHTER:
		switch(cls2) {
		case MAGE:
			id = 1;
			break;
		case THIEF:
			id = 2;
			break;
		}
		break;

	case CLERIC:
		switch(cls2) {
		case ASSASSIN:
			id = 3;
			break;
		case FIGHTER:
			id = 6;
			break;
		}
		break;

	case MAGE:
		switch(cls2) {
		case ASSASSIN:
			id = 7;
			break;
		case THIEF:
			id = 4;
			break;
		}
		break;

	case THIEF:
		id = 5;  // thief/mage is only thief-first combo
		break;
	}

	return(id);
}

//*********************************************************************
//						isOutdoors
//*********************************************************************

bool isOutdoors(Socket* sock) {
	if(sock && sock->getPlayer())
		return(sock->getPlayer()->getRoom()->isOutdoors());
	return(false);
}

//*********************************************************************
//						initLanguages
//*********************************************************************

void Player::initLanguages() {

	switch(race) {
	case DWARF:
		learnLanguage(LDWARVEN);
		learnLanguage(LGNOMISH);
		learnLanguage(LKOBOLD);
		learnLanguage(LGOBLINOID);
		learnLanguage(LORCISH);
		break;
	case ELF:
		learnLanguage(LELVEN);
		learnLanguage(LGNOMISH);
		learnLanguage(LORCISH);
		break;
	case HALFELF:
		learnLanguage(LELVEN);
		learnLanguage(LHALFLING);
		break;
	case HALFLING:
		learnLanguage(LHALFLING);
		learnLanguage(LGNOMISH);
		break;
	case ORC:
		learnLanguage(LORCISH);
		learnLanguage(LGIANTKIN);
		break;
	case HALFGIANT:
		learnLanguage(LGIANTKIN);
		learnLanguage(LOGRISH);
		learnLanguage(LTROLL);
		break;
	case GNOME:
		learnLanguage(LGNOMISH);
		learnLanguage(LDWARVEN);
		learnLanguage(LGOBLINOID);
		learnLanguage(LKOBOLD);
		break;
	case TROLL:
		learnLanguage(LTROLL);
		break;
	case HALFORC:
		learnLanguage(LORCISH);
		break;
	case OGRE:
		learnLanguage(LOGRISH);
		learnLanguage(LGIANTKIN);
		break;
	case DARKELF:
		learnLanguage(LDARKELVEN);
		learnLanguage(LORCISH);
		learnLanguage(LELVEN);
		learnLanguage(LDWARVEN);
		learnLanguage(LGNOMISH);
		learnLanguage(LKOBOLD);
		learnLanguage(LGOBLINOID);
		break;
	case GOBLIN:
		learnLanguage(LGOBLINOID);
		learnLanguage(LORCISH);
		break;
	case MINOTAUR:
		learnLanguage(LMINOTAUR);
		break;
	case SERAPH:
		learnLanguage(LELVEN);
		learnLanguage(LCELESTIAL);
		learnLanguage(LINFERNAL);
		learnLanguage(LGNOMISH);
		learnLanguage(LHALFLING);
		learnLanguage(LABYSSAL);
		break;
	case KOBOLD:
		learnLanguage(LKOBOLD);
		learnLanguage(LGNOMISH);
		learnLanguage(LDARKELVEN);
		learnLanguage(LOGRISH);
		learnLanguage(LGIANTKIN);
		break;
	case CAMBION:
		learnLanguage(LINFERNAL);
		learnLanguage(LDARKELVEN);
		learnLanguage(LELVEN);
		learnLanguage(LCELESTIAL);
		learnLanguage(LORCISH);
		learnLanguage(LGIANTKIN);
		learnLanguage(LGOBLINOID);
		break;
	case BARBARIAN:
		learnLanguage(LBARBARIAN);
		break;
	case KATARAN:
		learnLanguage(LKATARAN);
		break;
	case TIEFLING:
		learnLanguage(LHALFLING);
		learnLanguage(LINFERNAL);
		learnLanguage(LABYSSAL);
		learnLanguage(LORCISH);
		learnLanguage(LGOBLINOID);
		learnLanguage(LTIEFLING);
		break;
	} // End switch.

	switch(cClass) {
	case THIEF:
	case ASSASSIN:
	case BARD:
	case ROGUE:
		learnLanguage(LTHIEFCANT);
		break;
	case DRUID:
	case RANGER:
		learnLanguage(LDRUIDIC);
		break;
	case WEREWOLF:
		learnLanguage(LWOLFEN);
		break;
	case MAGE:
	case LICH:
		learnLanguage(LARCANIC);
		break;
	case CLERIC:
		switch(deity) {
		case ARAMON:
			learnLanguage(LINFERNAL);
			break;
		case ENOCH:
			learnLanguage(LCELESTIAL);
			break;
		case ARES:
			learnLanguage(LBARBARIAN);
			break;
		case KAMIRA:
			learnLanguage(LTHIEFCANT);
			break;
		}
		break;
	}
	return;
}

//*********************************************************************
//						doRecall
//*********************************************************************

void Player::doRecall(int roomNum) {
	Room	*new_rom=0;
	BaseRoom *newRoom=0;

	if(roomNum == -1) {
		newRoom = recallWhere();
	} else {
		if(!loadRoom(roomNum, &new_rom))
			newRoom = abortFindRoom(this, "doRecall");
		else
			newRoom = new_rom;
	}

	courageous();
	deleteFromRoom();
	addToRoom(newRoom);
	doPetFollow();
}


//*********************************************************************
//						recallWhere
//*********************************************************************
// Because of ethereal plane, we don't always know where we're going to
// recall to. We need a function to figure out where we are going.

BaseRoom* Creature::recallWhere() {
	// A builder should never get this far, but let's not chance it.
	// Only continue if they can't load the perm_low_room.
	if(cClass == BUILDER) {
		Room* uRoom=0;
		if(loadRoom(ROOM_BUILDER_PERM_LOW, &uRoom))
			return(uRoom);
	}

	if(	getRoom()->flagIsSet(R_ETHEREAL_PLANE) &&
		(mrand(1,100) <= 50)
	) {
		return(teleportWhere());
	}

	BaseRoom* room = getRecallRoom().loadRoom(getPlayer());
	// uh oh!
	if(!room)
		return(abortFindRoom(this, "recallWhere"));
	return(room);

}

//*********************************************************************
//						teleportWhere
//*********************************************************************
// Loops through rooms and finds us a place we can teleport to.
// This function will always return a room or it will crash trying to.

BaseRoom* Creature::teleportWhere() {
	Room	*uRoom=0;
	BaseRoom *newRoom=0;
	AreaRoom* aRoom=0;
	const CatRefInfo* cri = gConfig->getCatRefInfo(getRoom());
	int		i=0, zone = cri ? cri->teleportZone : 0;
	Area	*area=0;
	Location l;
	bool	found = false;



	// A builder should never get this far, but let's not chance it.
	// Only continue if they can't load the perm_low_room.
	if(cClass == BUILDER) {
		if(loadRoom(ROOM_BUILDER_PERM_LOW, &uRoom))
			return(uRoom);
	}

	do {
		if(i>250)
			return(abortFindRoom(this, "teleportWhere"));
		cri = gConfig->getRandomCatRefInfo(zone);

		// if this fails, we have nowhere to teleport to
		if(!cri)
			return(getRoom());

		// special area used to signify overland map
		if(cri->area == "area") {
			area = gConfig->getArea(cri->id);
			l.mapmarker.set(area->id, mrand(0, area->width), mrand(0, area->height), mrand(0, area->depth));
			if(area->canPass(0, &l.mapmarker, true)) {
				//area->adjustCoords(&mapmarker.x, &mapmarker.y, &mapmarker.z);

				// don't bother sending a creature because we've already done
				// canPass check here
				//aRoom = area->loadRoom(0, &mapmarker, false);
				if(Move::getRoom(this, 0, &uRoom, &aRoom, false, &l.mapmarker)) {
					if(uRoom)
						newRoom = uRoom;
					else
						newRoom = aRoom;
					found = true;
				}
			}
		} else {
			l.room.setArea(cri->area);
			// if misc, first 1000 rooms are off-limits
			l.room.id = mrand(l.room.isArea("misc") ? 1000 : 1, cri->teleportWeight);

			found = canPortHere(loadRoom(l.room, &uRoom), uRoom);
			if(found)
				newRoom = uRoom;
		}

		i++;
	} while(!found);

	if(!newRoom)
		return(abortFindRoom(this, "teleportWhere"));
	return(newRoom);
}


//*********************************************************************
//						canPortHere
//*********************************************************************
// A player, the return value from loadRoom, and a room will determine if the
// player is allowed to teleport to the room we're given.

bool Creature::canPortHere(bool loadRoom, const Room *room) const {
	if(!loadRoom)
		return(false);
	if(room->flagIsSet(R_NO_TELEPORT) ||
		room->flagIsSet(R_CONSTRUCTION) ||
		room->flagIsSet(R_SHOP_STORAGE) ||
		room->flagIsSet(R_JAIL) ||
		room->flagIsSet(R_IS_STORAGE_ROOM) ||
		room->flagIsSet(R_ETHEREAL_PLANE)
	)
		return(false);
	if(size && room->getSize() && size > room->getSize())
		return(false);
	if(room->isFull())
		return(false);
	if(room->deityRestrict(this))
		return(false);
	if(room->getLowLevel() > level)
		return(false);
	if(room->getHighLevel() && level > room->getHighLevel())
		return(false);
	// artificial limits for the misc area
	if(room->info.isArea("misc") && room->info.id <= 1000)
		return(false);
	if(!room->first_ext)
		return(false);

	return(true);
}

//*********************************************************************
//						checkSkillsGain
//*********************************************************************
// setToLevel: Set skill level to player level - 1, otherwise set to whatever the skill gain tells us to

void Creature::checkSkillsGain(std::list<SkillGain*>::const_iterator begin, std::list<SkillGain*>::const_iterator end, bool setToLevel) {
	SkillGain *sGain=0;
	std::list<SkillGain*>::const_iterator sgIt;
	for(sgIt = begin ; sgIt != end ; sgIt++) {
		sGain = (*sgIt);
		if(sGain->getName() == "")
			continue;
		if(!sGain->hasDeities() || sGain->deityIsAllowed(deity)) {
			if(!knowsSkill(sGain->getName())) {
				if(setToLevel)
					addSkill(sGain->getName(), (level-1)*10);
				else
					addSkill(sGain->getName(), sGain->getGained());
				print("You have learned the fine art of '%s'.\n", gConfig->getSkillDisplayName(sGain->getName()).c_str());
			}
		}
	}
}


//*********************************************************************
//						loseRage
//*********************************************************************

void Player::loseRage() {
	if(!flagIsSet(P_BERSERKED))
		return;
	printColor("^rYour rage diminishes.^x\n");
	clearFlag(P_BERSERKED);

	if(cClass == CLERIC && deity == ARES)
		strength.decrease(30);
	else
		strength.decrease(50);
	computeAC();
	computeAttackPower();
}

//*********************************************************************
//						losePray
//*********************************************************************

void Player::losePray() {
	if(!flagIsSet(P_PRAYED))
		return;
	if(cClass != DEATHKNIGHT) {
		printColor("^yYou feel less pious.\n");
		piety.decrease(50);
	} else {
		printColor("^rYour demonic strength leaves you.\n");
		strength.decrease(30);
		computeAC();
		computeAttackPower();
	}
	clearFlag(P_PRAYED);
}

//*********************************************************************
//						loseFrenzy
//*********************************************************************

void Player::loseFrenzy() {
	if(!flagIsSet(P_FRENZY))
		return;
	printColor("^gYou feel slower.\n");
	clearFlag(P_FRENZY);
	dexterity.decrease(50);
}

//*********************************************************************
//						halftolevel
//*********************************************************************
// The following function will return true if a player is more then halfway in experience
// to their next level, causing them to not gain exp until they go level. If they are
// not yet half way, it will return false. Staff and players under level 5 are exempt.

bool Player::halftolevel() const {
	if(isStaff())
		return(false);

	// can save up xp until level 5.
	if(experience <= gConfig->expNeeded(5))
		return(false);

	if(experience >= gConfig->expNeeded(level) + (((unsigned)(gConfig->expNeeded(level+1) - gConfig->expNeeded(level)))/2)) {
		print("You have enough experience to train.\nYou are half way to the following level.\n");
		print("You must go train in order to gain further experience.\n");
		return(true);
	}

	return(false);
}

//*********************************************************************
//						getSock
//*********************************************************************

Socket* Player::getSock() const {
	if(!this)
		return(null);
	return(mySock);
}

//*********************************************************************
//						setSock
//*********************************************************************

void Player::setSock(Socket* pSock) {
	mySock = pSock;
}


//*********************************************************************
//						getVision
//*********************************************************************

int Player::getVision() const {

	if(isStaff())
		return(MAX_VISION);

	int vision = 8;
	if(race == ELF)
		vision++;

	if(race == DARKELF) {
		if(isDay())
			vision--;
		else
			vision++;
	}

	if(isEffected("farsight"))
		vision *= 2;

	return(MIN(MAX_VISION, vision));
}


//*********************************************************************
//						getSneakChance
//*********************************************************************
// determines out percentage chance of being able to sneak

int Player::getSneakChance() const {
	int sLvl = (int)getSkillLevel("sneak");

	if(isStaff())
		return(101);

	// this is the base chance for most classes
	int chance = MIN(70, 5 + 2 * sLvl + 3 * bonus((int) dexterity.getCur()));

	switch(cClass) {
	case THIEF:
		if(cClass2 == MAGE)
			MIN(90, 5 + 8 * MAX(1,sLvl-2) + 3 * bonus((int) dexterity.getCur()));
		else
			chance = MIN(90, 5 + 8 * sLvl + 3 * bonus((int) dexterity.getCur()));

		break;
	case ASSASSIN:
		chance = MIN(90, 5 + 8 * sLvl + 3 * bonus((int) dexterity.getCur()));
		break;
	case CLERIC:
		if(cClass2 == ASSASSIN)
			chance = MIN(90, 5 + 8 * MAX(1,sLvl-2) + 3 * bonus((int) dexterity.getCur()));
		else if(deity == KAMIRA || deity == ARACHNUS)
			chance = MIN(90, 5 + 8 * MAX(1,sLvl-2) + 3 * bonus((int) piety.getCur()));

		break;
	case FIGHTER:
		if(cClass2 == THIEF)
			chance = MIN(90, 5 + 8 * MAX(1,sLvl-2) + 3 * bonus((int) dexterity.getCur()));

		break;
	case MAGE:
		if(cClass2 == THIEF || cClass2 == ASSASSIN)
			chance = MIN(90, 5 + 8 * MAX(1,sLvl-3) + 3 * bonus((int) dexterity.getCur()));

		break;
	case DRUID:
		if(getRoom()->isForest())
			chance = MIN(95 , 5 + 10 * sLvl + 3 * bonus((int) dexterity.getCur()));

		break;
	case RANGER:
		if(getRoom()->isForest())
			chance = MIN(95 , 5 + 10 * sLvl + 3 * bonus((int) dexterity.getCur()));
		else
			chance = MIN(83, 5 + 8 * sLvl + 3 * bonus((int) dexterity.getCur()));
	case ROGUE:
		chance = MIN(85, 5 + 7 * sLvl + 3 * bonus((int) dexterity.getCur()));
		break;
	default:
		break;
	}

	if(isBlind())
		chance = MIN(20, chance);

	if(isEffected("camouflage")) {
		if(getRoom()->isOutdoors())
			chance += 15;
		if(cClass == DRUID && getRoom()->isForest())
			chance += 5;
	}

	return(MIN(99, chance));
}



//*********************************************************************
//						breakObject
//*********************************************************************
// breaks a worn object and readds it to the player's inventory

bool Player::breakObject(Object* object, int loc) {
	if(!object)
		return(false);

	if(object->getShotscur() < 1) {
		printColor("Your %s is broken.\n", object->name);
		broadcast(getSock(), getRoom(), "%M broke %s %s.", this, hisHer(), object->name);

		if(object->compass) {
			delete object->compass;
			object->compass = 0;
		}

		object->clearFlag(O_WORN);
		object->parent_crt = this;
		Limited::remove(this, object);

		if(object->getAdjustment())
			object->setAdjustment(0);
		if(object->flagIsSet(O_TEMP_ENCHANT)) {
			object->damage.setPlus(0);
			object->clearFlag(O_TEMP_ENCHANT);
			object->clearFlag(O_RANDOM_ENCHANT);
			if(isEffected("detect-magic"))
				print("The enchantment on your %s fades.\n", object);
		}
		if(object->flagIsSet(O_ENVENOMED)) {
			object->clearFlag(O_ENVENOMED);
			object->clearEffect();
			print("The poison on your %s becomes useless.\n", object);
		}

		if(loc != -1) {
			unequip(loc);
		} else {
			broadcast(::isDm, "^g>>> BreakObject: BadLoc (Loc:%d) %'s %s", loc, name, object->name);
			if(ready[WIELD-1] == object) {
				unequip(WIELD);
			} else if(ready[HELD-1] == object) {
				unequip(HELD);
			}
		}
		return(true);
	}
	return(false);
}


//********************************************************************
//				getWhoString
//********************************************************************

bstring Player::getWhoString(bool whois, bool color, bool ignoreIllusion) const {
	std::ostringstream whoStr;

	if(whois) {
		whoStr << "^bWhois for [" << name << "]\n";
		whoStr << "----------------------------------------------------------------------------------\n";
	}

	whoStr << (color ? "^x[^c" : "[") << std::setw(2) << level
		   << ":" << std::setw(4) << bstring(getShortClassName(this)).left(4)
		   << (color ? "^x] " : "] ");

	if(isHardcore())
		whoStr << (color ? "^y" : "") << "H ";
	else if(flagIsSet(P_OUTLAW))
		whoStr << (color ? "^r" : "") << "O ";
	else if( (flagIsSet(P_NO_PKILL) || flagIsSet(P_DIED_IN_DUEL) ||
			getRoom()->flagIsSet(R_SAFE_ROOM)) &&
			(flagIsSet(P_CHAOTIC) || clan || cClass == CLERIC) )
		whoStr << (color ? "^y" : "") << "N ";
	else if(flagIsSet(P_CHAOTIC)) // Chaotic
		whoStr << (color ? "^y" : "") << "C ";
	else // Lawful
		whoStr << (color ? "^y" : "") << "L ";

	if(color)
		whoStr << (isPublicWatcher() ? "^w" : "^g");

	whoStr << fullName() << " the ";
	if(whois)
		whoStr << getSexName(getSex()) << " ";

	whoStr << gConfig->getRace(getDisplayRace())->getAdjective();
	// will they see through the illusion?
	if(ignoreIllusion && getDisplayRace() != getRace())
		whoStr << " (" << gConfig->getRace(getRace())->getAdjective() << ")";
	whoStr << " " << getTitle();
	if(whois)
		whoStr << " (Age:" << getAge() << ")";


	if(	flagIsSet(P_DM_INVIS) ||
		flagIsSet(P_INCOGNITO) ||
		isInvisible() ||
		flagIsSet(P_MISTED) ||
		(flagIsSet(P_LINKDEAD) && !isPublicWatcher())
	) {
		if(color)
			whoStr << " ^w";
		if(flagIsSet(P_DM_INVIS))
			whoStr << "[+]";
		if(flagIsSet(P_INCOGNITO) )
			whoStr << "[g]";
		if(isInvisible() )
			whoStr << "[*]";
		if(flagIsSet(P_MISTED) )
			whoStr << "[m]";
		if(flagIsSet(P_LINKDEAD) && !isPublicWatcher() )
			whoStr << "[l]";
	}


	if(flagIsSet(P_AFK))
		whoStr << (color ? "^R" : "") << " [AFK]";

	if(deity) {
		whoStr << (color ? "^r" : "") << " (" << gConfig->getDeity(deity)->getName() << ")";
	} else if(clan) {
		whoStr << (color ? "^r" : "") << " (" << gConfig->getClan(clan)->getName() << ")";
	}

	if(guild && guildRank >= GUILD_PEON) {
		whoStr << (color ? "^y" : "") << " [" << getGuildName(guild) << "]";
	}

	whoStr << (color ? "^x" : "") << "\n";
	return(whoStr.str());
}


//********************************************************************
//				titlePunctuation
//********************************************************************

bool titlePunctuation(char c) {
	switch(c) {
	case ' ':
	case '/':
	case '\'':
	case '-':
		return(true);
	default:
		return(false);
	}
}

//********************************************************************
//				cmdTitle
//********************************************************************

int cmdTitle(Player* player, cmd* cmnd) {
	double	punctuation=0;
	bstring title = getFullstrText(cmnd->fullstr, 1);

	if(!player->flagIsSet(P_CAN_CHOOSE_CUSTOM_TITLE)) {
		player->print("You cannot currently choose a custom title.\n");
		return(0);
	}
	if(cmnd->num < 2) {
		player->print("Please enter a title.\n");
		return(0);
	}

	for(size_t i = 0; i<title.length(); i++) {
		if(!titlePunctuation(title[i]) && !isalpha(title[i])) {
			player->print("Sorry, ""%c"" cannot be used in your title.\n", title[i]);
			return(0);
		}
		if(titlePunctuation(title[i]))
			punctuation++;
	}

	if(title.length() < 4) {
		player->print("Title must be atleast 4 characters.\n");
		return(0);
	}

	punctuation = punctuation / (float)title.length();
	if(punctuation > 0.25) {
		player->print("This title has too much punctuation.\n");
		return(0);
	}

	player->print("Your new title: %s\n", title.c_str());
	player->print("Is this acceptable? (Y/N)\n");

	player->setTempTitle(title);
	player->getSock()->setState(CON_CONFIRM_TITLE);
	return(0);
}

//*********************************************************************
//						doTitle
//*********************************************************************

void doTitle(Socket* sock, char *str) {
	Player* player = sock->getPlayer();

	if(low(str[0]) == 'y') {
		player->print("You are now known as %s the %s.\n", player->name, player->getTempTitle().c_str());
		if(!player->isStaff())
			broadcast("^y%s the %s is now known as %s the %s.", player->name,
					player->getTitle().c_str(), player->name, player->getTempTitle().c_str());

		player->setTitle(player->getTempTitle());
		player->clearFlag(P_CAN_CHOOSE_CUSTOM_TITLE);
	} else
		player->print("Aborted.\n");

	player->setTempTitle("");
	sock->setState(CON_PLAYING);
}


//*********************************************************************
//						getBound
//*********************************************************************

Location Player::getBound() {
	if(bound != getLimboRoom() && bound != getRecallRoom())
		return(bound);
	else {
		// we hardcode highport in case of failure
		const StartLoc* location = gConfig->getStartLoc("highport");
		bind(location);
		if(location)
			return(location->getBind());
	}
	// too many errors to handle!
	Location cr;
	cr.room.id = ROOM_BOUND_FAILURE;
	return(cr);
}


//*********************************************************************
//						expToLevel
//*********************************************************************

unsigned long Player::expToLevel() const {
	return(experience > gConfig->expNeeded(level) ? 0 : gConfig->expNeeded(level) - experience);
}

bstring Player::expToLevel(bool addX) const {
	if(level < MAXALVL) {
		std::ostringstream oStr;
		oStr.imbue(std::locale(isStaff() ? "C" : ""));
		oStr << expToLevel();
		if(addX)
			oStr << "X";
		return(oStr.str());
	}
	return("infinity");
}

//*********************************************************************
//						exists
//*********************************************************************

bool Player::exists(bstring name) {
	char	file[80];
	sprintf(file, "%s/%s.xml", PLAYERPATH, name.c_str());
	return(file_exists(file));
}

//*********************************************************************
//						inList functions
//*********************************************************************

bool Player::inList(const std::list<bstring>* list, bstring name) const {
	std::list<bstring>::const_iterator it;

	for(it = list->begin(); it != list->end() ; it++) {
		if((*it) == name)
			return(true);
	}
	return(false);
}


bool Player::isIgnoring(bstring name) const {
	return(inList(&ignoring, name));
}
bool Player::isGagging(bstring name) const {
	return(inList(&gagging, name));
}
bool Player::isRefusing(bstring name) const {
	return(inList(&refusing, name));
}
bool Player::isDueling(bstring name) const {
	return(inList(&dueling, name));
}
bool Player::isWatching(bstring name) const {
	return(inList(&watching, name));
}

//*********************************************************************
//						showList
//*********************************************************************

bstring Player::showList(const std::list<bstring>* list) const {
	std::ostringstream oStr;
	std::list<bstring>::const_iterator it;
	bool initial=false;

	for(it = list->begin(); it != list->end() ; it++) {
		if(initial)
			oStr << ", ";
		initial = true;

		oStr << (*it);
	}

	if(!initial)
		oStr << "No one";
	oStr << ".";

	return(oStr.str());
}


bstring Player::showIgnoring() const {
	return(showList(&ignoring));
}
bstring Player::showGagging() const {
	return(showList(&gagging));
}
bstring Player::showRefusing() const {
	return(showList(&refusing));
}
bstring Player::showDueling() const {
	return(showList(&dueling));
}
bstring Player::showWatching() const {
	return(showList(&watching));
}

//*********************************************************************
//						addList functions
//*********************************************************************

void Player::addList(std::list<bstring>* list, bstring name) {
	list->push_back(name);
}


void Player::addIgnoring(bstring name) {
	addList(&ignoring, name);
}
void Player::addGagging(bstring name) {
	addList(&gagging, name);
}
void Player::addRefusing(bstring name) {
	addList(&refusing, name);
}
void Player::addDueling(bstring name) {
	delList(&maybeDueling, name);

	// if they aren't dueling us, add us to their maybe dueling list
	Player* player = gServer->findPlayer(name);
	if(player && !player->isDueling(name))
		player->addMaybeDueling(this->name);

	addList(&dueling, name);
}
void Player::addMaybeDueling(bstring name) {
	addList(&maybeDueling, name);
}
void Player::addWatching(bstring name) {
	addList(&watching, name);
}

//*********************************************************************
//						delList functions
//*********************************************************************

void Player::delList(std::list<bstring>* list, bstring name) {
	std::list<bstring>::iterator it;

	for(it = list->begin(); it != list->end() ; it++) {
		if((*it) == name) {
			list->erase(it);
			return;
		}
	}
}


void Player::delIgnoring(bstring name) {
	delList(&ignoring, name);
}
void Player::delGagging(bstring name) {
	delList(&gagging, name);
}
void Player::delRefusing(bstring name) {
	delList(&refusing, name);
}
void Player::delDueling(bstring name) {
	delList(&dueling, name);
}
void Player::delWatching(bstring name) {
	delList(&watching, name);
}

//*********************************************************************
//						clear list functions
//*********************************************************************

void Player::clearIgnoring() {
	ignoring.clear();
}
void Player::clearGagging() {
	gagging.clear();
}
void Player::clearRefusing() {
	refusing.clear();
}
void Player::clearDueling() {
	dueling.clear();
}
void Player::clearMaybeDueling() {
	std::list<bstring>::iterator it;

	Player* player=0;
	for(it = maybeDueling.begin(); it != maybeDueling.end() ; it++) {
		player = gServer->findPlayer(*it);
		if(!player)
			continue;
		player->delDueling(name);
	}

	maybeDueling.clear();
}
void Player::clearWatching() {
	watching.clear();
}

//*********************************************************************
//						renamePlayerFiles
//*********************************************************************

void renamePlayerFiles(char *old_name, char *new_name) {
	char	file[80], file2[80];

	sprintf(file, "%s/%s.xml", PLAYERPATH, old_name);
	unlink(file);

	sprintf(file, "%s/%s.txt", POSTPATH, old_name);
	sprintf(file2, "%s/%s.txt", POSTPATH, new_name);
	rename(file, file2);

	sprintf(file, "%s/%s.txt", HISTPATH, old_name);
	sprintf(file2, "%s/%s.txt", HISTPATH, new_name);
	rename(file, file2);

	sprintf(file, "%s/%s.txt", BANKDIR, old_name);
	sprintf(file2, "%s/%s.txt", BANKDIR, new_name);
	rename(file, file2);
}

//*********************************************************************
//						checkHeavyRestrict
//*********************************************************************

bool Player::checkHeavyRestrict(const bstring& skill) const {
	// If we aren't one of the classes that can use heavy armor, but with restrictions
	// immediately return false
	if(! ((getClass() == FIGHTER && getSecondClass() == THIEF) ||
	      (getClass() == RANGER)) )
	      return(false);

	// Allows us to do a blank check and see if the player's class is one of the heavy armor
	// restricted classes.
	if(skill == "") 
                return(true);


        bool mediumOK = (getClass() == RANGER);

	for(int i = 0 ; i < MAXWEAR ; i++) {
		if(ready[i] && (ready[i]->isHeavyArmor() || (!mediumOK && ready[i]->isMediumArmor())) ) {
		        printColor("You can't ^W%s^x while wearing heavy armor!\n", skill.c_str());
			printColor("^W%O^x would hinder your movement too much!\n", ready[i]);
			return(true);
		}
	}
	return(false);
}