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/
/*
 * player2.cpp
 *	 Player routines.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2009 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
#include <math.h>
#include "mud.h"
#include "login.h"
#include "commands.h"

void Player::calcStats(vstat sendStat, vstat *toStat) {
	int			i=0, levels = 0, index, switchNum=0;
	int			cls=0, cls2=0, lvl=0, r=0;
	int			hptemp=0,mptemp=0;

	// stats are 1 based, the array is 0 based, so give us extra room
	int		num[MAX_STAT+1];



	if(sendStat.race)
		r = tstat.race;
	else
		r = race;

	if(sendStat.cls)
		cls = tstat.cls;
	else
		cls = cClass;

	if(sendStat.cls2)
		cls2 = tstat.cls2;
	else
		cls2 = cClass2;

	if(sendStat.level)
		lvl = tstat.level;
	else
		lvl = level;


	for(i=1; i<=MAX_STAT; i++) {
		num[i] = sendStat.num[i-1];
		num[i] += gConfig->getRace(r)->getStatAdj(i);
		num[i] = MAX(1, num[i]) * 10;
	}


	// Now to adjust hit points accordingly

	if(!cls2) {
		hptemp = class_stats[(int)cls].hpstart;
		mptemp = class_stats[(int)cls].mpstart;
	} else {
		hptemp = (class_stats[(int)cls].hpstart + class_stats[(int)cls2].hpstart) / 2;
		mptemp = (class_stats[(int)cls].mpstart + class_stats[(int)cls2].mpstart) / 2;
	}


	for(levels = 0; levels < lvl-1; levels ++) {
		index = levels % 10;

		if(!cls2)
			switchNum = level_cycle[(int)cls][index];
		else
			switchNum = multiStatCycle[getMultiClassID(cls, cls2)][index];

		num[switchNum] += 10;

		// Now to adjust hit points accordingly

		if(!cls2) {
			hptemp += class_stats[(int)cls].hp;
			mptemp += class_stats[(int)cls].mp;
		} else {
			hptemp += multiHpMpAdj[getMultiClassID(cls, cls2)][0];
			mptemp += multiHpMpAdj[getMultiClassID(cls, cls2)][1];
		}

		if(cls != LICH)	{
			if(cls == BERSERKER && num[CON] >= 70)
				hptemp++;
			if(num[CON] >= 130)
				hptemp++;
			if(num[CON] >= 210)
				hptemp++;
			if(num[CON] >= 250)
				hptemp++;
		}

		// liches gain an extra HP at every even level.
		if(levels % 2 == 0 && cls == LICH)
			hptemp++;
	}

	for(i=0; i<MAX_STAT; i++)
		toStat->num[i] = num[i+1];

	toStat->hp = hptemp;
	toStat->mp = mptemp;
}

bool Player::checkConfusion() {
	int		action=0, dmg=0;
	mType	targetType = PLAYER;
	char	atk[50];
	Creature* target=0;
	Exit	*newExit=0;
	BaseRoom* room = getRoom(), *bRoom=0;
	CatRef	cr;


	if(!isEffected("confusion"))
		return(false);

	action = mrand(1,4);
	switch(action) {
	case 1: // Stand confused

		broadcast(getSock(), room, "%M stands with a confused look on %s face.",
		              this, hisHer());
		printColor("^BYou are confused and dizzy. You stand and look around cluelessly.\n");
		stun(mrand(5,10));
		return(true);
		break;
	case 2: // Wander to random exit

		newExit = getFleeableExit();
		if(!newExit)
			return(false);
		bRoom = getFleeableRoom(newExit);
		if(!bRoom)
			return(false);

		printColor("^BWanderlust overtakes you.\n");
		printColor("^BYou wander aimlessly to the %s exit.\n", newExit->name);
		broadcast(getSock(), room, "%M wanders aimlessly to the %s exit.", this, newExit->name);
		deleteFromRoom();
		addToRoom(bRoom);
		doPetFollow();
		return(true);
		break;
	case 3: // Attack something randomly
		if(!checkAttackTimer(false))
			return(false);

		switch(mrand(1,2)) {
		case 1:
			target = getRandomMonster(room);
			if(!target)
				target = getRandomPlayer(room);
			if(target) {
				targetType = target->getType();
				break;
			}
			break;
		case 2:
			target = getRandomPlayer(room);
			if(!target)
				target = getRandomMonster(room);
			if(target) {
				targetType = target->getType();
				break;
			}
			break;
		default:
			break;
		}

		if(!target || target == this)
			targetType = INVALID;


		switch(targetType) {
		case PLAYER: // Random player in room
			printColor("^BYou are convinced %s is trying to kill you!\n", target->name);
			attackCreature(target);
			return(true);
			break;
		case INVALID: // Self

			if(ready[WIELD-1]) {
				dmg = ready[WIELD-1]->damage.roll() +
				      bonus((int)strength.getCur()) + ready[WIELD-1]->getAdjustment();
				printColor("^BYou frantically swing your weapon at imaginary enemies.\n");
			} else {
				printColor("^BYou madly flail around at imaginary enemies.\n");
				if(cClass == MONK) {
					dmg = mrand(1,2) + level/3 + mrand(1,(1+level)/2);
					if(strength.getCur() < 90) {
						dmg -= (90-strength.getCur())/10;
						dmg = MAX(1,dmg);
					}
				} else
					dmg = damage.roll();
			}

			getDamageString(atk, this, ready[WIELD - 1]);

			printColor("^BYou %s yourself for %d damage!\n", atk, dmg);

			broadcast(getSock(), room, "%M %s %sself!", this, atk, himHer());
			hp.decrease(dmg);
			if(hp.getCur() < 1) {

				hp.setCur(1);

				printColor("^BYou accidentally killed yourself!\n");
				broadcast("### Sadly, %s accidentally killed %sself.", name, himHer());

				mp.setCur(1);

				if(!inJail()) {
					bRoom = getLimboRoom().loadRoom(this);
					if(bRoom) {
						deleteFromRoom();
						addToRoom(bRoom);
						doPetFollow();
					}
				}
			}
			return(true);
			break;
		default: // Random monster in room.
			if(target->flagIsSet(M_UNKILLABLE))
				return(false);

			printColor("^BYou think %s is attacking you!\n", target);
			broadcast(getSock(), room, "%M yells, \"DIE %s!!!\"\n", this, target->name);
			attackCreature(target);
			return(true);
			break;
		}
		break;
	case 4: // flee
		printColor("^BPhantom dangers in your head beckon you to flee!\n");
		broadcast(getSock(), room, "%M screams in terror, pointing around at everything.", this);
		flee();
		return(true);
		break;
	default:
		break;
	}
	return(false);
}


Monster* Player::getPet() const {
	Monster* pet=0;
	ctag* fp=0;

	fp = first_fol;
	if(!fp)
		return(0);
	while(fp) {
		if(fp->crt->isPet())
			pet = fp->crt->getMonster();
		if(pet && pet->following == this)
			return(pet);
		fp = fp->next_tag;
	}

	return(0);
}
void Player::doFollow() {
	ctag* cp=0;

	cp = first_fol;
	while(cp) {
		Player* pFollow = cp->crt->getPlayer();
		Monster* mFollow = cp->crt->getMonster();
		if(cp->crt->getRoom() != getRoom()) {
			if(pFollow) {
				pFollow->deleteFromRoom();
				pFollow->addToRoom(getRoom());
			} else {
				mFollow->deleteFromRoom();
				mFollow->addToRoom(getRoom());
			}
		}
		cp = cp->next_tag;
	}

}
void Player::doPetFollow() {
	Monster *pet = getPet();

	if(pet && pet->getRoom() != getRoom()) {
		pet->deleteFromRoom();
		pet->addToRoom(getRoom());
	}
	if(alias_crt && alias_crt->getRoom() != getRoom()) {
		alias_crt->deleteFromRoom();
		alias_crt->addToRoom(getRoom());
	}
}

CatRef getEtherealTravelRoom() {
	const CatRefInfo* eth = gConfig->getCatRefInfo("et");
	CatRef cr;
	cr.setArea("et");
	cr.id = mrand(1, eth->teleportWeight);
	return(cr);
}

void etherealTravel(Player* player) {
	Room	*newRoom=0;
	CatRef	cr = getEtherealTravelRoom();

	if(!loadRoom(cr, &newRoom))
		return;

	player->deleteFromRoom();
	player->addToRoom(newRoom);
	player->doPetFollow();
}

//*********************************************************************
//						cmdSurname
//*********************************************************************

int cmdSurname(Player* player, cmd* cmnd) {
	int		nonalpha=0;
	unsigned int i=0;
	bool	illegalNonAlpha=false;

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

	if(player->flagIsSet(P_NO_SURNAME)) {
		player->print("You have lost the privelage of choosing a surname.\n");
		return(0);
	}

	if(player->flagIsSet(P_CHOSEN_SURNAME) && !player->isCt()) {
		player->print("You've already chosen your surname.\n");
		return(0);
	}

	if(player->getLevel() < SURNAME_LEVEL && !player->isStaff()) {
		player->print("You must be level %d to choose a surname.\n", SURNAME_LEVEL);
		return(0);
	}

	if(cmnd->num < 2) {
		player->print("Syntax: surname <surname>\n");
		return(0);
	}


	if(strlen(cmnd->str[1]) > 14) {
		player->print("Your surname may only be a max of 14 characters long.\n");
		return(0);
	}

	if(strlen(cmnd->str[1]) < 3) {
		player->print("Your surname must be at least 3 characters in length.\n");
		return(0);
	}

	for(i=0; i< strlen(cmnd->str[1]); i++) {
		if(!isalpha(cmnd->str[1][i])) {
			nonalpha++;
			if(cmnd->str[1][i] != '\'' && cmnd->str[1][i] != '-')
				illegalNonAlpha=true;
		}

	}

	if(illegalNonAlpha) {
		player->print("The only non-alpha characters allowed in surnames are ' and -.\n");
		return(0);
	}


	if(nonalpha && strlen(cmnd->str[1]) < 6) {
		player->print("Your surname must be at least 6 characters in order to contain a - or '.\n");
		return(0);
	}


	if(nonalpha > 1) {
		player->print("Your surname may not have more than one non-alpha character.\n");
		return(0);
	}

	for(i=0; i < strlen(cmnd->str[1]); i++) {
		if(!isalpha(cmnd->str[1][i]) && cmnd->str[1][i] != '\'' && cmnd->str[1][i] != '-') {
			player->print("Your surname must be alphabetic.\n");
			player->print("It may only contain the non-alpha characters ' and -.\n");
			return(0);
		}
	}

	if(cmnd->str[1][0] == '\'' || cmnd->str[1][0] == '-') {
		player->print("The first character of your surname must be a letter.\n");
		return(0);
	}

	if(cmnd->str[1][strlen(cmnd->str[1])-1] == '\'' || cmnd->str[1][strlen(cmnd->str[1])-1] == '-' ||
	        cmnd->str[1][strlen(cmnd->str[1])-2] == '\'' || cmnd->str[1][strlen(cmnd->str[1])-2] == '-') {
		player->print("The last two characters of your surname must be letters.\n");
		return(0);
	}

	lowercize(cmnd->str[1], 1);

	player->setSurname(cmnd->str[1]);

	player->printColor("^WNote that profane or otherwise idiotic surnames, as well as\n");
	player->printColor("idiotic combinations of name and surname, will not be tolerated.^x\n\n");
	player->print("Your full name will be %s %s.\n", player->name, player->getSurname().c_str());
	player->print("Is this acceptable?(Y/N)?\n");

	player->getSock()->setState(CON_CONFIRM_SURNAME);
	return(0);
}


//*********************************************************************
//						doSurname
//*********************************************************************

void doSurname(Socket* sock, char *str) {
	if(low(str[0]) == 'y') {
		sock->print("You are now known as %s %s.\n", sock->getPlayer()->getName(), sock->getPlayer()->getSurname().c_str());
		if(!sock->getPlayer()->isStaff())
				broadcast("### %s is now known as %s %s.", sock->getPlayer()->getName(), sock->getPlayer()->getName(),
					sock->getPlayer()->getSurname().c_str());

		sock->getPlayer()->setFlag(P_CHOSEN_SURNAME);
	} else
			sock->print("Aborted.\n");

	sock->setState(CON_PLAYING);
}


//********************************************************************
//						remFromGroup
//********************************************************************
// if player is in a group, they will be removed from it.

void remFromGroup(Creature* player) {
	ctag            *cp=0, *prev=0;
	Creature *leader=0;

	if(!player->following)
			return;

	leader = player->following;

	cp = leader->first_fol;
	if(cp->crt == player) {
			leader->first_fol = cp->next_tag;
			delete cp;
	} else {
			while(cp) {
					if(cp->crt == player) {
							prev->next_tag = cp->next_tag;
							delete cp;
							break;
					}
					prev = cp;
					cp = cp->next_tag;
			}

	}
	player->following = 0;
}

//********************************************************************
//						cmdVisible
//********************************************************************

int cmdVisible(Player* player, cmd* cmnd) {
	if(!player->isInvisible()) {
		player->print("You are not invisible.\n");
		return(0);
	} else {
		player->removeEffect("invisibility");
		player->removeEffect("greater-invisibility");
	}
	return(0);
}

//********************************************************************
//						cmdDice
//********************************************************************

int cmdDice(Creature* player, cmd* cmnd) {
	char    *str=0, *tok=0, diceOutput[256], add[256];
	int             strLen=0, i=0;
	int             diceSides=0,diceNum=0,diceAdd=0;
	int             rolls=0, total=0;

	const char *Syntax =	"\nSyntax: dice 1d2\n"
					"        dice 1d2+3\n";

	strcpy(diceOutput, "");
	strcpy(add ,"");

	strLen = strlen(cmnd->fullstr);

	// This kills all leading whitespace
	while(i<strLen && isspace(cmnd->fullstr[i]))
		i++;
	// This kills the command itself
	while(i<strLen && !isspace(cmnd->fullstr[i]))
		i++;

	str = strstr(&cmnd->fullstr[i], "d");
	if(!str)
		return(action(player, cmnd));

	str = strdup(&cmnd->fullstr[i]);
	if(!str) {
		player->print(Syntax);
		return(0);
	}

	tok = strtok(str, "d");
	if(!tok) {
		player->print(Syntax);
		return(0);
	}
	diceNum = atoi(tok);

	tok = strtok(NULL, "+");
	if(!tok) {
		player->print(Syntax);
		return(0);
	}
	diceSides = atoi(tok);

	tok = strtok(NULL, "+");

	if(tok)
		diceAdd = atoi(tok);

	if(diceNum < 0) {
		player->print("How can you roll a negative number of dice?\n");
		return(0);
	}

	diceNum = MAX(1, diceNum);

	if(diceSides<2) {
		player->print("A die has a minimum of 2 sides.\n");
		return(0);
	}

	diceNum = MIN(100, diceNum);
	diceSides = MIN(100, diceSides);
	diceAdd = MAX(-100,MIN(100, diceAdd));


	sprintf(diceOutput, "%dd%d", diceNum, diceSides);
	if(diceAdd) {
		if(diceAdd > 0)
			sprintf(add, "+%d", diceAdd);
		else
			sprintf(add, "%d", diceAdd);
		strcat(diceOutput, add);
	}


	for(rolls=0;rolls<diceNum;rolls++)
		total += mrand(1, diceSides);

	total += diceAdd;


	player->print("You roll %s\n: %d\n", diceOutput, total);
	broadcast(player->getSock(), player->getRoom(), "(Dice %s): %M got %d.", diceOutput, player, total );

	return(0);
}


//********************************************************************
//						plyChooseAlignment
//********************************************************************

int plyChooseAlignment(Player* player, cmd* cmnd) {
	char syntax[] = "Syntax: alignment lawful\n"
	                "        alignment chaotic\n"
	                "Note: Tieflings must be chaotic.\n\n";

	if(player->isStaff() && !player->isCt())
		return(0);

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

	if(player->flagIsSet(P_CHOSEN_ALIGNMENT)) {
		player->print("You have already chosen your alignment.\n");
		if(player->flagIsSet(P_CHAOTIC))
			player->print("In order to convert to lawful, use the 'convert' command. HELP CONVERT.\n");
		return(0);
	} else if(player->getLevel() < ALIGNMENT_LEVEL) {
		player->print("You cannot choose your alignment until level %d.\n", ALIGNMENT_LEVEL);
		return(0);
	} else if(player->getLevel() > ALIGNMENT_LEVEL) {
		player->print("Your alignment has already been chosen.\n");
		return(0);
	}

	if(cmnd->num < 2) {
		player->print(syntax);
		return(0);
	}


	if(!strcasecmp(cmnd->str[1], "lawful")) {
		if(player->getRace() == TIEFLING) {
			player->print("Tieflings are required to be chaotic.\n\n");
		} else {
			broadcast("^B### %s chooses to adhere to the order of LAW!", player->getName());
			player->setFlag(P_CHOSEN_ALIGNMENT);
		}
	} else if(!strcasecmp(cmnd->str[1], "chaotic")) {
		broadcast("^R### %s chooses to embrace the whims of CHAOS!", player->getName());
		player->setFlag(P_CHAOTIC);
		player->setFlag(P_CHOSEN_ALIGNMENT);
	} else {
		player->print(syntax);
		return(0);
	}
	return(0);
}

//********************************************************************
//						plyHasObj
//********************************************************************
// This will check to see if a player has a specific object type
// either in their inventory or in a bag, or in their worn equipment.

bool plyHasObj(Creature* player, Object *item) {
	int		a=0;
	Object	*obj=0;
	Room* room=0;
	otag	*op=0, *cop=0;


	//check inventory
	op = player->first_obj;
	while(op) {

		obj = op->obj;
		if(*&obj->info == *&item->info)
			return(true);

		// if item is a bag, check in bag
		if(obj->getType() == CONTAINER) {
			cop = obj->first_obj;
			while(cop) {
				if(*&cop->obj->info == *&item->info)
					return(true);
				cop = cop->next_tag;
			}
		}

		op = op->next_tag;

	}
	// check worn equipment
	for(a=0;a<MAXWEAR;a++) {

		if(!player->ready[a])
			continue;

		obj = player->ready[a];
		if(*&obj->info == *&item->info && obj != item)
			return(true);

		// if worn item is a bag, check in bag
		if(obj->getType() == CONTAINER) {
			cop = obj->first_obj;
			while(cop) {
				if(*&cop->obj->info == *&item->info && cop->obj != item)
					return(true);
				cop = cop->next_tag;
			}
		}


	}

	// check player's storage room, if it exists
	if(player->isPlayer()) {
		CatRef	sr = gConfig->getSingleProperty(player->getPlayer(), PROP_STORAGE);
		if(sr.id < 1 || !loadRoom(sr, &room))
			return(0);

		op = room->first_obj;
		while(op) {
			if(op->obj->getType() == CONTAINER) {
				cop = op->obj->first_obj;
				while(cop) {
					if(*&cop->obj->info == *&item->info && cop->obj != item)
						return(true);
					cop = cop->next_tag;
				}
			}

			if(*&op->obj->info == *&item->info && op->obj != item)
				return(true);
			op = op->next_tag;
		}

	}


	return(false);
}