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/
/*
 * misc.c
 *   Miscellaneous string, file and data structure manipulation routines
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 *	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 <stdio.h>
//#include <sys/types.h>
//#include <ctype.h>
//#include <stdarg.h>
#include <math.h>
#include "mud.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);
}



//*********************************************************************
//						addhp
//*********************************************************************
// adds hp to the given player

void addhp(struct Creature* player, int addhp) {
	player->hp.increase(addhp);
}
//*********************************************************************
//						addmp
//*********************************************************************
// adds mp to the given player

void addmp(struct Creature* player, int addmp) {
	player->mp.increase(addmp);
}


//*********************************************************************
//						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(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[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;
}

// Temporary (but static) data for the next several functions

static char	xstr[5][80];
static int	xnum=0;


#ifdef __CYGWIN__

char *obj_str(const Object *obj, int num, int flag ) {
    char *str;

    bstring tmp = obj->getObjStr(NULL, flag, num);

    str = xstr[xnum];
    xnum = (xnum + 1)%5;
    strncpy(str, tmp.c_str(), 80);
    return(str);
}

#endif

//*********************************************************************
//						crt_str
//*********************************************************************
// This function takes the creature given it in the first parameter,
// and forms the appropriate singularized or pluralized version of the
// creature's name using certain flags.

char *crt_str(const Creature *crt, int num, int flag ) {
	char	ch;
	char	*str;
	char    pform[80];
	char    sform[80];
	char    pfile[80];
	int     found, mobNum=0;
	FILE    *plural;

	if(!crt)
		return("(NULL CRT)");

	const Player* pCrt = crt->getConstPlayer();

	str = xstr[xnum];
	xnum = (xnum + 1)%5;

	// Player
	if(crt->isPlayer()) {
		// Target is possessing a monster -- Show the monsters name if invis
		if(crt->flagIsSet(P_ALIASING) && crt->flagIsSet(P_DM_INVIS)) {
			if(!pCrt->getAlias()->flagIsSet(M_NO_PREFIX)) {
				strcpy(str, "A ");
				strcat(str, pCrt->getAlias()->name);
			} else
				strcpy(str, pCrt->getAlias()->name);
		}
		// Target is a dm, is dm invis, and viewer is not a dm
		else if(crt->getClass() == DUNGEONMASTER && crt->flagIsSet(P_DM_INVIS) && !(flag & ISDM) ) {
			strcpy(str, "Someone");
		}
		// Target is a ct, is dm invis, and viewer is not a dm or ct
		else if( crt->getClass() == CARETAKER && (crt->flagIsSet(P_DM_INVIS) && !(flag & ISDM) && !(flag & ISCT))) {
			strcpy(str, "Someone");
		}
		// Target is staff less than a ct and is dm invis, viewier is less than a builder
		else if( crt->flagIsSet(P_DM_INVIS) && !(flag & ISDM) && !(flag & ISCT) && !(flag & ISBD)) {
			strcpy(str, "Someone");
		}
		// Target is misted, viewer can't detect mist, or isn't staff
		else if( crt->flagIsSet(P_MISTED) && !(flag & MIST) && !(flag & ISDM) && !(flag & ISCT) && !(flag & ISBD)) {
			strcpy(str, "A light mist");
		}
		// Target is invisible and viewer doesn't have detect-invis or isn't staff
		else if(crt->isInvisible() && !(flag & INV) && !(flag & ISDM) && !(flag & ISCT) && !(flag & ISBD)) {
			strcpy(str, "Someone");
			// Can be seen
		} else {
			strcpy(str, crt->name);
			// Dm Invis
			if(crt->flagIsSet(P_DM_INVIS))
				strcat(str, " (+)");
			// Invis
			else if(crt->isInvisible())
				strcat(str, " (*)");
			// Misted
			else if(crt->flagIsSet(P_MISTED))
				strcat(str, " (m)");
		}
		return(str);
	}

	// Monster
	// Target is a monster, is invisible, and viewer doesn't have detect-invis or is not staff
	if(crt->isMonster() && crt->isInvisible() && !(flag & INV) &&
	        !(flag & ISDM) && !(flag & ISCT) && !(flag & ISBD)) {
		strcpy(str, "Something");
		return(str);
	} else {

		if(num == 0) {
			if(!crt->flagIsSet(M_NO_PREFIX)) {
				strcpy(str, "the ");
				if(!(flag & NONUM)) {
					mobNum = ((Monster*)crt)->getNumMobs();
					if(mobNum>1) {
						strcat(str, getOrdinal(mobNum).c_str());
						strcat(str, " ");
					}
				}
				strcat(str, crt->name);
			} else
				strcpy(str, crt->name);
		} else if(num == 1) {
			if(crt->flagIsSet(M_NO_PREFIX))
				strcpy(str, "");
			else {
				ch = low(crt->name[0]);
				if(ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' ||
				        ch == 'u')
					strcpy(str, "an ");
				else
					strcpy(str, "a ");
			}
			strcat(str, crt->name);
		} else {
			strcpy(str, int_to_text(num));
			strcat(str, " ");
			strcat(str, crt->name);
			if(crt->flagIsSet(M_REN_ON_PLURAL)) {
				strcat(str, "ren");
			} else if(crt->flagIsSet(M_MAN_TO_MEN)) {
				str[strlen(str)-2] = 'e';
			} else if(!crt->flagIsSet(M_NO_S_ON_PLURAL)) {
				str[strlen(str)+1] = 0;
				str[strlen(str)+2] = 0;
				if(str[strlen(str)-1] == 's' || str[strlen(str)-1] == 'x') {
					str[strlen(str)] = 'e';
					str[strlen(str)] = 's';
				} else {
					if(crt->flagIsSet(M_F_TO_VE_ON_PLURAL)) {
						if(str[strlen(str)-2] == 'f' && str[strlen(str)-1] == 'e') {
							strcat(str, "s");
							str[strlen(str)-3] = 'v';
						} else {
							strcat(str, "s");
							str[strlen(str)-2] = 'v';
							str[strlen(str)-1] = 'e';
							str[strlen(str)] = 's';
						}
					} else if(crt->flagIsSet(M_Y_TO_IE_ON_PLURAL)) {
						strcat(str, "s");
						str[strlen(str)-2] = 'i';
						str[strlen(str)-1] = 'e';
						str[strlen(str)] = 's';
					} else
						str[strlen(str)] = 's';
				}
			}
		}
		if(crt->flagIsSet(M_IRREGULAR_PLURAL) && num > 1) {
			found = 0;

			sprintf(pfile, "%s/plurals.txt", GAMEPATH);

			plural = fopen(pfile, "r");
			if(plural != NULL) {
				while(!found && !(feof (plural))) {
					fflush(plural);
					// get singular form
					fgets(sform, sizeof(sform), plural);
					sform[strlen(sform)-1] = 0;
					if(sform[strlen(sform)-1] == '\r')
						sform[strlen(sform)-1] = 0;
					fflush(plural);
					// get plural form
					fgets(pform, sizeof(pform), plural);
					pform[strlen(pform)-1] = 0;
					if(pform[strlen(pform)-1] == '\r')
						pform[strlen(pform)-1] = 0;

					if(strcmp(crt->name, sform) == 0) {
						strcpy(str, "");
						if(num < 21)
							strcpy( str, int_to_text(num) );
						strcat( str, " " );
						strcat(str, pform);
						found = 1;
					}
				}
				fclose(plural);
			}
		}

		if(flag & CAP)
			str[0] = up(str[0]);
		// Target is magic, and viewer has detect magic on
		if((flag & MAG) && crt->isMonster() && crt->flagIsSet(M_CAN_CAST))
			strcat(str, " (M)");


		return(str);
	}
}

//*********************************************************************
//						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] == '\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, char *str ) {
	char	buf[FBUF+1];
	int	i, l, n, ff, line;
	long	offset;

	buf[FBUF] = 0;
	switch(sock->getParam()) {
	case 1:
		offset = 0L;
		strcpy(sock->tempstr[1], str);
		ff = open(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->printColor("%s\n", &buf[l]);
					offset += (i-l+1);
					l = i+1;
				}
				if(line > 20)
					break;
			}
			if(line > 20) {
				sprintf(sock->tempstr[0], "%ld", offset);
				break;
			} else if(l != n) {
				sock->printColor("%s", &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[0]);
		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->printColor("%s\n", &buf[l]);
					offset += (i-l+1);
					l = i+1;
				}
				if(line > 20)
					break;
			}
			if(line > 20) {
				sprintf(sock->tempstr[0], "%ld", offset);
				break;
			} else if(l != n) {
				sock->printColor("%s", &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, char *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	800

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

	buf[FBUF] = 0;
	{
		offset = 0L;
		strcpy(sock->tempstr[1], str);
		ff = open(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); // nothing to put into (%m)?
			}
			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;
					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) {
				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, char *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);
		if((ff = fopen(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[0]);
		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[0], "%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, char *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);
}

void clean_str(char *str, int strip_count ) {
	char	str_buf[2048];
	char	*pnew;
	char	*porg;
	int		nPlusCount;
	int		ndx;

	pnew = str_buf;
	porg = str;
	nPlusCount = 0;

	// strip strip_count words from the beginning
	for( ndx = 0; ndx < strip_count; ndx++ ) {
		/* strip leading space */
		while( *porg != '\0' && *porg == ' ')
			porg++;

		/* skip word */
		while( *porg != '\0' && *porg != ' ')
			porg++;
	}

	// strip spaces after last stripped word
	while( *porg != '\0' && *porg == ' ')
		porg++;

	// copy the rest of the string in to the clean buffer
	// removing offensive chars
	while( *porg != '\0' ) {
		switch( *porg ) {
		case '+':
			nPlusCount++;
			if( nPlusCount < 3 ) {
				*(pnew++) = *porg;
			}
			break;
		default:
			nPlusCount = 0;
			*(pnew++) = *porg;
			break;
		}
		porg++;
	}

	*pnew = '\0';

	// now copy it back into the original buffer
	strcpy(str, str_buf );
}

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

int isdm(char *player) {
	char	**s = dmname;
	while(*s) {
		if(0 == strcmp(*s, player))
			return(1);
		s++;
	}
	return(0);
}
//*********************************************************************
//						smashInvis
//*********************************************************************
int Creature::smashInvis() {
	if(isPlayer()) {
		unhide();
		removeEffect("invisibility");
		unmist();
	}
	return(0);
}

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

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

	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", CONFPATH);
	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() {
	std::pair<bstring, Player*> p;
	Player* player=0;
	int		idle=0;
	long	t = time(0);

	foreach(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/player.bug/%s.txt", LOGPATH, name);
	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 autosplit(Creature* player, long amount) {
	ctag	*cp=0;
	int		remain=0, split=0, party=0;
	Creature *leader=0;

	if(player->isStaff())
		return(0);
	if(!player->ableToDoCommand())
		return(0);

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

	if(player->following)
		leader = player->following;  // Find the leader.
	else
		leader = player;

	cp = leader->first_fol;

	party = 1; // The leader.
	// Staff members are not included in the split
	while(cp) {
		if(cp->crt->isPlayer() && !cp->crt->isStaff()) // Count up how many in group.
			party += 1;
		cp = cp->next_tag;
	}

	// 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.



	if(leader == player) {	// Player picking up gets the odd coins.
		leader->print("You received %d gold as your split.\n", split+remain);
		leader->coins.add(split+remain, GOLD);
	} else {
		leader->print("You received %d gold as your split from %N.\n", split, player);
		leader->coins.add(split, GOLD);
		leader->print("You now have %d gold coins.\n", leader->coins[GOLD]);
	}

	cp = leader->first_fol;
	while(cp) {
		if(!cp->crt->isPet() && cp->crt != player && !cp->crt->isStaff()) {
			cp->crt->print("You received %d gold as your split from %N.\n", split, player);
			cp->crt->coins.add(split, GOLD);
			cp->crt->print("You now have %d gold coins.\n", cp->crt->coins[GOLD]);
		}

		if(cp->crt == player) {
			cp->crt->print("You received %d gold as your split.\n", split+remain);
			cp->crt->coins.add(split+remain, GOLD);
		}

		cp = cp->next_tag;
	}

	return(1);
}

//*********************************************************************
//						in_group
//*********************************************************************

int in_group(Creature* player, char *name) {
	ctag		*cp=0;
	Creature	*leader=0, *creature=0;

	if(player->following)
		leader = player->following;
	else
		leader = player;
	cp = leader->first_fol;

	while(cp) {
		creature = cp->crt;
		if(creature->isPet()) {
			cp=cp->next_tag;
			continue;
		}
		if(!strcmp(name, creature->name) && (player != creature) ) {
			return(1);
		}
		cp = cp->next_tag;
	}

	return(0);
}

//*********************************************************************
//						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;
	ctag	*cp = player->getRoom()->first_mon;

	while(cp) {
		if(cp->crt->getMonster()->isEnmCrt(player->name))
			count++;
		cp = cp->next_tag;
	}

	return(count);
}

//*********************************************************************
//						numInGroup
//*********************************************************************

int numInGroup(Creature* player) {
	int		count=0;
	ctag	*cp=0;
	Creature *leader=0;

	if(player->following)
		leader = player->following;
	else
		leader = player;

	cp = leader->first_fol;
	while(cp) {
		if(player->inSameRoom(cp->crt))
			count++;

		cp = cp->next_tag;
	}

	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;
	//  	char *name=0;
	//        str = str;

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

	for(i = 0; i < n; i++) {
		if(str[i] == '/') {
			str[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);
}

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

bool findTarget(Creature * player, int findWhere, int findFlags, char *str, int val, void** target, int* targetType) {
	int	match=0;
	bool found=false;
	do {
		if(findWhere & FIND_OBJ_INVENTORY) {
			if(findObj(player, player->first_obj, findFlags, str, val, &match, (Object**)target)) {
				*targetType = OBJECT;
				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(!player->ready[n])
					continue;
				if(keyTxtEqual(player->ready[n], str))
					match++;
				else
					continue;
				if(val == match) {
					*targetType = OBJECT;
					*target = player->ready[n];
					found = true;
					break;
				}
            }
            if(found)
            	break;
		}

		if(findWhere & FIND_OBJ_ROOM) {
			if(findObj(player, player->getRoom()->first_obj, findFlags, str, val, &match, (Object**)target)) {
				*targetType = OBJECT;
				found = true;
				break;
			}
		}

		if(findWhere & FIND_MON_ROOM) {
			if(findCrt(player, player->getRoom()->first_mon, findFlags, str, val, &match, (Creature **)target)) {
				*targetType = MONSTER;
				found = true;
				break;
			}
		}

		if(findWhere & FIND_PLY_ROOM) {
			if(findCrt(player, player->getRoom()->first_ply, findFlags, str, val, &match, (Creature **)target)) {
				*targetType = PLAYER;
				found = true;
				break;
			}
		}
	} while(0);

	return(found);
}

//**********************************************************************
//						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);
}