roh/conf.old/area/
roh/config/code/python/
roh/config/game/area/
roh/config/game/signs/
roh/help/dmhelp/
roh/help/help/
roh/log/
roh/log/staff/
roh/monsters/ocean/
roh/objects/misc/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.47e/
/*
 * misc.cpp
 *   Miscellaneous string, file and data structure manipulation routines
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 *	Copyright (C) 2007-2012 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
//#include <stdio.h>
//#include <sys/types.h>
//#include <ctype.h>
//#include <stdarg.h>
#include "mud.h"
#include <math.h>
#include "commands.h"
#include "login.h"


//*********************************************************************
//						validId functions
//*********************************************************************

bool validMobId(const CatRef cr) {
	return(!cr.isArea("") && cr.id > 0 && cr.id < MMAX);
}
bool validObjId(const CatRef cr) {
	// 0 = coins
	return(!cr.isArea("") && cr.id >= 0 && cr.id < OMAX);
}
bool validRoomId(const CatRef cr) {
	// 0 = void
	return(!cr.isArea("") && cr.id >= 0 && cr.id < RMAX);
}



//*********************************************************************
//						nameEqual
//*********************************************************************
// we need to remove colors that might be in the name!

bstring removeColor(bstring obj) {
	int i=0, len=0;
	bstring name = "";

	for(len = obj.length(); i<len; i++) {
		while(i<len && obj.at(i) == '^')
			i += 2;
		if(i<len)
			name += obj.at(i);
	}

	return(name);
}

//*********************************************************************
//						nameEqual
//*********************************************************************
// checks to see if the names are equal.

bool nameEqual(bstring obj, bstring str) {
	if(obj=="" || str=="")
		return(false);

	if(!strncasecmp(removeColor(obj).c_str(), str.c_str(), str.length()))
		return(true);

	return(false);
}



//*********************************************************************
//						lowercize
//*********************************************************************
// This function takes the string passed in as the first parameter and
// converts it to lowercase. If the flag in the second parameter has
// its first bit set, then the first letter is capitalized.

void lowercize(bstring& str, int flag ) {
	int 	i, n;

	n = str.length();
	for(i=0; i<n; i++)
		str[i] = (str[i] >= 'A' && str[i] <= 'Z') ? str[i]+32:str[i];
	if(flag & 1)
		str[0] = (str[0] >= 'a' && str[0] <= 'z') ? str[0]-32:str[0];
}

void lowercize(char *str, int flag ) {
    int     i, n;

    n = str ? strlen(str) : 0 ;
    for(i=0; i<n; i++)
        str[i] = (str[i] >= 'A' && str[i] <= 'Z') ? str[i]+32:str[i];
    if(flag & 1)
        str[0] = (str[0] >= 'a' && str[0] <= 'z') ? str[0]-32:str[0];
}

//*********************************************************************
//						low
//*********************************************************************
// If the character passed in as the first parameter is an uppercase
// alphabetic character, then it is converted to lowercase and returned
// Otherwise, it is unchanged.

int low(char ch) {
	if(ch >= 'A' && ch <= 'Z')
		return(ch+32);
	else
		return(ch);
}

int up(char ch) {
	if(ch >= 'a' && ch <= 'z')
		return(ch-32);
	else
		return(ch);
}

int bonus(int num) {
	return(statBonus[tMIN(num, MAX_STAT_NUM)/10]);
}

int crtWisdom(Creature* creature) {
	return(bonus(creature->intelligence.getCur()) + bonus(creature->piety.getCur()))/2;
}

int crtAwareness(Creature* creature) {
	return(bonus(creature->intelligence.getCur()) + bonus(creature->dexterity.getCur()))/2;
}
//*********************************************************************
//						zero
//*********************************************************************
// This function zeroes a block of bytes at the given pointer and the
// given length.

void zero( void	*ptr, int size ) {
	char 	*chptr;
	int	i;
	chptr = (char *)ptr;
	for(i=0; i<size; i++)
		chptr[i] = 0;
}


//*********************************************************************
//						delimit
//*********************************************************************
// This function takes a given string, and if it is greater than a given
// number of characters, then it is split up into several lines. This
// is done by replacing spaces with carriage returns before the end of/
// the line.

bstring delimit(const char *str, int wrap) {
	int 	i=0, j=0, x=0, l=0, len=0, lastspace=0;
	char* work = strdup(str);
	std::ostringstream outStr;

	if(wrap <= 10)
		wrap = 78;

	j = (str) ? strlen(str) : 0;
	if(j < wrap) {
		free(work);
		return str;
	}

	len = 0;
	lastspace = -1;
	l = 0;
	for(i=0; i<j; i++) {
		if(work[i] == ' ')
			lastspace = i;
		if(work[i] == '\n') {
			len = 0;
			lastspace = -1;
		}
		if(work[i] == '^')
		    x+=2;
		if(work[i] == '\033')
			x+=7;

		len++;
		if(len > (wrap + x) && lastspace > -1) {
			work[lastspace] = 0;
			outStr << &work[l] << "\n  ";
			l = lastspace + 1;
			len = i - lastspace + 3;
			lastspace = -1;
			x=0;
		}
	}
	outStr << &work[l];
	free(work);
	return(outStr.str());
}

//*********************************************************************
//						viewFile
//*********************************************************************
// This function views a file whose name is given by the third
// parameter. If the file is longer than 20 lines, then the user is
// prompted to hit return to continue, thus dividing the output into
// several pages.

#define FBUF	800

void viewFileReal(Socket* sock, bstring str ) {
	char	buf[FBUF+1];
	int	i, l, n, ff, line;
	long	offset;

	buf[FBUF] = 0;
	switch(sock->getParam()) {
	case 1:
		offset = 0L;
		strncpy(sock->tempstr[1], str.c_str(),255);
		sock->tempstr[1][255] = 0;
		ff = open(str.c_str(), O_RDONLY, 0);
		if(ff < 0) {
			sock->print("File could not be opened.\n");
			if(sock->getPlayer())
				sock->getPlayer()->clearFlag(P_READING_FILE);
			sock->restoreState();
			return;
		}
		line = 0;
		while(1) {
			n = read(ff, buf, FBUF);
			l = 0;
			for(i=0; i<n; i++) {
				if(buf[i] == '\n') {
					buf[i] = 0;
					line++;
					sock->bprint(&buf[l]);
					sock->bprint("\n");
					offset += (i-l+1);
					l = i+1;
				}
				if(line > 20)
					break;
			}
			if(line > 20) {
				sprintf(sock->tempstr[2], "%ld", offset);
				break;
			} else if(l != n) {
			    sock->bprint(&buf[l]);
				offset += (i-l);
			}
			if(n<FBUF)
				break;
		}
		if(n==FBUF || line>20) {
			sock->getPlayer()->setFlag(P_READING_FILE);
			if(!sock->getPlayer()->flagIsSet(P_MIRC))
				sock->print("[Hit Return, Q to Quit]: ");
			else
				sock->print("[Hit C to continue]: ");
			gServer->processOutput();
		}
		if(n<FBUF && line <= 20) {
			close(ff);
			sock->restoreState();
			return;
		} else {
			close(ff);
			sock->getPlayer()->setFlag(P_READING_FILE);
			sock->setState(CON_VIEWING_FILE, 2);
			return;
		}
	case 2:
		if(str[0] != 0  && str[0] != 'c' && str[0] != 'C') {
			sock->print("Aborted.\n");
			if(sock->getPlayer())
				sock->getPlayer()->clearFlag(P_READING_FILE);
			sock->restoreState();
			return;
		}
		offset = atol(sock->tempstr[2]);
		ff = open(sock->tempstr[1], O_RDONLY, 0);
		if(ff < 0) {
			sock->print("File could not be opened [%s].\n", sock->tempstr);
			if(sock->getPlayer())
				sock->getPlayer()->clearFlag(P_READING_FILE);
			sock->restoreState();
			return;
		}
		lseek(ff, offset, 0);
		line = 0;
		while(1) {
			n = read(ff, buf, FBUF);
			l = 0;
			for(i=0; i<n; i++) {
				if(buf[i] == '\n') {
					buf[i] = 0;
					line++;
					sock->bprint(&buf[l]);
					sock->bprint("\n");
					offset += (i-l+1);
					l = i+1;
				}
				if(line > 20)
					break;
			}
			if(line > 20) {
				sprintf(sock->tempstr[2], "%ld", offset);
				break;
			} else if(l != n) {
			    sock->bprint(&buf[l]);
				offset += (i-l);
			}
			if(n<FBUF)
				break;
		}
		if(n==FBUF || line > 20) {
			if(sock->getPlayer())
				sock->getPlayer()->setFlag(P_READING_FILE);
			sock->print("[Hit Return, Q to Quit]: ");
			gServer->processOutput();
		}
		if(n<FBUF && line <= 20) {
			close(ff);
			if(sock->getPlayer())
				sock->getPlayer()->clearFlag(P_READING_FILE);
			sock->restoreState();
			return;
		} else {
			close(ff);
			sock->setState(CON_VIEWING_FILE, 2);
		}
	}
}
// Wrapper function for viewFile_real that will set the correct connected state
void viewFile(Socket* sock, bstring str) {
	if(sock->getState() != CON_VIEWING_FILE)
		sock->setState(CON_VIEWING_FILE);

	viewFileReal(sock, str);
}

//*********************************************************************
//						viewLoginFile
//*********************************************************************
// This function views a file whose name is given by the third
// parameter. If the file is longer than 20 lines, then the user is
// prompted to hit return to continue, thus dividing the output into
// several pages.

#define FBUF_L	1024

void viewLoginFile(Socket* sock, bstring str, bool showError) {
	char	buf[FBUF_L + 1];
	int		i=0, l=0, n=0, ff=0, line=0;
	long	offset=0;
	zero(buf, sizeof(buf));

	buf[FBUF_L] = 0;
	{
		offset = 0L;
		strcpy(sock->tempstr[1], str.c_str());
		ff = open(str.c_str(), O_RDONLY, 0);
		if(ff < 0) {
			if(showError) {
				sock->print("File could not be opened.\n");
				broadcast(isCt, "^yCan't open file: %s.\n", str.c_str()); // nothing to put into (%m)?
			}
			return;
		}
		line = 0;
		while(1) {
			n = read(ff, buf, FBUF_L);
			l = 0;
			for(i=0; i<n; i++) {
				if(buf[i] == '\n') {
					buf[i] = 0;
					if(i != 0 && buf[i-1] == '\r')
						buf[i-1] = 0;
					line++;
					sock->printColor("%s\n", &buf[l]);
					offset += (i - l + 1);
					l = i + 1;
				}
			}
			if(l != n) {
				sock->printColor("%s", &buf[l]);
				offset += (i - l);
			}
			if(n < FBUF_L) {
				close(ff);
				return;
			}
		}
		// Never makes it out of the while loop to get here
//		close(ff);
	}
}

//*********************************************************************
//						viewFileReverseReal
//*********************************************************************
// displays a file, line by line starting with the last
// similar to unix 'tac' command

void viewFileReverseReal(Socket* sock, bstring str) {
	off_t oldpos;
	off_t newpos;
	off_t temppos;
	int i,more_file=1,count,amount=1621;
	char string[1622];
	char search[80];
	long offset;
	FILE *ff;
	int TACBUF = ( (81 * 20 * sizeof(char)) + 1 );

	if(strlen(sock->tempstr[3]) > 0)
		strcpy(search, sock->tempstr[3]);
	else
		strcpy(search, "\0");

	switch(sock->getParam()) {
	case 1:

		strcpy(sock->tempstr[1], str.c_str());
		if((ff = fopen(str.c_str(), "r")) == NULL) {
			sock->print("error opening file\n");
			sock->restoreState();
			return;
		}



		fseek(ff, 0L, SEEK_END);
		oldpos = ftell(ff);
		if(oldpos < 1) {
			sock->print("Error opening file\n");
			sock->restoreState();
			return;
		}
		break;

	case 2:

		if(str[0] != 0) {
			sock->print("Aborted.\n");
			sock->getPlayer()->clearFlag(P_READING_FILE);
			sock->restoreState();
			return;
		}

		if((ff = fopen(sock->tempstr[1], "r")) == NULL) {
			sock->print("error opening file\n");
			sock->getPlayer()->clearFlag(P_READING_FILE);
			sock->restoreState();
			return;
		}

		offset = atol(sock->tempstr[2]);
		fseek(ff, offset, SEEK_SET);
		oldpos = ftell(ff);
		if(oldpos < 1) {
			sock->print("Error opening file\n");
			sock->restoreState();
			return;
		}

	}

nomatch:
	temppos = oldpos - TACBUF;
	if(temppos > 0)
		fseek(ff, temppos, SEEK_SET);
	else {
		fseek(ff, 0L, SEEK_SET);
		amount = oldpos;
	}

	newpos = ftell(ff);


	fread(string, amount,1, ff);
	string[amount] = '\0';
	i = strlen(string);
	i--;

	count = 0;
	while(count < 21 && i > 0) {
		if(string[i] == '\n') {
			if(	(	strlen(search) > 0 &&
					strstr(&string[i], search)
				) ||
				search[0] == '\0'
			) {
				sock->printColor("%s", &string[i]);
				count++;
			}
			string[i]='\0';
			if(string[i-1] == '\r')
				string[i-1]='\0';
		}
		i--;
	}

	oldpos = newpos + i + 2;
	if(oldpos < 3)
		more_file = 0;

	sprintf(sock->tempstr[2], "%ld", (long) oldpos);


	if(more_file && count == 0)
		goto nomatch;		// didnt find a match within a screenful
	else if(more_file) {
		sock->print("\n[Hit Return, Q to Quit]: ");
		gServer->processOutput();
		sock->intrpt &= ~1;

		fclose(ff);
		sock->getPlayer()->setFlag(P_READING_FILE);
		sock->setState(CON_VIEWING_FILE_REVERSE, 2);
		return;
	} else {
		if((strlen(search) > 0 && strstr(string, search))
		        || search[0] == '\0') {
			sock->print("\n%s\n", string);
		}
		fclose(ff);
		sock->getPlayer()->clearFlag(P_READING_FILE);
		sock->restoreState();
		return;
	}

}

// Wrapper for viewFileReverse_real that properly sets the connected state
void viewFileReverse(Socket* sock, bstring str) {
	if(sock->getState() != CON_VIEWING_FILE_REVERSE)
		sock->setState(CON_VIEWING_FILE_REVERSE);
	viewFileReverseReal(sock, str);
}

//*********************************************************************
//						dice
//*********************************************************************
// This function rolls n s-sided dice and adds p to the total.

int dice(int n, int s, int p) {
	int	i;
	if(n==0 || s== 0)
		return(p);

	for(i=0; i<n; i++)
		p += mrand(1,s);

	return(p);
}

//*********************************************************************
//						exp_to_lev
//*********************************************************************
// This function takes a given amount of experience as its first
// argument returns the level that the experience reflects.

int exp_to_lev(unsigned long exp) {
	int level = 1;

	while(exp >= gConfig->expNeeded(level) && level < MAXALVL)
		level++;
	if(level == MAXALVL) {
		level = exp/(needed_exp[MAXALVL-1]);
		level++;
		level= MAX(MAXALVL, level);
	}

	return(MAX(1,level));
}

//*********************************************************************
//						dec_daily
//*********************************************************************
// This function is called whenever a daily-use item or operation is
// used or performed. If the number of daily uses are used, up then
// a 0 is returned. Otherwise, the number of uses is decremented and
// a 1 is returned.

int dec_daily(struct daily *dly_ptr) {
	long		t;
	struct tm	*tm, time1, time2;

	t = time(0);
	tm = localtime(&t);
	time1 = *tm;
	tm = localtime(&dly_ptr->ltime);
	time2 = *tm;

	if(time1.tm_yday != time2.tm_yday) {
		dly_ptr->cur = dly_ptr->max;
		dly_ptr->ltime = t;
	}

	if(dly_ptr->cur == 0)
		return(0);

	dly_ptr->cur--;

	return(1);
}

//*********************************************************************
//						update_daily
//*********************************************************************

int update_daily(struct daily *dly_ptr) {
	long		t = time(0);
	struct tm	*tm, time1, time2;

	tm = localtime(&t);
	time1 = *tm;
	tm = localtime(&dly_ptr->ltime);
	time2 = *tm;

	if(time1.tm_yday != time2.tm_yday) {
		dly_ptr->cur = dly_ptr->max;
		dly_ptr->ltime = t;
	}

	return(0);
}


//*********************************************************************
//						file_exists
//*********************************************************************
// This function returns 1 if the filename specified by the first
// parameter exists, 0 if it doesn't.

bool file_exists(char *filename) {
	int ff=0;
	ff = open(filename, O_RDONLY);
	if(ff > -1) {
		close(ff);
		return(true);
	}
	return(false);
}

/*====================================================================*/
// checks if the given str contains all digits
bool is_num(char *str ) {
	int len, i;
	len = strlen(str);
	for(i=0;i < len; i++)
		if(!isdigit(str[i]))
			return(false);
	return(true);
}

//*********************************************************************
//						isdm
//*********************************************************************
// returns 1 if the given player name is a dm

bool isdm(bstring name) {
	char **s = dmname;
	while(*s) {
		if(name == *s)
			return(true);
		s++;
	}
	return(false);
}
//*********************************************************************
//						smashInvis
//*********************************************************************
int Creature::smashInvis() {
	if(isPlayer()) {
		unhide();
		removeEffect("invisibility");
		unmist();
	}
	return(0);
}

//*********************************************************************
//						parse_name
//*********************************************************************
// Determine if a given name is acceptable

bool parse_name(bstring name) {
	FILE	*fp=0;
	int		i = name.length() - 1;
	char	str[80], path[80], forbid[20];
	strcpy(str, name.c_str());

	if(isTitle(str) || isClass(str))
		return(false);
	if(gConfig->racetoNum(str) >= 0)
		return(false);
	if(gConfig->deitytoNum(str) >= 0)
		return(false);


	// don't allow names with all the same char
	str[0] = tolower(str[0]);
	for(; i>0; i--)
		if(str[i] != str[0])
			break;
	if(!i)
		return(false);
	str[0] = toupper(str[0]);


	// check the DM names
	i=0;
	while(dmname[i]) {
		// don't forbid names directly equal to DM
		if(strcmp(dmname[i], str)) {
			if(!strncmp(dmname[i], str, strlen(str)))
				return(false);
			if(!strncmp(str, dmname[i], strlen(dmname[i])))
				return(false);
		}
		i++;
	}


	sprintf(path, "%s/forbidden_name.txt", Path::Config);
	fp = fopen(path, "r");
	if(!fp)
		merror("ERROR - forbidden name.txt", NONFATAL);
	else {
		while(!feof(fp)) {
			fscanf(fp, "%s", forbid);
			if(!strcmp(forbid, str)) {
				fclose(fp);
				return(false);
			}
		}
		fclose(fp);
	}


	lowercize(str, 0);
	if(strstr(str, "fuck"))
		return(false);
	if(strstr(str, "shit"))
		return(false);
	if(strstr(str, "suck"))
		return(false);
	if(strstr(str, "gay"))
		return(false);
	if(strstr(str, "isen"))
		return(false);
	if(strstr(str, "cock"))
		return(false);
	if(strstr(str, "realm"))
		return(false);
	if(strstr(str, "piss"))
		return(false);
	if(strstr(str, "dick"))
		return(false);
	if(strstr(str, "pussy"))
		return(false);
	if(strstr(str, "dollar"))
		return(false);
	if(strstr(str, "cunt"))
		return(false);

	return(true);
}

//*********************************************************************
//						dmIson
//*********************************************************************

int dmIson() {
	Player* player=0;
	int		idle=0;
	long	t = time(0);

	for( std::pair<bstring, Player*> p : gServer->players) {
		player = p.second;

		if(!player->isConnected())
			continue;
		idle = t - player->getSock()->ltime;
		if(player->isDm() && idle < 600)
			return(1);
	}

	return(0);
}

//*********************************************************************
//						bug
//*********************************************************************

void Player::bug(const char *fmt, ...) const {
	char	file[80];
	char	str[2048];
	int		fd;
	long	t = time(0);
	va_list	ap;

	if(!flagIsSet(P_BUGGED))
		return;

	va_start(ap, fmt);

	sprintf(file, "%s/%s.txt", Path::BugLog, getCName());
	fd = open(file, O_RDWR | O_APPEND, 0);
	if(fd < 0) {
		fd = open(file, O_RDWR | O_CREAT, ACC);
		if(fd < 0)
			return;
	}
	lseek(fd, 0L, 2);

	// prevent string overruns with vsn
	strcpy(str, ctime(&t));
	str[24] = ':';
	str[25] = ' ';
	vsnprintf(str + 26, 2000, fmt, ap);
	va_end(ap);

	write(fd, str, strlen(str));

	close(fd);
}

//*********************************************************************
//						autosplit
//*********************************************************************

int Player::autosplit(long amount) {
	int		remain=0, split=0, party=0;

	if(isStaff())
		return(0);
	if(!ableToDoCommand())
		return(0);

	if(amount <= 5)
		return(0);

	Group* group = getGroup();
	if(!group)
		return(0);

	for(Creature* crt : group->members) {
		if(crt->isPlayer() && !crt->isStaff() && crt->inSameRoom(this))
			party++;
	}
	// If group is 1, return with no split.
	if(party < 2)
		return(0);
	// If less gold then people, there is no split.
	if(amount < party)
		return(0);

	remain = amount % party;	// Find remaining odd coins.
	split = ((amount - remain) / party);  // Determine split minus the remaining odd coins.

	for(Creature* crt : group->members) {
		if(crt->isPlayer() && !crt->isStaff() && crt->inSameRoom(this)) {
			if(crt == this) {
				crt->print("You received %d gold as your split.\n", split+remain);
				crt->coins.add(split+remain, GOLD);
			} else {
				crt->print("You received %d gold as your split from %N.\n", split, this);
				crt->coins.add(split, GOLD);
				crt->print("You now have %d gold coins.\n", crt->coins[GOLD]);
			}
		}

	}
	return(1);
}

//*********************************************************************
//						strPrefix
//*********************************************************************
// Determines if needle is a prefix to haystack
// Returns 1 if it is, 0 otherwise

int strPrefix(const char *haystack, const char *needle) {
	if(!haystack)
		return(0);
	if(!needle)
		return(0);

	for(; *haystack && *needle; haystack++, needle++)
		if(*haystack != *needle)
			return(0);

	return(1);
}

//*********************************************************************
//						strSuffix
//*********************************************************************
// Determines if needle is a suffix of haystack
// Returns 1 if it is, 0 otherwise

int strSuffix(const char *haystack, const char *needle) {
	int hayLen = strlen(haystack);
	int needleLen = strlen(needle);

	if(hayLen >= needleLen && !strcmp(needle, haystack + hayLen - needleLen))
		return(1);

	return(0);
}

//*********************************************************************
//						pkillPercent
//*********************************************************************

int pkillPercent(int pkillsWon, int pkillsIn) {
	double percent=0;
	double pkWon = pkillsWon * 1.0, pkIn = pkillsIn * 1.0;

	if(pkillsIn == pkillsWon) {
		percent = 100;
	} else {
		//	if(pkillsWon == 0)
		//		pkillsWon = 1;
		if(pkillsIn == 0)
			pkIn = 1.0;

		if(pkillsWon > pkillsIn)
			pkWon = pkIn;

		percent = (pkWon/pkIn) * 100.0;
	}
	return((int)percent);
}

//*********************************************************************
//						stun
//*********************************************************************
// stops a creature or player from attacking, casting a spell, or reading
// a scroll for <delay> seconds.  Used by stun (befuddle), circle

void Creature::stun(int delay) {
	if(!delay)
		return;
	updateAttackTimer(true, (delay+1)*10);
	lasttime[LT_KICK].ltime = time(0);
	lasttime[LT_SPELL].ltime = time(0);
	lasttime[LT_READ_SCROLL].ltime = time(0);
	lasttime[LT_KICK].interval = delay+1;
	lasttime[LT_SPELL].interval = delay+1;
	lasttime[LT_READ_SCROLL].interval = delay+1;
	if(isPlayer()) {
		lasttime[LT_PLAYER_STUNNED].ltime = time(0);
		lasttime[LT_PLAYER_STUNNED].interval = delay+1;
		setFlag(P_STUNNED);
	}
}

//*********************************************************************
//						numEnemyMonInRoom
//*********************************************************************

int	numEnemyMonInRoom(Creature* player) {
	int		count=0;
	for(Monster* mons : player->getRoomParent()->monsters) {
        if(mons->getAsMonster()->isEnemy(player))
            count++;
	}

	return(count);
}

//*********************************************************************
//						stripLineFeeds
//*********************************************************************

char *stripLineFeeds(char *str) {
	int n=0, i=0;
	char *name=0;

	name = str;
	n = strlen(name);

	for(i = n; i > 0; i--) {
		if(name[i] == '\n') {
			name[i] = ' ';
			break;
		}
	}

	return(name);
}

//*********************************************************************
//						stripBadChars
//*********************************************************************

void stripBadChars(char *str) {
	int n=0, i=0;

	n = (str) ? strlen(str) : 0;

	for(i = 0; i < n; i++) {
		if(str[i] == '/') {
			str[i] = ' ';
		}
	}
}

void stripBadChars(bstring str) {
    int n=0, i=0;

    n = str.length();

    for(i = 0; i < n; i++) {
        if(str[i] == '/') {
            str[i] = ' ';
        }
    }
}

//*********************************************************************
//						getLastDigit
//*********************************************************************
// This function returns the last digits of any integer n sent
// to it. digits specifies how many. -Tim C.

int	getLastDigit(int n, int digits) {
	double	num=0.0, sub=0.0;
	int		a=0, temp=0, digit=0;

	digits = MAX(1,MIN(4,digits));

	num = (double)n;
	for(a=5; a>digits-1; a--)	// start with 10^5th power (100000)
	{
		sub = pow(10,a);
		if(num - sub < 0)
			continue;
		else {
			temp = (int)num;
			temp %= (int)sub;
			num = (double)temp;
		}

	}

	digit = (int)num;
	return(digit);
}

//*********************************************************************
//						ltoa
//*********************************************************************

char *ltoa(
    long val,	// value to be converted
    char *buf,	// output string
    int base)	// conversion base
{
	ldiv_t r;	// result of val / base

	// no conversion if wrong base
	if(base > 36 || base < 2) {
		*buf = '\0';
		return buf;
	}
	if(val < 0)
		*buf++ = '-';
	r = ldiv (labs(val), base);

	// output digits of val/base first

	if(r.quot > 0)
		buf = ltoa ( r.quot, buf, base);

	// output last digit
	*buf++ = "0123456789abcdefghijklmnopqrstuvwxyz"[(int)r.rem];
	*buf   = '\0';
	return(buf);
}
//*********************************************************************
//                      findCrt
//*********************************************************************

template<class SetType>
MudObject* findCrtTarget(Creature * player, SetType& set, int findFlags, const char *str, int val, int* match) {

    if(!player || !str || set.empty())
        return(NULL);

    for(typename SetType::key_type crt : set) {
        if(!crt) {
            continue;
        }
        if(!player->canSee(crt)) {
            continue;
        }

        if(keyTxtEqual(crt, str)) {
            (*match)++;
            if(*match == val) {
                return(crt);
            }
        }

    }
    return(NULL);
}



//*********************************************************************
//						findTarget
//*********************************************************************

MudObject* Creature::findTarget(int findWhere, int findFlags, bstring str, int val) {
	int	match=0;
	bool found=false;
	MudObject* target;
	do {
		if(findWhere & FIND_OBJ_INVENTORY) {
			if((target = findObjTarget(objects, findFlags, str, val, &match))) {
				found = true;
				break;
			}
		}
		// See if we should look for a match in the player's equipment
		// -- Do this after looking for the object in their inventory
		if(findWhere & FIND_OBJ_EQUIPMENT) {
			int n;
			for(n=0; n<MAXWEAR; n++) {
				if(!ready[n])
					continue;
				if(keyTxtEqual(ready[n], str.c_str()))
					match++;
				else
					continue;
				if(val == match) {
					target = ready[n];
					found = true;
					break;
				}
			}
			if(found)
				break;
		}

		if(findWhere & FIND_OBJ_ROOM) {
			if((target = findObjTarget(getRoomParent()->objects, findFlags, str, val, &match))) {
				found = true;
				break;
			}
		}

		if(findWhere & FIND_MON_ROOM) {
			if((target = findCrtTarget<MonsterSet>(this, getParent()->monsters, findFlags, str.c_str(), val, &match))) {
				found = true;
				break;
			}
		}

		if(findWhere & FIND_PLY_ROOM) {
			if((target = findCrtTarget<PlayerSet>(this, getParent()->players, findFlags, str.c_str(), val, &match))) {
				found = true;
				break;
			}
		}

		if(findWhere & FIND_EXIT) {
			if((target = findExit(this, str, val, getRoomParent()))) {
				found = true;
				break;
			}
		}
	} while(0);

	return(target);
}

//**********************************************************************
//						new_merror
//**********************************************************************
// merror is called whenever an error message should be output to the
// log file.  If the error is fatal, then the program is aborted

void new_merror(const char *str, char errtype, const char *file, const int line) {
    printf("\nError: %s @ %s %d.\n", str, file, line);
    logn("error.log", "Error occured in %s in file %s line %d\n", str, file, line);
    if(errtype == FATAL) {
  		abort();
    }
//	exit(-1);
}

//*********************************************************************
//						timeStr
//*********************************************************************

bstring timeStr(int secs) {
	int hours = 0;
	int minutes = 0;
	int seconds = 0;
	hours = secs / 3600;
	secs -= hours * 3600;
	minutes = secs / 60;
	seconds = secs - minutes * 60;

	std::ostringstream timeStr;
	if(hours)
		timeStr << hours << " hour(s) ";
	if(minutes)
		timeStr << minutes << " minutes(s) ";
	// Always show seconds
	timeStr << seconds << " second(s)";

	return(timeStr.str());
}

//*********************************************************************
//						progressBar
//*********************************************************************

bstring progressBar(int barLength, float percentFull, bstring text, char progressChar, bool enclosed) {
	bstring str = "";
	int i=0, progress = (int)(barLength * percentFull);
	int lowTextBound=-1, highTextBound=-1;

	if(text.getLength()) {
		if(text.getLength() >= barLength)
			return(text);
		lowTextBound = (barLength - text.getLength())/2;
		highTextBound = lowTextBound + text.getLength();
	}

	if(enclosed)
		str += "[";

	for(i=0; i<barLength; i++) {
		// if we're in the range of the text we want to include, skip
		if(i == lowTextBound) {
			str += text;
			continue;
		}
		if(i > lowTextBound && i < highTextBound)
			continue;

		// otherwise give us a character
		if(i < progress)
			str += progressChar;
		else
			str += " ";
	}

	if(enclosed)
		str += "]";
	return(str);
}