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/
/*
 * creature.cpp
 *   Routines that act on creatures
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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
 *
 */

#include <fcntl.h>

#include "mud.h"
#include "commands.h"
#include "effects.h"
#include "calendar.h"

//#include <time.h>
#include "move.h"

const short Creature::OFFGUARD_REMOVE=0;
const short Creature::OFFGUARD_NOREMOVE=1;
const short Creature::OFFGUARD_NOPRINT=2;

//*********************************************************************
//						sameRoom
//*********************************************************************

bool Creature::inSameRoom(const Creature *b) const {
	return(getRoom() == b->getRoom());
}

//*********************************************************************
//						find_exact_crt
//*********************************************************************
// This function will attempt to locate a given creature within a given
// list.  The first parameter contains a pointer to the creature doing
// the search, the second contains a tag pointer to the first tag in
// the list.  The third contains the match string, and the fourth
// contains the number of times to match the string.

Creature *find_exact_crt(Creature* player, ctag *first_ct, char *str, int val) {
	Creature* target=0;
	ctag	*cp=0;
	int		match=0;

	ASSERTLOG( player );
	ASSERTLOG( str );

	if(!player || !str || !first_ct)
		return(0);

	cp = first_ct;
	while(cp) {
		target = cp->crt;
		cp = cp->next_tag;

		if(!target)
			continue;
		if(!player->canSee(target))
			continue;

		if(!strcmp(target->name, str)) {
			match++;
			if(match == val)
				return(target);
		}
	}
	return(0);
}

//*********************************************************************
//						getFirstAggro
//*********************************************************************

Creature *getFirstAggro(Creature* creature, Creature* player) {
	ctag		*cp=0;
	Creature *foundCrt=0;

	if(creature->isPlayer())
		return(creature);

	cp = creature->getRoom()->first_mon;
	while(cp) {
		if(strcmp(cp->crt->name, creature->name)) {
			cp = cp->next_tag;
			continue;
		}
		if(cp->crt->getMonster()->isEnmCrt(player->name)) {
			foundCrt = cp->crt;
			break;
		}

		cp = cp->next_tag;
	}

	if(foundCrt)
		return(foundCrt);
	else
		return(creature);
}

//*********************************************************************
//						addEnmName
//*********************************************************************
// This function adds a new enemy's name to the front a creature's enemy
// list.  The closer to the front of the list, the higher the likelihood
// that you will be attacked.

int Monster::addEnmName(const char *enemy) {
	etag	*ep=0;
	int		n = -1;

	ep = first_enm;
	while(ep) {
		if(!strcmp(ep->enemy, enemy)) {
			n = ep->damage;
			//			strncpy(owner, ep->owner, 79);
			delEnmCrt(enemy);
			break;

		}
		ep = ep->next_tag;
	}
	ep = new etag;
	if(!ep)
		merror("add_enm_name", FATAL);
	strncpy(ep->enemy, enemy, 79);
	ep->next_tag = 0;
	ep->damage = (n > -1) ? n : 0;


	if(!first_enm) {
		first_enm = ep;
		return (n);
	}

	ep->next_tag = first_enm;
	first_enm = ep;

	if(n < 0)
		NUMHITS = 0;

	return (n);
}

//*********************************************************************
//						addEnmCrt
//*********************************************************************
// This function adds a new enemy's name to the front a creature's enemy
// list.  The closer to the front of the list, the higher the likelihood
// that you will be attacked.

int Monster::addEnmCrt(Creature *target) {
	etag	*ep=0;
	int		n = -1;

	ASSERTLOG( target );

	ep = first_enm;


	if(target->isPlayer()) {
		// take pity on these people
		if(target->isEffected("petrification") || target->isUnconscious())
			return(0);
		// pets should not attack master
		if(isPet() && this == target->getPlayer()->getPet())
			return(0);
	}

	while(ep) {
		if(!strcmp(ep->enemy, target->name)) {
			n = ep->damage;
			// strncpy(owner, ep->owner, 79);
			delEnmCrt(target->name);
			break;

		}
		ep = ep->next_tag;
	}

	ep = new etag;
	if(!ep)
		merror("add_enm_crt", FATAL);

	strncpy(ep->enemy, target->name, 79);
	ep->next_tag = 0;
	ep->damage = (n > -1) ? n : 0;

	if(target->isMonster() && target->isPet())
		strncpy(ep->owner, target->following->name, 79);
	else
		ep->owner[0] = 0;

	if(!first_enm) {
		first_enm = ep;
		return(n);
	}

	ep->next_tag = first_enm;
	first_enm = ep;

	if(n < 0)
		NUMHITS = 0;

	return(n);
}

//*********************************************************************
//						delEnmCrt
//*********************************************************************
// This function removes an enemy's name from their enemy list.

int Monster::delEnmCrt(const char *enemy, const char *owner) {
	etag	*ep=0, *prev=0;
	int		dmg=0;

	ASSERTLOG( enemy );

	ep = first_enm;
	if(!ep)
		return(-1);

	if(!strcmp(ep->enemy, enemy)) {
		if(!owner || !strcmp(ep->owner, owner)) {
			first_enm = ep->next_tag;
			dmg = ep->damage;
			delete ep;
			return(dmg);
		}
	}

	while(ep) {
		if(!strcmp(ep->enemy, enemy)) {
			if(!owner || !strcmp(ep->owner, owner)) {
				prev->next_tag = ep->next_tag;
				dmg = ep->damage;
				delete ep;
				return(dmg);
			}
		}
		prev = ep;
		ep = ep->next_tag;
	}

	return(-1);
}

//*********************************************************************
//						endEnmCrt
//*********************************************************************
// This function moves an enemy within a monster's enemy list to the
// very end of the list.

void Monster::endEnmCrt(const char *enemy) {
	etag    *ep=0, *move=0, *prev=0;

	ASSERTLOG( enemy );

	move = 0;
	move = new etag;
	if(!move)
		merror("end_enm_crt", FATAL);

	strcpy(move->enemy, enemy);

	ep = first_enm;
	if(ep) {
		if(!strcmp(ep->enemy, enemy)) {
			first_enm = ep->next_tag;
			move->damage = ep->damage;
			strcpy(move->owner, ep->owner);
			delete ep;
			ep = NULL;
		}

		while(ep) {
			if(!strcmp(ep->enemy, enemy)) {
				prev->next_tag = ep->next_tag;
				move->damage = ep->damage;
				strcpy(move->owner, ep->owner);
				delete ep;
				ep = NULL;
				break;
			}
			prev = ep;
			ep = ep->next_tag;
		}
	}


	move->next_tag = 0;

	ep = first_enm;
	if(!ep) {
		first_enm = move;
		return;
	}

	while(ep->next_tag)
		ep = ep->next_tag;

	ep->next_tag = move;

}

//*********************************************************************
//						addEnmDmg
//*********************************************************************
// This function adds the amount of damage indicated by the third
// argument to the enemy total for the creature pointed to by the
// second argument.  The first argument contains the name of the player
// who hit the creature.

void Monster::addEnmDmg(const Creature *enemy, int dmg) {
	ASSERTLOG( enemy );

	etag *ep = first_enm;
	while(ep) {
		if(!strcmp(enemy->name, ep->enemy)) {
			// If pet, make sure owners match
			if( (enemy->isPet() && !strcmp(enemy->following->name, ep->owner)) || !enemy->isPet() )
				ep->damage += dmg;
		}
		ep = ep->next_tag;
	}
}

//*********************************************************************
//						isEnmCrt
//*********************************************************************
// This function returns true if the name passed in the first parameter
// is in the enemy list of the creature pointed to by the second.

bool Monster::isEnmCrt(const char *enemy) const {
	etag    *ep=0;

	ASSERTLOG( enemy );

	ep = first_enm;

	while(ep) {
		if(!strcmp(ep->enemy, enemy))
			return(true);
		ep = ep->next_tag;
	}

	return(false);
}

//*********************************************************************
//						same_enemies
//*********************************************************************

int same_enemies(Creature* creature, Creature *victim) {
	etag		*cep=0, *vep=0;
	char		enm1[1024], enm2[1024];


	strcpy(enm1, "");
	strcpy(enm2, "");

	cep = creature->first_enm;
	while(cep) {
		if(!cep->enemy) {
			cep = cep->next_tag;
			continue;
		}
		strcat(cep->enemy, enm1);

		cep = cep->next_tag;
	}

	vep = victim->first_enm;
	while(vep) {
		if(!vep->enemy) {
			vep = vep->next_tag;
			continue;
		}
		strcat(vep->enemy, enm2);

		vep = vep->next_tag;
	}

	if(!strcmp(enm1, enm2))
		return(1);
	else
		return(0);
}

//*********************************************************************
//						nearEnemy
//*********************************************************************

bool Monster::nearEnemy() const {
	ctag	*cp=0;

	if(nearEnmPly())
		return(true);

	cp = getRoom()->first_mon;
	while(cp) {
		if(isEnmCrt(cp->crt->name) && !cp->crt->flagIsSet(M_FAST_WANDER))
			return(true);
		cp = cp->next_tag;
	}

	return(false);
}

//*********************************************************************
//						nearEnmPly
//*********************************************************************

bool Monster::nearEnmPly() const {
	ctag	*cp = getRoom()->first_ply;

	while(cp) {
		if(isEnmCrt(cp->crt->name) && !cp->crt->flagIsSet(P_DM_INVIS))
			return(true);
		cp = cp->next_tag;
	}

	return(false);
}

//*********************************************************************
//						nearEnemy
//*********************************************************************
// Searches for a near enemy, excluding the current player being attacked.

bool Monster::nearEnemy(const Creature* target) const {
	ctag	*cp=0;
	BaseRoom* room = getRoom();

	cp = room->first_ply;
	while(cp) {
		if(isEnmCrt(cp->crt->name) && !cp->crt->flagIsSet(P_DM_INVIS)
		        && strcmp(target->name, cp->crt->name))
			return(true);
		cp = cp->next_tag;
	}

	cp = room->first_mon;
	while(cp) {
		if(isEnmCrt(cp->crt->name) && !cp->crt->flagIsSet(M_FAST_WANDER))
			return(true);
		cp = cp->next_tag;
	}

	return(false);
}

//*********************************************************************
//						findCloseEnm
//*********************************************************************

Creature* Monster::findCloseEnm() {
	BaseRoom* room=0;
	Creature *attacker=0;
	etag	*ep=0;
	char	*enemy=0, *vacant_enemy=0, *gone_enemy=0;

	room = getRoom();
	ep = first_enm;
	while(ep) {
		while(1) {
			enemy = ep->enemy;
			if(!enemy)
				ep = ep->next_tag;
			if(!ep)
				return NULL;
			if(enemy)
				break;
		}
		attacker = room->findCreature(this, enemy, 1);
		if(attacker) {
			if(vacant_enemy)
				endEnmCrt(vacant_enemy);
			if(gone_enemy)
				endEnmCrt(gone_enemy);


			// usta be del_enm
			return(attacker);
		}


		if(!gServer->findPlayer(enemy))
			gone_enemy = enemy;
		else
			vacant_enemy = enemy;
		ep = ep->next_tag;
	}

	return(0);
}

//*********************************************************************
//						tempPerm
//*********************************************************************

void Object::tempPerm() {
	otag	*op=0;

	setFlag(O_PERM_INV_ITEM);
	setFlag(O_TEMP_PERM);
	op = first_obj;
	while(op) {
		op->obj->tempPerm();
		op = op->next_tag;
	}
}

//*********************************************************************
//						diePermCrt
//*********************************************************************
// This function is called whenever a permanent monster is killed. The
// room the monster was in has its permanent monster list checked to
// if the monster was loaded from that room. If it was, then the last-
// time field for that permanent monster is updated.

void Monster::diePermCrt() {
	std::map<int, crlasttime>::iterator it;
	crlasttime* crtm=0;
	Monster *temp_mob=0;
	Room	*room=0;
	char	perm[80];
	long	t = time(0);
	int		i=0;

	strcpy(perm,name);

	if(!parent_rom)
		return;
	room = parent_rom;

	for(it = room->permMonsters.begin(); it != room->permMonsters.end() ; it++) {
		crtm = &(*it).second;
		if(!crtm->cr.id)
			continue;
		if(crtm->ltime + crtm->interval > t)
			continue;
		if(!loadMonster(crtm->cr, &temp_mob))
			continue;
		if(!strcmp(temp_mob->name, name)) {
			crtm->ltime = t;
			free_crt(temp_mob);
			break;
		}
		free_crt(temp_mob);
	}

	if(flagIsSet(M_DEATH_SCENE) && !flagIsSet(M_FOLLOW_ATTACKER)) {
		int     fd,n;
		char    tmp[2048], file[80],pName[80];

		strcpy(pName, name);
		for(i=0; pName[i]; i++)
			if(pName[i] == ' ')
				pName[i] = '_';

		sprintf(file,"%s/%s_%d", DDESCPATH, pName, level);
		fd = open(file,O_RDONLY,0);
		if(fd) {
			n = read(fd,tmp,2048);
			tmp[n] = 0;
			broadcast(NULL, getRoom(), "\n%s", tmp);
		}
		close(fd);
	}
}

//*********************************************************************
//						consider
//*********************************************************************
// This function allows the player pointed to by the first argument to
// consider how difficult it would be to kill the creature in the
// second parameter.

bstring Player::consider(Creature* creature) const {
	int		diff=0;
	bstring str = "";

	// staff always needle
	if(creature->isStaff() && !isStaff())
		diff = -4;
	else if(isStaff() && !creature->isStaff())
		diff = 4;
	else {
		diff = level - creature->getLevel();
		diff = MAX(-4, MIN(4, diff));
	}

	// upHeShe is used most often
	str = creature->upHeShe();

	switch(diff) {
	case 0:
		str += " is a perfect match for you!\n";
		break;
	case 1:
		str += " is not quite as good as you.\n";
		break;
	case -1:
		str += " is a little better than you.\n";
		break;
	case 2:
		str += " shouldn't be too tough to kill.\n";
		break;
	case -2:
		str += " might be tough to kill.\n";
		break;
	case 3:
		str += " should be easy to kill.\n";
		break;
	case -3:
		str += " should be really hard to kill.\n";
		break;
	case 4:
		str = "You could kill ";
		str += creature->himHer();
		str += " with a needle.\n";
		break;
	case -4:
		str += " could kill you with a needle.\n";
		break;
	}
	return(str);
}

//********************************************************************
//						hasCharm
//********************************************************************
// This function returns true if the name passed in the first parameter
// is in the charm list of the creature

bool Creature::hasCharm(bstring charmed) {
	etag    *cp=0;

	if(!this || charmed == "")
		return(false);

	Player* player = getPlayer();

	if(!player)
		return(false);

	cp = player->first_charm;

	while(cp) {
		if(!strcmp(cp->enemy, charmed.c_str()))
			return(true);
		cp = cp->next_tag;
	}

	return(false);
}

//********************************************************************
//						addCharm
//********************************************************************
// This function adds the creature pointed to by the first
// parameter to the charm list of the creature

int Player::addCharm(Creature* creature) {
	etag    *cp=0;

	ASSERTLOG( creature );

	if(hasCharm(creature->name))
		return(0);

	if(creature && !creature->isDm()) {
		cp = new etag;
		if(!cp)
			merror("add_charm_crt", FATAL);

		strcpy(cp->enemy, creature->name);
		cp->next_tag = first_charm;
		first_charm = cp;
	}
	return(0);
}

//********************************************************************
//						delCharm
//********************************************************************
// This function deletes the creature pointed to by the first
// parameter from the charm list of the creature

int Player::delCharm(Creature* creature) {
	etag    *cp, *prev;

	ASSERTLOG( creature );

	if(!hasCharm(creature->name))
		return(0);

	cp = first_charm;

	if(!strcmp(cp->enemy, creature->name)) {
		first_charm = cp->next_tag;
		delete cp;
		return(1);
	} else
		while(cp) {
			if(!strcmp(cp->enemy, creature->name)) {
				prev->next_tag = cp->next_tag;
				delete cp;
				return(1);
			}
			prev = cp;
			cp = cp->next_tag;
		}
	return(0);
}

//*********************************************************************
//						mobileEnter
//*********************************************************************

bool mobileEnter(Exit* exit) {
	return( !exit->flagIsSet(X_SECRET) &&
		!exit->flagIsSet(X_NO_SEE) &&
		!exit->flagIsSet(X_LOCKED) &&
		!exit->flagIsSet(X_PASSIVE_GUARD) &&
		!exit->flagIsSet(X_NO_WANDER) &&
		!exit->isConcealed() &&
		!exit->flagIsSet(X_DESCRIPTION_ONLY) &&
		!exit->isEffected("wall-of-fire") &&
		!exit->isEffected("wall-of-force") &&
		!exit->isEffected("wall-of-thorns")
	);
}

//*********************************************************************
//						mobile_crt
//*********************************************************************
// This function provides for self-moving monsters. If
// the M_MOBILE_MONSTER flag is set the creature will move around.

int Monster::mobileCrt() {
	BaseRoom *newRoom=0;
	Room	*uRoom=0;
	AreaRoom* aRoom=0, *caRoom = area_room;
	xtag	*xp=0;
	int		i=0, num=0, ret=0;
	bool	mem=false;


	if(	!flagIsSet(M_MOBILE_MONSTER) ||
		flagIsSet(M_PERMENANT_MONSTER) ||
		flagIsSet(M_PASSIVE_EXIT_GUARD)
	)
		return(0);

	if(nearEnemy())
		return(0);

	xp = getRoom()->first_ext;
	while(xp) {
		// count up exits
		if(mobileEnter(xp->ext))
			i += 1;
		xp = xp->next_tag;
	}

	if(!i)
		return(0);

	num = mrand(1, i);
	i = 0;

	xp = getRoom()->first_ext;
	while(xp) {
		if(mobileEnter(xp->ext))
			i += 1;

		if(i == num) {

			// get us out of this room
			if(!Move::getRoom(this, xp->ext, &uRoom, &aRoom))
				return(0);
			if(aRoom) {
				mem = aRoom->getStayInMemory();
				aRoom->setStayInMemory(true);
				newRoom = aRoom;
			} else
				newRoom = uRoom;

			// make sure there are no problems with the new room
			if(!newRoom)
				return(0);
			if(newRoom->countCrt() >= newRoom->getMaxMobs())
				return(0);


			if(xp->ext->flagIsSet(X_CLOSED) && !xp->ext->flagIsSet(X_LOCKED)) {
				broadcast(NULL, getRoom(), "%M just opened the %s.", this, xp->ext->name);
				xp->ext->clearFlag(X_CLOSED);
			}

			if(flagIsSet(M_WILL_SNEAK) && flagIsSet(M_HIDDEN))
				setFlag(M_SNEAKING);

			if(	flagIsSet(M_SNEAKING) &&
				mrand (1,100) <= (3+dexterity.getCur())*3)
			{
				broadcast(::isStaff, getSock(), getRoom(), "*DM* %M just snuck to the %s.", this,xp->ext->name);
			} else {
				if(flagIsSet(M_CHASING_SOMEONE) && (first_enm))
					broadcast(NULL, getRoom(), "%M %s to the %s, looking for %s.",
						this, Move::getString(this).c_str(), xp->ext->name, first_enm->enemy);
				else
					broadcast(NULL, getRoom(), "%M just %s to the %s.",
						this, Move::getString(this).c_str(), xp->ext->name);

				clearFlag(M_SNEAKING);
			}


			// see if we can recycle this room
			deleteFromRoom();
			if(caRoom && aRoom == caRoom) {
				aRoom = Move::recycle(aRoom, xp->ext);
				newRoom = aRoom;
			}
			addToRoom(newRoom);

			// reset stayInMemory
			if(aRoom)
				aRoom->setStayInMemory(mem);

			lasttime[LT_MON_WANDER].ltime = time(0);
			lasttime[LT_MON_WANDER].interval = mrand(5,60);

			ret = 1;
			break;
		}
		xp = xp->next_tag;
	}

	if(mrand(1,100) > 80)
		clearFlag(M_MOBILE_MONSTER);

	return(ret);
}

//*********************************************************************
//						monsterCombat
//*********************************************************************
// This function should make monsters attack each other

void Monster::monsterCombat(Monster *target) {
	broadcast(NULL, getRoom(), "%M attacks %N.\n", this, target);

	addEnmCrt(target);
}

//*********************************************************************
//						mobWield
//*********************************************************************

int Monster::mobWield() {
	otag	       *op;
	Object         *object;
	int			i=0, found=0;

	if(!first_obj)
		return(0);

	if(ready[WIELD - 1])
		return(0);

	op = first_obj;
	while(op) {
		for(i=0;i<10;i++) {
			if(carry[i].info == op->obj->info) {
				found=1;
				break;
			}
		}

		if(!found) {
			op = op->next_tag;
			continue;
		}

		if(op->obj->getWearflag() == WIELD) {
			if((op->obj->damage.getNumber() + op->obj->damage.getSides() + op->obj->damage.getPlus()) <
			        (damage.getNumber() + damage.getSides() + damage.getPlus())/2 ||
			        (op->obj->getShotscur() < 1)) {
				op = op->next_tag;
				continue;
			}

			object = op->obj;
			break;
		}
		op = op->next_tag;
	}
	if(!op)
		return(0);
	if(!object)
		return(0);

	equip(object, WIELD);
	return(1);
}

//*********************************************************************
//						getMobSave
//*********************************************************************

void Monster::getMobSave() {
	int		lvl=0, cls=0, index=0;

	if(cClass == 0)
		cls = 4;	// All mobs default save as fighters.
	else
		cls = cClass;

	if((saves[POI].chance +
		 saves[DEA].chance +
		 saves[BRE].chance +
		 saves[MEN].chance +
		 saves[SPL].chance) == 0)
	{


		saves[POI].chance = 5;
		saves[DEA].chance = 5;
		saves[BRE].chance = 5;
		saves[MEN].chance = 5;
		saves[SPL].chance = 5;

		if(race) {
			saves[POI].chance += gConfig->getRace(race)->getSave(POI);
			saves[DEA].chance += gConfig->getRace(race)->getSave(DEA);
			saves[BRE].chance += gConfig->getRace(race)->getSave(BRE);
			saves[MEN].chance += gConfig->getRace(race)->getSave(MEN);
			saves[SPL].chance += gConfig->getRace(race)->getSave(SPL);
		}

		if(level > 1) {
			for(lvl = 2;  lvl <= level; lvl++) {
				index = (lvl - 2) % 10;
				switch (saving_throw_cycle[cls][index]) {
				case POI:
					saves[POI].chance += 6;
					break;
				case DEA:
					saves[DEA].chance += 6;
					break;
				case BRE:
					saves[BRE].chance += 6;
					break;
				case MEN:
					saves[MEN].chance += 6;
					break;
				case SPL:
					saves[SPL].chance += 6;
					break;
				}

			}
		}

		saves[LCK].chance = ((saves[POI].chance +
		                              saves[DEA].chance +
		                              saves[BRE].chance +
		                              saves[MEN].chance +
		                              saves[SPL].chance)/5);
	}
}

//*********************************************************************
//						castDelay
//*********************************************************************

void Creature::castDelay(long delay) {
	if(delay < 1)
		return;

	lasttime[LT_SPELL].ltime = time(0);
	lasttime[LT_SPELL].interval = delay;
}

//*********************************************************************
//						attackDelay
//*********************************************************************

void Creature::attackDelay(long delay) {
	if(delay < 1)
		return;

	getPlayer()->updateAttackTimer(true, delay*10);
}

//*********************************************************************
//						enm_in_group
//*********************************************************************

Creature *enm_in_group(Creature *target) {
	Creature *leader=0, *enemy=0;
	ctag	*cp=0;
	int		group_count=0, chosen=0, num=0;


	if(!target || mrand(1,100) <= 50)
		return(target);

	if(target->following && !target->following->flagIsSet(P_DM_INVIS))
		leader = target->following;
	else
		leader = target;

	cp = leader->first_fol;
	while(cp) {
		if((cp->crt->flagIsSet(P_DM_INVIS) && cp->crt->isPlayer()) ||
		        !cp->crt->inSameRoom(target)) {
			cp = cp->next_tag;
			continue;
		}

		group_count++;

		cp = cp->next_tag;
	}

	if(group_count <= 1)
		return(leader);
	else
		chosen = mrand(1,group_count);

	cp = leader->first_fol;
	while(cp) {
		if((cp->crt->flagIsSet(P_DM_INVIS) && cp->crt->isPlayer()) ||
				!cp->crt->inSameRoom(target)) {
			cp = cp->next_tag;
			continue;
		}
		num++;
		if(num == chosen) {
			enemy = cp->crt;
			break;
		}

		cp = cp->next_tag;
	}

	if(!enemy)
		enemy = target;
	return(enemy);
}

//*********************************************************************
//						clearEnemyList
//*********************************************************************

void Monster::clearEnemyList() {
	etag	*ep = first_enm, *prev = 0;

	while(ep) {
		prev = ep;
		ep = ep->next_tag;

		if(!delEnmCrt(prev->enemy))
			continue;
	}
}

//*********************************************************************
//						clearMobInventory
//*********************************************************************

void Monster::clearMobInventory() {
	otag *op = first_obj;
	Object* object=0;

	while(op) {
		object = op->obj;
		op = op->next_tag;
		this->delObj(object, false, false, true, false);
		delete object;
	}
	checkDarkness();
}

//*********************************************************************
//						cleanMobForSaving
//*********************************************************************
// This function will clean up the mob so it is ready for saving to the database
// ie: it will clear the inventory, any flags that shouldn't be saved to the
// database, clear any following etc.

int Monster::cleanMobForSaving() {

	// No saving pets!
	if(isPet())
		return(-1);

	// Clear the inventory
	clearMobInventory();

	// Clear any flags that shouldn't be set
	clearFlag(M_WILL_BE_LOGGED);

//	// If the creature is possessed, clean that up
	if(flagIsSet(M_DM_FOLLOW)) {
		clearFlag(M_DM_FOLLOW);
		if(following != NULL) {
			following->clearFlag(P_ALIASING);
			following->getPlayer()->setAlias(0);
			following->print("%1M's soul was saved.\n", this);
			doStopFollowing(this, FALSE);
		}
	}

	// Success
	return(1);
}

//*********************************************************************
//						displayFlags
//*********************************************************************

int Creature::displayFlags() const {
	int flags = 0;
	if(isEffected("detect-invisible"))
		flags |= INV;
	if(isEffected("detect-magic"))
		flags |= MAG;
	if(isEffected("true-sight"))
		flags |= MIST;
	if(isPlayer()) {
		if(cClass == BUILDER)
			flags |= ISBD;
		if(cClass == CARETAKER)
			flags |= ISCT;
		if(isDm())
			flags |= ISDM;
		if(flagIsSet(P_NO_NUMBERS))
			flags |= NONUM;
	}
	return(flags);
}

//*********************************************************************
//						displayCreature
//*********************************************************************

int Player::displayCreature(Creature* target) const {
	int		percent=0, align=0, rank=0, chance=0, flags = displayFlags();
	Player	*pTarget = target->getPlayer();
	Monster	*mTarget = target->getMonster();
	std::ostringstream oStr;
	bstring str = "";
	bool space=false;

	if(mTarget) {
		oStr << "You see " << crt_str(mTarget, 1, flags) << ".\n";
		if(mTarget->getDescription() != "")
			oStr << mTarget->getDescription() << "\n";
		else
			oStr << "There is nothing special about " << crt_str(mTarget, 0, flags) << ".\n";

		if(mTarget->getMobTrade()) {
			rank = mTarget->getSkillLevel()/10;
			oStr << "^y" << crt_str(mTarget, 0, flags | CAP) << " is a " << get_trade_string(mTarget->getMobTrade())
				 << ". " << mTarget->upHisHer() << " skill level: " << get_skill_string(rank) << ".^x\n";
		}
	} else if(pTarget) {

		oStr << "You see " << pTarget->fullName() << " the "
			 << gConfig->getRace(pTarget->getDisplayRace())->getAdjective();
		// will they see through the illusion?
		if(willIgnoreIllusion() && pTarget->getDisplayRace() != pTarget->getRace())
			oStr << " (" << gConfig->getRace(pTarget->getRace())->getAdjective() << ")";
		oStr << " "
			 << pTarget->getTitle() << ".\n";

		if(gConfig->getCalendar()->isBirthday(pTarget)) {
			oStr << "^yToday is " << pTarget->name << "'s birthday! " << pTarget->upHeShe()
				 << " is " << pTarget->getAge() << " years old.^x\n";
		}

		if(pTarget->description != "")
			oStr << pTarget->description << "\n";
	}

	if(target->isEffected("vampirism")) {
		chance = intelligence.getCur()/10 + piety.getCur()/10;
		// vampires and werewolves can sense each other
		if(isEffected("vampirism") || isEffected("lycanthropy"))
			chance = 101;
		if(chance > mrand(0,100)) {
			switch(mrand(1,4)) {
			case 1:
				oStr << target->upHeShe() << " looks awfully pale.\n";
				break;
			case 2:
				oStr << target->upHeShe() << " has an unearthly presence.\n";
				break;
			case 3:
				oStr << target->upHeShe() << " has hypnotic eyes.\n";
				break;
			default:
				oStr << target->upHeShe() << " looks rather pale.\n";
			}
		}
	}

	if(target->isEffected("lycanthropy")) {
		chance = intelligence.getCur()/10 + piety.getCur()/10;
		// vampires and werewolves can sense each other
		if(isEffected("vampirism") || isEffected("lycanthropy"))
			chance = 101;
		if(chance > mrand(0,100)) {
			switch(mrand(1,3)) {
			case 1:
				oStr << target->upHeShe() << " looks awfully shaggy.\n";
				break;
			case 2:
				oStr << target->upHeShe() << " has a feral presence.\n";
				break;
			default:
				oStr << target->upHeShe() << " looks rather shaggy.\n";
			}
		}
	}

	if(target->isEffected("slow"))
		oStr << target->upHeShe() << " is moving very slowly.\n";
	else if(target->isEffected("haste"))
		oStr << target->upHeShe() << " is moving unnaturally quick.\n";


	if((cClass == CLERIC && deity == JAKAR && level >=7) || isCt())
		oStr << "^y" << crt_str(target, 0, flags | CAP) << " is carrying "
			 << target->coins[GOLD] << " gold coin"
			 << (target->coins[GOLD] != 1 ? "s" : "") << ".^x\n";

	if(isEffected("know-aura") || cClass==PALADIN) {
		space = true;
		oStr << crt_str(target, 0, flags | CAP) << " ";

		align = target->getAdjustedAlignment();

		switch(align) {
		case BLOODRED:
			oStr << "has a blood red aura.";
			break;
		case REDDISH:
			oStr << "has a reddish aura.";
			break;
		case PINKISH:
			oStr << "has a pinkish aura.";
			break;
		case NEUTRAL:
			oStr << "has a grey aura.";
			break;
		case LIGHTBLUE:
			oStr << "has a light blue aura.";
			break;
		case BLUISH:
			oStr << "has a bluish aura.";
			break;
		case ROYALBLUE:
			oStr << "has a royal blue aura.";
			break;
		default:
			oStr << "has a grey aura.";
			break;
		}
	}

	if(mTarget && mTarget->getRace()) {
		if(space)
			oStr << " ";
		space = true;
		oStr << mTarget->upHeShe() << " is ^W"
			 << gConfig->getRace(mTarget->getRace())->getAdjective().toLower() << "^x.";
	}
	if(target->getSize() != NO_SIZE) {
		if(space)
			oStr << " ";
		space = true;
		oStr << target->upHeShe() << " is ^W" << getSizeName(target->getSize()) << "^x.";
	}

	if(space)
		oStr << "\n";

	if(target->hp.getCur() > 0 && target->hp.getMax())
		percent = (100 * (target->hp.getCur())) / (target->hp.getMax());
	else
		percent = -1;

	oStr << target->upHeShe();
	if(percent >= 100 || !target->hp.getMax())
		oStr << " is in excellent condition.\n";
	else if(percent >= 90)
		oStr << " has a few scratches.\n";
	else if(percent >= 75)
		oStr << " has some small wounds and bruises.\n";
	else if(percent >= 60)
		oStr << " is wincing in pain.\n";
	else if(percent >= 35)
		oStr << " has quite a few wounds.\n";
	else if(percent >= 20)
		oStr << " has some big nasty wounds and scratches.\n";
	else if(percent >= 10)
		oStr << " is bleeding awfully from big wounds.\n";
	else if(percent >= 5)
		oStr << " is barely clinging to life.\n";
	else if(percent >= 0)
		oStr << " is nearly dead.\n";

	if(pTarget) {
		if(pTarget->flagIsSet(P_MISTED)) {
			oStr << pTarget->upHeShe() << "%s is currently in mist form.\n";
			printColor("%s", oStr.str().c_str());
			return(0);
		}

		if(pTarget->flagIsSet(P_UNCONSCIOUS))
			oStr << pTarget->name << " is "
				 << (pTarget->flagIsSet(P_SLEEPING) ? "sleeping" : "unconscious") << ".\n";

		if(pTarget->isEffected("petrification"))
			oStr << pTarget->name << " is petrified.\n";

		if(pTarget->isBraindead())
			oStr << pTarget->name << "%M is currently brain dead.\n";
	} else {
		if(mTarget->isEnmCrt(name))
			oStr << mTarget->upHeShe() << " looks very angry at you.\n";
		else if(mTarget->getPrimeFaction() != "")
			oStr << mTarget->upHeShe() << " " << getFactionMessage(mTarget->getPrimeFaction()) << ".\n";

		if(mTarget->first_enm) {
			if(!strcmp(mTarget->first_enm->enemy, name)) {
				if(	/* !mTarget->flagIsSet(M_WILL_BASH) &&
					!mTarget->flagIsSet(M_WILL_BACKSTAB) && */
					!mTarget->flagIsSet(M_HIDDEN) &&
					!(mTarget->isInvisible() && isEffected("detect-invisible"))
				)
					oStr << mTarget->upHeShe() << " is attacking you.\n";
			} else
				oStr << mTarget->upHeShe() << " is attacking " << mTarget->first_enm->enemy << ".\n";

			/// print all the enemies if a CT or DM is looking
			if(isCt()) {
				etag           *ep;
				oStr << "Enemy list:\n";
				ep = mTarget->first_enm;
				while(ep) {
					if(ep->owner[0])
						oStr << "  " << mTarget->upHeShe() << " is attacking " << ep->enemy
							 << "(" << ep->owner << "). (" << ep->damage << " DMG)\n";
					else
						oStr << "  " << mTarget->upHeShe() << " is attacking " << ep->enemy
							 << ". (" << ep->damage << " DMG)\n";

					ep = ep->next_tag;
				}
			}
		}
		oStr << consider(mTarget);

		// pet code
		if(mTarget->isPet() && mTarget->following == this) {
			str = listObjects(this, mTarget->first_obj, true);
			oStr << mTarget->upHeShe() << " ";
			if(str == "")
				oStr << "isn't holding anything.\n";
			else
				oStr << "is carrying: " << str << ".\n";
		}
	}
	printColor("%s", oStr.str().c_str());
	target->printEquipList(this);
	return(0);
}

//*********************************************************************
//						findCrt
//*********************************************************************

int findCrt(Creature * player, ctag *first_ct, int findFlags, char *str, int val, int* match, Creature ** target ) {
	ctag *cp;
	int found=0;

	if(!player || !str || !first_ct)
		return(0);

	cp = first_ct;
	while(cp) {
		if(!cp->crt) {
			cp = cp->next_tag;
			continue;
		}
		if(!player->canSee(cp->crt)) {
			cp = cp->next_tag;
			continue;
		}

		if(keyTxtEqual(cp->crt, str)) {
			(*match)++;
			if(*match == val) {
				*target = cp->crt;
				found = 1;
				break;
			}
		}

		cp = cp->next_tag;
	}
	return(found);
}

//*********************************************************************
//						checkSpellWearoff
//*********************************************************************

void Monster::checkSpellWearoff() {
	long t = time(0);

	if(flagIsSet(M_WILL_MOVE_FOR_CASH)) {
		if(t > LT(this, LT_MOB_PASV_GUARD)) {
			// Been 30 secs, time to stand guard again
			setFlag(M_PASSIVE_EXIT_GUARD);
		}
	}

	if(t > LT(this, LT_CHARMED) && flagIsSet(M_CHARMED)) {
		printColor("^y%M's demeanor returns to normal.\n", this);
		clearFlag(M_CHARMED);
	}
}


//*********************************************************************
//						canFlee
//*********************************************************************

bool Creature::canFlee() const {
	bool	crtInRoom=false;
	long	t=0;
	int		i=0;
	ctag*	cp=0;

	if(isMonster()) {

		if(!flagIsSet(M_WILL_FLEE) || flagIsSet(M_DM_FOLLOW))
			return(false);

		if(hp.getCur() > hp.getMax()/5)
			return(false);

		if(flagIsSet(M_PERMENANT_MONSTER))
			return(false);

	} else {
		//Player* pThis = getPlayer();
		if(!ableToDoCommand())
			return(false);

		if(flagIsSet(P_SITTING)) {
			print("You have to stand up first.\n");
			return(false);
		}

		if(isEffected("hold-person")) {
			print("You are unable to move right now.\n");
			return(false);
		}


		if(	(cClass == BERSERKER || cClass == CLERIC) &&
			flagIsSet(P_BERSERKED)
		) {
			printColor("^rYour lust for battle prevents you from fleeing!\n");
			return(false);
		}

		if(!isEffected("fear") && !isStaff()) {
//			// Check attack timer first
//			pThis->modifyAttackDelay(30);
//			bool result = pThis->checkAttackTimer();
//			pThis->modifyAttackDelay(-30); // Set the timer back to whwere it was before
//			if(result == false)
//				return(false);

			t = time(0);
			i = MAX(getLTAttack(), MAX(lasttime[LT_SPELL].ltime,lasttime[LT_READ_SCROLL].ltime)) + 3L;
			if(t < i) {
				pleaseWait(i-t);
				return(false);
			}
		}

		// confusion and drunkenness overrides following checks
		if(isEffected("confusion") || isEffected("drunkenness"))
			return(true);


		// players can only flee if someone/something else is in the room
		cp = getRoom()->first_mon;
		while(cp && !crtInRoom) {
			if(!cp->crt->isPet())
				crtInRoom = true;
			cp = cp->next_tag;
		}

		cp = getRoom()->first_ply;
		while(cp && !crtInRoom) {
			if(cp->crt == this) {
				cp = cp->next_tag;
				continue;
			}
			if(!cp->crt->isStaff())
				crtInRoom = true;
			cp = cp->next_tag;
		}

		if(!crtInRoom) {
			print("There's nothing to flee from!\n");
			return(false);
		}
	}

	return(true);
}


//*********************************************************************
//						canFleeToExit
//*********************************************************************

bool Creature::canFleeToExit(const Exit *exit, bool skipScary) {
	Player	*pThis = getPlayer();

	// flags both players and mobs have to obey
	if(!canEnter(exit) ||

		exit->flagIsSet(X_SECRET) ||
		exit->isConcealed(this) ||
		exit->flagIsSet(X_DESCRIPTION_ONLY) ||

		exit->flagIsSet(X_NO_FLEE) ||

		exit->flagIsSet(X_TO_STORAGE_ROOM) ||
		exit->flagIsSet(X_TO_BOUND_ROOM) ||
		exit->flagIsSet(X_TOLL_TO_PASS) ||
		exit->flagIsSet(X_WATCHER_UNLOCK)
	)
		return(false);


	if(pThis) {

		if(
			(getRoom()->flagIsSet(R_NO_FLEE) && inCombat()) ||
			(exit->flagIsSet(X_DIFFICULT_CLIMB) && !isEffected("levitate")) ||
			(
	        	(
	        		exit->flagIsSet(X_NEEDS_CLIMBING_GEAR) ||
	        		exit->flagIsSet(X_CLIMBING_GEAR_TO_REPEL) ||
	        		exit->flagIsSet(X_DIFFICULT_CLIMB)
	        	) &&
				!isEffected("levitate")
			)
		) return(false);

		int chance=0, *scary;

		// should we skip checking for scary exits?
		skipScary = skipScary || fd == -1 || exit->target.mapmarker.getArea() || isEffected("fear");

		// are they scared of going in that direction
		if(!skipScary && pThis->scared_of) {
			scary = pThis->scared_of;
			while(*scary) {
				if(exit->target.room.id && *scary == exit->target.room.id) {
					// oldPrint(fd, "Scared of going %s!\n", exit->name);

					// there is a chance we will flee to this exit anyway
					chance = 65 + bonus((int) dexterity.getCur()) * 5;

					if(getRoom()->flagIsSet(R_DIFFICULT_TO_MOVE) || getRoom()->flagIsSet(R_DIFFICULT_FLEE))
						chance /=2;

					if(mrand(1,100) < chance)
						return(false);

					// success; means that the player is now scared of this room
					if(parent_rom && fd > -1) {
						scary = pThis->scared_of;
						{
							int roomNum = parent_rom->info.id;
							if(scary) {
								int size = 0;
								while(*scary) {
									size++;
									if(*scary == roomNum)
										break;
									scary++;
								}
								if(!*scary) {
									// LEAK: Next line reported to be leaky: 10 count
									pThis->scared_of = (int *) realloc(pThis->scared_of, (size + 2) * sizeof(int));
									(pThis->scared_of)[size] = roomNum;
									(pThis->scared_of)[size + 1] = 0;
								}
							} else {
								// LEAK: Next line reported to be leaky: 10 count
								pThis->scared_of = (int *) malloc(sizeof(int) * 2);
								(pThis->scared_of)[0] = roomNum;
								(pThis->scared_of)[1] = 0;
							}
						}
					}
					return(true);
				}
				scary++;
			}
		}

	}

	return(true);
}


//*********************************************************************
//						getFleeableExit
//*********************************************************************

Exit* Creature::getFleeableExit() {
	xtag*	xp = getRoom()->first_ext;
	int		i=0, exit=0;
	bool	skipScary=false;

	// count exits so we can randomly pick one
	while(xp) {
		if(canFleeToExit(xp->ext))
			i++;
		xp = xp->next_tag;
	}

	if(i) {
		// pick a random exit
		exit = mrand(1, i);
	} else if(isPlayer()) {
		// force players to skip the scary list
		xp = getRoom()->first_ext;
		skipScary = true;
		i=0;

		while(xp) {
			if(canFleeToExit(xp->ext, true))
				i++;
			xp = xp->next_tag;
		}
		if(i)
			exit = mrand(1, i);
	}

	if(!exit)
		return(0);

	i=0;
	xp = getRoom()->first_ext;
	while(xp) {
		if(canFleeToExit(xp->ext, skipScary))
			i++;
		if(i == exit)
			return(xp->ext);
		xp = xp->next_tag;
	}

	return(0);
}


//*********************************************************************
//						getFleeableRoom
//*********************************************************************

BaseRoom* Creature::getFleeableRoom(Exit* exit) {
	AreaRoom* aRoom=0;
	Room*	uRoom=0;
	if(!exit)
		return(0);
	// exit flags have already been taken care of above, so
	// feign teleporting so we don't recycle the room
	Move::getRoom(this, exit, &uRoom, &aRoom, false, 0, false);
	if(uRoom)
		return(uRoom);
	else if(aRoom)
		return(aRoom);
	return(0);
}


//*********************************************************************
//						flee
//*********************************************************************
// This function allows a player to flee from an enemy. If successful
// the player will drop their readied weapon and run through one of the
// visible exits, losing 10% or 1000 experience, whichever is less.

int Creature::flee(bool magicTerror) {
	Monster* mThis = getMonster();
	Player* pThis = getPlayer();
	BaseRoom* newRoom=0;
	Room*	uRoom=0;
	MapMarker* mapmarker=0;
	Exit*	exit=0;
	unsigned int n=0;

	if(!magicTerror && !canFlee())
		return(0);

	if(isEffected("fear"))
		magicTerror = true;

	exit = getFleeableExit();
	if(!exit) {
		printColor("You failed to escape!\n");
		return(0);
	}

	newRoom = getFleeableRoom(exit);
	if(!newRoom) {
		printColor("You failed to escape!\n");
		return(0);
	}


	switch(mrand(1,10)) {
	case 1:
		print("You run like a chicken.\n");
		break;
	case 2:
		print("You flee in terror.\n");
		break;
	case 3:
		print("You run screaming in horror.\n");
		break;
	case 4:
		print("You flee aimlessly in any direction you can.\n");
		break;
	case 5:
		print("You run screaming for your mommy.\n");
		break;
	case 6:
		print("You run as your life flashes before your eyes.\n");
		break;
	case 7:
		print("Your heart throbs as you attempt to escape death.\n");
		break;
	case 8:
		print("Colors and sounds mingle as you frantically flee.\n");
		break;
	case 9:
		print("Fear of death grips you as you flee in panic.\n");
		break;
	case 10:
		print("You run like a coward.\n");
		break;
	default:
		print("You run like a chicken.\n");
		break;
	}


	broadcast(getSock(), getRoom(), "%M flees to the %s.", this, exit->name);
	if(mThis) {
		mThis->diePermCrt();
		if(exit->doEffectDamage(this))
			return(2);
		mThis->deleteFromRoom();
		mThis->addToRoom(newRoom);
	} else if(pThis) {
		pThis->dropWeapons();
		pThis->checkDarkness();
		unhide();

		if(magicTerror)
			printColor("^rYou flee from unnatural fear!\n");

		if(area_room)
			mapmarker = &(area_room->mapmarker);
		Move::track(parent_rom, mapmarker, exit, pThis, false);

		if(pThis->flagIsSet(P_ALIASING)) {
			pThis->getAlias()->deleteFromRoom();
			pThis->getAlias()->addToRoom(newRoom);
		}

		Move::update(pThis);
		pThis->statistics.flee();

		if(cClass == PALADIN && deity != GRADIUS && !magicTerror && level >= 10) {
			n = level * 8;
			n = MIN(experience, n);
			print("You lose %d experience for your cowardly retreat.\n", n);
			experience -= n;
		}

		if(exit->doEffectDamage(this))
			return(2);

		mThis = pThis->getPet();
		if(mThis && inSameRoom(mThis))
			broadcast(getSock(), getRoom(), "%M flees to the %s with its master.",
				mThis, exit->name);
		mThis = 0;

		pThis->deleteFromRoom();
		pThis->addToRoom(newRoom);
	}
	broadcast(getSock(), newRoom, "%M just fled rapidly into the room.", this);

	if(pThis) {
		pThis->doPetFollow();
		uRoom = newRoom->getUniqueRoom();
		if(uRoom)
			pThis->checkTraps(uRoom);
	}

	return(1);
}