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/
/*
 * rogues.c
 *   Functions that deal with rouge like abilities
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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 "mud.h"
#include "commands.h"
#include "dm.h"
#include "move.h"
#include "factions.h"
#include "calendar.h"


//*********************************************************************
//						cmdPrepareForTraps
//*********************************************************************
// This function allows players to prepare themselves for traps that
// might be in the next room they enter.  This way, they can hope to
// avoid a trap that they already know is there.

int cmdPrepareForTraps(Player* player, cmd* cmnd) {
	long	i=0, t = time(0);


	player->clearFlag(P_AFK);

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

	if(player->flagIsSet(P_PREPARED)) {
		player->print("You've already prepared.\n");
		return(0);
	}


	if(player->inCombat()) {
		player->print("You are too busy trying to keep from dying.\n");
		return(0);
	}

	i = LT(player, LT_PREPARE);

	if(t < i) {
		player->pleaseWait(i-t);
		return(0);
	}

	player->lasttime[LT_PREPARE].ltime = t;
	player->lasttime[LT_PREPARE].interval = player->isDm() ? 0:15;

	player->print("You prepare yourself for traps.\n");
	broadcast(player->getSock(), player->getRoom(), "%M prepares for traps.", player);
	player->setFlag(P_PREPARED);
	if(player->isBlind())
		player->clearFlag(P_PREPARED);

	return(0);
}

//*********************************************************************
//						cmdBribe
//*********************************************************************
// This function allows players to bribe monsters. If they give the
// monster enough money, it will leave the room. If not, the monster
// keeps the money and stays.

int cmdBribe(Player* player, cmd* cmnd) {
	Monster *creature=0;
	unsigned long amount=0;
	Money cost;

	player->clearFlag(P_AFK);

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

	if(player->getClass() == BUILDER) {
		player->print("You cannot do that.\n");
		return(0);
	}
	if(cmnd->num < 2) {
		player->print("Bribe whom?\n");
		return(0);
	}
	if(cmnd->num < 3 || cmnd->str[2][0] != '$') {
		player->print("Syntax: bribe <monster> $<amount>\n");
		return(0);
	}

	creature = player->getRoom()->findMonster(player, cmnd);
	if(!creature) {
		player->print("That is not here.\n");
		return(0);
	}

	if(!Faction::willDoBusinessWith(player, creature->getPrimeFaction())) {
		player->print("%M refuses to do business with you.\n", creature);
		return(0);
	}

	if(creature->isPet()) {
		player->print("%M is too loyal to %s for you to bribe %s.\n", creature,
			creature->following->name, creature->himHer());
		creature->following->print("%M tried to bribe %N.\n", player, creature);
		return(0);
	}

	amount = strtoul(&cmnd->str[2][1], 0, 0);
	if(amount < 1 || amount > player->coins[GOLD]) {
		player->print("Please enter the amount of the bribe.\n");
		return(0);
	}

	cost.set(creature->getExperience()*50, GOLD);
	cost = Faction::adjustPrice(player, creature->getPrimeFaction(), cost, true);

	player->coins.sub(cost);

	if(amount < cost[GOLD] || creature->flagIsSet(M_PERMENANT_MONSTER)) {
		player->print("%M takes your money, but stays.\n", creature);
		broadcast(player->getSock(), player->getRoom(), "%M tried to bribe %N.", player, creature);
		creature->coins.add(cost);
	} else {
		player->print("%M takes your money and leaves.\n", creature);
		broadcast(player->getSock(), player->getRoom(), "%M bribed %N.", player, creature);

		log_immort(true, player, "%s bribed %s.\n", player->name, creature->name);

		creature->deleteFromRoom();
		gServer->delActive(creature);
		free_crt(creature);
	}

	return(0);
}


//*********************************************************************
//						cmdGamble
//*********************************************************************
// Code for people to gamble money

int cmdGamble(Player* player, cmd* cmnd) {
	if(!player->getRoom()->flagIsSet(R_CASINO) && !player->isCt()) {
		player->print("You can't gamble here.\n");
		return(0);
	}

	player->unhide();

	player->print("Not implemented yet.\n");
	return(0);
}

//*********************************************************************
//						cmdSearch
//*********************************************************************
// This function allows a player to search a room for hidden objects,
// exits, monsters and players.

int cmdSearch(Player* player, cmd* cmnd) {
	BaseRoom* room = player->getRoom();
	xtag	*xp=0;
	otag	*op=0;
	ctag	*cp=0;
	long	i=0, t = time(0);
	int		chance=0;
	bool	found=false, detectMagic = player->isEffected("detect-magic");

	player->clearFlag(P_AFK);

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

	if(player->isBlind()) {
		player->print("You're blind! How can you do that?\n");
		return(0);
	}

	int level = (int)(player->getSkillLevel("search"));

	// TODO: SKILLS: tweak the formula
	chance = 15 + 5*bonus((int)player->piety.getCur()) + level*2;
	chance = MIN(chance, 90);
	if(player->getClass() == RANGER)
		chance += level*8;
	if(player->getClass() == WEREWOLF)
		chance += level*7;
	if(player->getClass() == DRUID)
		chance += level*6;

	if(player->getClass() == CLERIC && (
		(player->getDeity() == KAMIRA && player->getAdjustedAlignment() >= NEUTRAL) ||
		(player->getDeity() == ARACHNUS && player->alignInOrder())
	) )
		chance += level*3;

	if(player->isStaff())
		chance = 100;

	i = LT(player, LT_SEARCH);

	if(t < i) {
		player->pleaseWait(i-t);
		return(0);
	}

	player->unhide();

	player->lasttime[LT_SEARCH].ltime = t;
	if( player->isDm() )
		player->lasttime[LT_SEARCH].interval = 0;
	else if(player->getClass() == RANGER || player->getClass() == WEREWOLF)
		player->lasttime[LT_SEARCH].interval = 3;
	else if((player->getClass() == CLERIC && player->getDeity() == KAMIRA && player->getAdjustedAlignment() >= NEUTRAL) || player->getClass() == THIEF || player->getClass() == ROGUE)
		player->lasttime[LT_SEARCH].interval = 5;
	else
		player->lasttime[LT_SEARCH].interval = 7;

	xp = room->first_ext;
	while(xp) {
		if(	(xp->ext->flagIsSet(X_SECRET) && mrand(1,100) <= (chance + searchMod(xp->ext->getSize()))) ||
			(xp->ext->isConcealed(player) && mrand(1,100) <= 5)
		) {
			// canSee doesnt handle DescOnly
			if(player->canSee(xp->ext) && !xp->ext->flagIsSet(X_DESCRIPTION_ONLY)) {
				found = true;
				player->print("You found an exit: %s.\n", xp->ext->name);

				if(xp->ext->isWall("wall-of-fire"))
					player->printColor("%s", xp->ext->blockedByStr('R', "wall of fire", "wall-of-fire", detectMagic, true).c_str());
				if(xp->ext->isWall("wall-of-force"))
					player->printColor("%s", xp->ext->blockedByStr('s', "wall of force", "wall-of-force", detectMagic, true).c_str());
				if(xp->ext->isWall("wall-of-thorns"))
					player->printColor("%s", xp->ext->blockedByStr('o', "wall of thorns", "wall-of-thorns", detectMagic, true).c_str());
			}
		}
		xp = xp->next_tag;
	}

	op = room->first_obj;
	while(op) {
		if(	op->obj->flagIsSet(O_HIDDEN) &&
			player->canSee(op->obj) &&
			mrand(1,100) <= (chance + searchMod(op->obj->getSize()))
		) {
			found = true;
			player->printColor("You found %1P.\n", op->obj);
		}
		op = op->next_tag;
	}

	cp = room->first_ply;
	while(cp) {
		if(	cp->crt->flagIsSet(P_HIDDEN) &&
			player->canSee(cp->crt) &&
			mrand(1,100) <= (chance + searchMod(cp->crt->getSize()))
		) {
			found = true;
			player->print("You found %s hiding.\n", cp->crt->name);
		}
		cp = cp->next_tag;
	}

	cp = room->first_mon;
	while(cp) {
		if(	cp->crt->flagIsSet(M_HIDDEN) &&
			player->canSee(cp->crt) &&
			mrand(1,100) <= (chance + searchMod(cp->crt->getSize()))
		) {
			found = true;
			player->print("You found %1N hiding.\n", cp->crt);
		}
		cp = cp->next_tag;
	}

	// reuse chance as chance to find herbs
	// make it a 15-85% scale, range from 1-MAXALVL*10
	chance = level * (MAXALVL*10 / (85-15)) + 15;

	if(player->isStaff())
		chance = 100;

	if(chance >= mrand(1,100)) {
		if(player->area_room && player->area_room->spawnHerbs()) {
			player->print("You found some herbs!\n");
			found = true;
		}
	}

	broadcast(player->getSock(), room, "%M searches the room.", player);

	if(found) {
		broadcast(player->getSock(), room, "%s found something!", player->upHeShe());
		player->checkImprove("search", true);
	} else {
		player->print("You didn't find anything.\n");
		player->checkImprove("search", false);
	}

	return(0);
}

//*********************************************************************
//						spawnHerbs
//*********************************************************************

bool AreaRoom::spawnHerbs() {
	if(!area)
		return(false);
	const TileInfo* tile = area->getTile(area->getTerrain(0, &mapmarker, 0, 0, 0, true), area->getSeasonFlags(&mapmarker));
	if(!tile)
		return(false);
	return(tile->spawnHerbs(this));
}

//*********************************************************************
//						spawnHerbs
//*********************************************************************

bool TileInfo::spawnHerbs(BaseRoom* room) const {
	Object*	object=0;
	otag*	op = room->first_obj;
	int		max = herbs.size();
	short	num = mrand(1,MAX(1,max)), i=0, n=0, k=0;
	std::list<CatRef>::const_iterator it;
	bool	found=false;

	if(!max)
		return(false);

	// don't spawn if there's already herbs here
	while(op) {
		if(op->obj->flagIsSet(O_DISPOSABLE))
			return(false);
		op = op->next_tag;
	}

	for(; i<num; i++) {
		k = 1;
		n = mrand(1, max);

		for(it = herbs.begin() ; it != herbs.end() ; it++) {
			if(k++==n)
				break;
		}

		if(loadObject(*it, &object)) {
			found = true;
			object->setFlag(O_DISPOSABLE);
			object->addToRoom(room);
		}
	}

	return(found);
}

//*********************************************************************
//						cmdHide
//*********************************************************************
// This command allows a player to try and hide themself in the shadows
// or it can be used to hide an object in a room.

int cmdHide(Player* player, cmd* cmnd) {
	Object	*object=0;
	long	i = LT(player, LT_HIDE), t = time(0);;
	int		chance=0;

	player->clearFlag(P_AFK);

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

	if(!player->knowsSkill("hide")) {
		player->print("You try to hide but fail miserably!\n");
		return(0);
	}
	if(player->flagIsSet(P_SITTING)) {
		player->print("You cannot do that while sitting down.\n");
		return(0);
	}

	if(player->flagIsSet(P_MISTED)) {
		player->print("You are already hidden as a mist.\n");
		return(0);
	}

	if(i > t && !player->isCt()) {
		player->pleaseWait(i-t);
		return(0);
	}

	player->lasttime[LT_HIDE].ltime = t;
	player->lasttime[LT_HIDE].interval = (player->getClass() == THIEF ||
	           player->getClass() == ASSASSIN || player->getClass() == ROGUE || player->getClass() == RANGER) ? 5:15;


	if(player->getSecondClass() == THIEF || player->getSecondClass() == ASSASSIN || (player->getClass() == CLERIC && player->getDeity() == KAMIRA))
		player->lasttime[LT_HIDE].interval = 6L;

	int level = (int)player->getSkillLevel("hide");
	if(cmnd->num == 1) {
		switch(player->getClass()) {
		case THIEF:
			if(player->getSecondClass() == MAGE) {
				chance = MIN(90, 5 + 4*level + 3*bonus((int) player->dexterity.getCur()));
				player->lasttime[LT_HIDE].interval = 8;
			} else
				chance = MIN(90, 5 + 6*level + 3*bonus((int) player->dexterity.getCur()));
			break;
		case ASSASSIN:
			chance = MIN(90, 5 + 6*level + 3*bonus((int) player->dexterity.getCur()));
			break;
		case FIGHTER:
			if(player->getSecondClass() == THIEF)
				chance = MIN(90, 5 + 4*level + 3*bonus((int) player->dexterity.getCur()));
			else
				chance = MIN(90, 5 + 2*level + 3*bonus((int) player->dexterity.getCur()));
			break;
		case MAGE:
			if(player->getSecondClass() == ASSASSIN || player->getSecondClass() == THIEF) {
				chance = MIN(90, 5 + 4*level + 3*bonus((int) player->dexterity.getCur()));
				player->lasttime[LT_HIDE].interval = 8;
			} else
				chance = MIN(90, 5 + 2*level +
				             3*bonus((int) player->dexterity.getCur()));
			break;
		case CLERIC:
			if(player->getSecondClass() == ASSASSIN) {
				chance = MIN(90, 5 + 5*level + 3*bonus((int) player->dexterity.getCur()));
			} else if( (player->getDeity() == KAMIRA && player->getAdjustedAlignment() >= NEUTRAL) ||
				(player->getDeity() == ARACHNUS && player->alignInOrder())
			) {
				chance = MIN(90, 5 + 4*level + 3*bonus((int) player->piety.getCur()));
			} else
				chance = MIN(90, 5 + 2*level + 3*bonus((int) player->dexterity.getCur()));

			break;
		case RANGER:
		case DRUID:
			chance = 5 + 10*level + 3*bonus((int) player->dexterity.getCur());
			break;
		case ROGUE:
			chance = MIN(90, 5 + 5*level + 3*bonus((int) player->dexterity.getCur()));
			break;
		default:
			chance = MIN(90, 5 + 2*level + 3*bonus((int) player->dexterity.getCur()));
			break;
		}


		if(player->isStaff())
			chance = 101;


		if(player->isEffected("camouflage") && player->getRoom()->isOutdoors())
			chance += 20;

		if(	(player->getRoom()->flagIsSet(R_DARK_AT_NIGHT) && !isDay()) ||
			player->getRoom()->flagIsSet(R_DARK_ALWAYS) ||
			player->getRoom()->isEffected("dense-fog")
		)
			chance += 10;

		if(player->dexterity.getCur()/10 < 9 && !(player->getClass() == CLERIC && player->getDeity() == KAMIRA))
			chance -= 10*(9 - player->dexterity.getCur()/10); // Having less then average dex

		player->print("You attempt to hide in the shadows.\n");

		if((player->getClass() == RANGER || player->getClass() == DRUID) && !player->getRoom()->isOutdoors()) {
			chance /= 2;
			chance = MAX(25, chance);
			player->print("You have trouble hiding while inside.\n");
		}

		if(player->inCombat())
			chance = 0;

		if(player->isBlind())
			chance = MIN(chance, 20);

		if(mrand(1,100) <= chance || player->flagIsSet(P_MISTED)) {
			player->setFlag(P_HIDDEN);
			player->print("You slip into the shadows unnoticed.\n");
			player->checkImprove("hide", true);
		} else {
			player->unhide();
			broadcast(player->getSock(), player->getRoom(), "%M tries to hide in the shadows.", player);
			player->checkImprove("hide", false);
		}

		return(0);
	}

	object = findObject(player, player->getRoom()->first_obj, cmnd);

	if(!object) {
		player->print("You don't see that here.\n");
		return(0);
	}

	player->unhide();

	if(	object->flagIsSet(O_NO_TAKE) &&
		!player->checkStaff("You cannot hide that.\n")
	)
		return(0);

	if(isGuardLoot(player->getRoom(), player, "%M will not let you hide that.\n"))
		return(0);

	if(player->isDm())
		chance = 100;
	else if(player->getClass() == THIEF || player->getClass() == ASSASSIN || player->getClass() == ROGUE)
		chance = MIN(90, 10 + 5*level + 5*bonus((int) player->dexterity.getCur()));
	else if(player->getClass() == RANGER || player->getClass() == DRUID)
		chance = 5 + 9*level + 3*bonus((int) player->dexterity.getCur());
	else
		chance = MIN(90, 5 + 3*level + 3*bonus((int) player->dexterity.getCur()));



	player->print("You attempt to hide it.\n");
	broadcast(player->getSock(), player->getRoom(), "%M attempts to hide %1P.", player, object);

	if(mrand(1, 100) <= chance) {
		object->setFlag(O_HIDDEN);
		player->printColor("You tuck %1P into a corner.\n", object);
		player->checkImprove("hide", true);
	} else {
		object->clearFlag(O_HIDDEN);
		player->checkImprove("hide", false);
	}

	return(0);
}

//*********************************************************************
//						cmdScout
//*********************************************************************

int cmdScout(Player* player, cmd* cmnd) {
	BaseRoom* room=0;
	Exit	*exit=0;
	int		chance=0;
	long	t=0, i=0;

	player->clearFlag(P_AFK);

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

	if(!player->isStaff() && !player->knowsSkill("scout")) {
		player->print("You lack the training to properly scout exits.\n");
		return(0);
	}

	if(!player->isStaff() && player->isBlind()) {
		player->printColor("^CYou're blind!\n");
		return(0);
	}

	if(	player->getRoom()->isEffected("dense-fog") &&
		!player->checkStaff("This room is filled with a dense fog.\nYou cannot scout effectively.\n")
	)
		return(0);

	if(cmnd->num < 2) {
		player->print("Scout where?\n");
		return(0);
	}

	i = player->lasttime[LT_SCOUT].ltime + player->lasttime[LT_SCOUT].interval;
	t = time(0);

	if(!player->isStaff() && t < i) {
		player->pleaseWait(i - t);
		return(0);
	}

	exit = findExit(player, Move::formatFindExit(cmnd), cmnd->val[1], player->getRoom()->first_ext);


	if(!exit) {
		player->print("You don't see that exit.\n");
		player->lasttime[LT_SCOUT].ltime = t;
		player->lasttime[LT_SCOUT].interval = 10L;
		return(0);
	}


	if(	exit->flagIsSet(X_NO_SCOUT) &&
		!player->checkStaff("You were unable to scout it.\n"))
		return(0);

	Monster* guard = player->getRoom()->getGuardingExit(exit);
	if(guard && !player->checkStaff("%M won't let you scout there.\n", guard))
		return(0);

	player->lasttime[LT_SCOUT].ltime = t;
	player->lasttime[LT_SCOUT].interval = 20L;

	chance = 40 + (bonus((int) player->dexterity.getCur()) + (int)player->getSkillLevel("scout")) * 4;

	if(exit->isEffected("wall-of-fire"))
		chance -= 15;
	if(exit->isEffected("wall-of-thorns"))
		chance -= 15;

	chance = MIN(85, chance);

	if(!player->isStaff() && mrand(1, 100) > chance) {
		player->print("You fail scout in that direction.\n");
		player->checkImprove("scout", false);
		return(0);
	}

	if(	(exit->flagIsSet(X_LOCKED) || exit->flagIsSet(X_CLOSED) ) &&
		!player->checkStaff("You cannot scout through a closed exit.\n")
	)
		return(0);

	if(	exit->flagIsSet(X_NEEDS_FLY) &&
		!player->isEffected("fly") &&
		!player->checkStaff("You must fly to scout there.\n")
	)
		return(0);

	// can't scout where you can't go
	if(exit->target.mapmarker.getArea()) {
		Area *area = gConfig->getArea(exit->target.mapmarker.getArea());
		if(!area) {
			player->print("Off the map in that direction.\n");
			if(player->isStaff())
				player->printColor("^eArea does not exist.\n");
			return(0);
		}
		if(	!area->canPass(player, &exit->target.mapmarker, true) &&
			!player->checkStaff("You cannot scout that way.\n") )
			return(0);
	}

	AreaRoom *aRoom=0;
	Room	*uRoom=0;
	i=0;

	Move::getRoom(player, exit, &uRoom, &aRoom, true);

	if(aRoom)
		room = aRoom;
	else if(uRoom)
		room = uRoom;

	player->checkImprove("scout", true);
	player->print("You scout the %s exit.\n", exit->name);

	if(player->isStaff() && player->flagIsSet(P_DM_INVIS))
		broadcast(isStaff, player->getSock(), player->getRoom(), "%M scouts the %s exit.", player, exit->name);
	else if(exit->flagIsSet(X_SECRET) || exit->isConcealed() || exit->flagIsSet(X_DESCRIPTION_ONLY))
		broadcast(player->getSock(), player->getRoom(), "%M scouts the area.", player);
	else
		broadcast(player->getSock(), player->getRoom(), "%M scouts the %s exit.", player, exit->name);

	if(	room &&
		room->isEffected("dense-fog")&&
		!player->checkStaff("That room is filled with a dense fog.\n")
	)
		return(0);

	if(!room && exit->target.mapmarker.getArea()) {
		Area* area = gConfig->getArea(exit->target.mapmarker.getArea());
		if(!area) {
			player->print("Off the map in that direction.\n");
			if(player->isStaff())
				player->printColor("^eArea does not exist.\n");
			return(0);
		}
		// There is no room, but we're going to pretend there is.
		player->print("\n");
		if(area->name != "")
			player->printColor("%s%s^x\n\n",
				(!player->flagIsSet(P_NO_EXTRA_COLOR) && area->isSunlight(&exit->target.mapmarker) ? "^C" : "^c"),
				area->name.c_str());

		player->printColor("%s", area->showGrid(player, &exit->target.mapmarker, false).c_str());
		player->printColor("^g%s exits: north, east, south, west, northeast, northwest, southeast, southwest.^w\n\n",
			player->isStaff() ? "All" : "Obvious");

	} else if(room) {
		display_rom(player, room);
	} else {
		player->print("Off the map in that direction.\n");
		if(player->isStaff())
			player->printColor("^eRoom does not exist.\n");
	}
	return(0);
}

//*********************************************************************
//						cmdEnvenom
//*********************************************************************
// This will allow an assassin to put poison on a weapon and use it to
// poison other players and monsters. -- TC

int cmdEnvenom(Player* player, cmd* cmnd) {
	Object		*weapon=0, *object=0;

	player->clearFlag(P_AFK);

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

	if(!player->knowsSkill("envenom")) {
		player->print("You lack the training to envenom your weapons.\n");
		return(0);
	}

	if(player->isBlind()) {
		player->printColor("^CYou can't do that! You're blind!\n");
		return(0);
	}

	if(cmnd->num < 3) {
		player->print("Syntax: envenom (weapon) (poison)\n");
		return(0);
	}

	weapon = findObject(player, player->first_obj, cmnd);

	if(!weapon) {
		player->print("You do not have that weapon in your inventory.\n");
		return(0);
	}

	if(weapon->flagIsSet(O_ENVENOMED)) {
		player->printColor("%O is already envenomed.\n", weapon);
		return(0);
	}

	if(weapon->getShotscur() < 1) {
		player->print("You cannot envenom a broken weapon.\n");
		return(0);
	}

	bstring category = weapon->getWeaponCategory();
	if(category != "slashing" && category != "piercing"&&
		!player->checkStaff("You can only envenom slashing and piercing weapons.\n"))
		return(0);

	object = findObject(player, player->first_obj, cmnd, 2);

	if(!object) {
		player->print("You do not have that poison in your inventory.\n");
		return(0);
	}

	if(object->getType() != POISON) {
		player->print("That is not poison.\n");
		return(0);
	}

	if(object->getShotscur() < 1) {
		player->printColor("%O is all used up.\n", object);
		return(0);
	}

	player->printColor("You envenom %P with %P.\n", weapon, object);
	player->checkImprove("envenom", true);
	broadcast(player->getSock(), player->getRoom(), "%M envenoms %P with %P.", player, weapon, object);


	// TODO: make poison more powerful with better envenom skill
	object->decShotscur();
	weapon->setFlag(O_ENVENOMED);
	if(object->getEffect() != "")
		weapon->setEffect(object->getEffect());
	else
		weapon->setEffect("poison");
	weapon->setEffectDuration(object->getEffectDuration()); // Sets poison duration.
	weapon->setEffectStrength(object->getEffectStrength()); // Sets poison tick damage.

	/* Did this because some poisons are better then others, and assassins
	   will be buying the poison. The more expensive, the better it is. Having
	   them have to buy it makes it more of a pain for some idiot to go around
	   poisoning everyone with a small knife just for fun.                    */

	weapon->lasttime[LT_ENVEN].ltime = time(0);
	weapon->lasttime[LT_ENVEN].interval = 60*(mrand(3,5)+weapon->getAdjustment());

	return(0);
}


//*********************************************************************
//						cmdShoplift
//*********************************************************************
// This function will allow thieves to steal from shops.

int cmdShoplift(Player* player, cmd* cmnd) {
	Room	*room=0, *storage=0;
	Object	*object=0, *object2=0;
	ctag	*cp=0;
	Monster *tmp_crt=0;
	int		chance=0, guarded=0;


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

	if(!needUniqueRoom(player))
		return(0);

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

	if(player->getClass() == BUILDER)
		return(0);

	room = player->parent_rom;

	if(!player->isCt() && player->getLevel() < 7 && player->getClass() != THIEF) {
		player->print("You couldn't possibly succeed. Wait until level 7.\n");
		return(0);
	}

	if(player->getClass() == THIEF && player->getLevel() < 4) {
		player->print("You couldn't possibly succeed. Wait until level 4.\n");
		return(0);
	}

	if(player->getClass() == PALADIN) {
		player->print("Paladins do not allow themselves to fall to the level of petty theft.\n");
		return(0);
	}

	if(player->flagIsSet(P_BERSERKED)) {
		player->print("You can't shoplift while your going berserk! Go break something!\n");
		return(0);
	}

	if(!room->flagIsSet(R_SHOP)) {
		player->print("This is not a shop; there's nothing to shoplift.\n");
		return(0);
	}

	if(!room->flagIsSet(R_CAN_SHOPLIFT)) {
		player->print("Someone surely would see you if you tried that here.\n");
		return(0);
	}

	if(player->inCombat()) {
		player->print("You can't shoplift in the middle of combat!\n");
		return(0);
	}

	if(player->flagIsSet(P_MISTED)) {
		player->print("You can't shoplift while you are a mist.\n");
		return(0);
	}

//	player->print("Someone surely would see you if you tried that here.\n");
	if(	!loadRoom(shopStorageRoom(room), &storage) ||
		!storage->flagIsSet(R_SHOP_STORAGE)
	) {
		player->print("This is not a shop; there's nothing to shoplift.\n");
		return(0);
	}
	storage->addPermCrt();
	cp = storage->first_mon;
	while(cp) {
		if(cp->crt->getLevel() >= 10 && cp->crt->flagIsSet(M_PERMENANT_MONSTER))
			guarded++;
		cp = cp->next_tag;
	}

	// This is done in order that a catastrophic building mistake cannot be made.
	// If someone forgot to perm mobs in the storeroom, and there were no perms
	// in the shop itself to guard the items, then players would be able to steal
	// all they wanted from the shop all day long whenever they wanted to.

	if(guarded < 2) {
		player->print("Someone surely would see you if you tried that here.\n");
		return(0);
	}

	// This has to be done to prevent baiting.
	// The mobs will wander after a time.
	cp = room->first_mon;
	while(cp) {
		if(cp->crt->flagIsSet(M_ATTACKING_SHOPLIFTER) && !player->isDm()) {
			player->print("The shopkeep or shop's guards are too alert right now.\n");
			return(0);
		}
		cp = cp->next_tag;
	}


	if(cmnd->num < 2) {
		player->print("Shoplift what?\n");
		return(0);
	}

	object = findObject(player, storage->first_obj, cmnd);

	if(!object) {
		player->print("That item isn't on display.\n");
		return(0);
	}
	//	if(!strcmp(object->name, "lottery ticket"))
	//	{
	//		player->print("Shoplifting a lottery ticket would bring down the wrath of the gods.\n");
	//		return(0);
	//	}

	if(!strcmp(object->name, "storage room")) {
		player->print("You can't shoplift that!\n");
		return(0);
	}

	if(object->flagIsSet(O_NO_SHOPLIFT)) {
		player->print("That item is too well guarded for you to shoplift.\n");
		return(0);
	}

	if(object->getActualWeight() > 50) {
		player->print("That object is too heavy and bulky to shoplift.\n");
		return(0);
	}

	if(player->getWeight() + object->getActualWeight() > player->maxWeight()) {
		player->print("That would be too much for you to carry.\n");
		return(0);
	}

	player->unhide();

	switch (player->getClass()) {
	case THIEF:
	case CARETAKER:
	case DUNGEONMASTER:
		chance = ((player->getLevel()+35 - (2*object->getActualWeight())) - (object->value[GOLD]/1500))
		         + (bonus((int) player->dexterity.getCur())*2);
		break;

	case ASSASSIN:
	case BARD:
	case ROGUE:
		chance = ((player->getLevel()+20 - (2*object->getActualWeight())) - (object->value[GOLD]/1500))
		         + (bonus((int) player->dexterity.getCur())*2);
		break;

	// Casting classes just aren't cut out for it.
	case MAGE:
	case LICH:
	case DRUID:
	case CLERIC:
		chance = (((int)player->getLevel() - (2*object->getActualWeight())) - (object->value[GOLD]/1500))
		         + (bonus((int) player->dexterity.getCur())*2);
	default:
		chance = (((int)player->getLevel()+10 - (2*object->getActualWeight())) - (object->value[GOLD]/1500))
		         + (bonus((int) player->dexterity.getCur())*2);
		break;
	}

	// Armor makes a lot of noise
	if(object->getType() == ARMOR && object->getWearflag() != FINGER)
		chance -= 10;

	// An invisible person would have it easier.
	if(player->isInvisible())
		chance += 5;


	chance = MIN(70, MAX(2,chance));
	if(player->isCt())
		player->print("Chance: %d%\n", chance);

	if(player->isDm())
		chance = 101;

	player->statistics.attemptSteal();
	player->setFishing(false);

	if(mrand(1,100) > chance) {
		player->setFlag(P_CAUGHT_SHOPLIFTING);
		player->print("You were caught!\n");
		broadcast(player->getSock(), room, "%M tried to shoplift %1P.\n%M was seen!", player, object, player);

		player->smashInvis();

		// Activates lag protection.
		if(player->flagIsSet(P_LAG_PROTECTION_SET))
			player->setFlag(P_LAG_PROTECTION_ACTIVE);

		cp = room->first_mon;
		// Any shopkeeper perms will attack.
		while(cp) {
			tmp_crt = cp->crt->getMonster();
			if(tmp_crt->flagIsSet(M_PERMENANT_MONSTER)) {

				gServer->addActive(tmp_crt);
				tmp_crt->clearFlag(M_DEATH_SCENE);
				tmp_crt->clearFlag(M_FAST_WANDER);
				tmp_crt->addPermEffect("detect-invisible");
				tmp_crt->addPermEffect("true-sight");
				tmp_crt->setFlag(M_BLOCK_EXIT);
				tmp_crt->setFlag(M_ATTACKING_SHOPLIFTER);

				tmp_crt->addEnmCrt(player);
				tmp_crt->diePermCrt();
				player->print("%M attacks you!\n", tmp_crt);
				broadcast(player->getSock(), room, "%M attacks %N!", tmp_crt, player);
			}
			cp = cp->next_tag;
		}

		cp = storage->first_mon;
		// All guard mobs permed in storeroom will attack.
		while(cp) {
			tmp_crt = cp->crt->getMonster();
			cp = cp->next_tag;
			if(tmp_crt->flagIsSet(M_PERMENANT_MONSTER)) {
				if(storage->first_ply)
					broadcast(tmp_crt->getSock(), tmp_crt->getRoom(),
						"%M runs out after a shoplifter.", tmp_crt);
				else
					gServer->addActive(tmp_crt);
				tmp_crt->clearFlag(M_PERMENANT_MONSTER);  // This is all done to prevent people from getting
				tmp_crt->clearFlag(M_FAST_WANDER);  // killed mostly by others baiting these mobs in.
				tmp_crt->clearFlag(M_DEATH_SCENE);
				tmp_crt->addPermEffect("detect-invisible");
				tmp_crt->addPermEffect("true-sight");
				tmp_crt->setFlag(M_BLOCK_EXIT);
				tmp_crt->setFlag(M_WILL_ASSIST);
				tmp_crt->setFlag(M_WILL_BE_ASSISTED);
				tmp_crt->clearFlag(M_AGGRESSIVE_GOOD);
				tmp_crt->clearFlag(M_AGGRESSIVE_EVIL);
				tmp_crt->clearFlag(M_AGGRESSIVE);

				tmp_crt->setFlag(M_OUTLAW_AGGRO);
				tmp_crt->setFlag(M_ATTACKING_SHOPLIFTER);

				tmp_crt->setExperience(10);
				tmp_crt->coins.zero();

				tmp_crt->addEnmCrt(player);
				tmp_crt->diePermCrt();
				tmp_crt->deleteFromRoom();
				tmp_crt->addToRoom(room);

				player->print("%M attacks you!\n", tmp_crt);

				broadcast(player->getSock(), player->getRoom(),
					"%M attacks %N!", tmp_crt, player);
			}
		}

		// prevents shoptlift ; flee
		player->stun(mrand(3,5));

	} else {

		object2 = new Object;
		if(!object2)
			merror("shoplift", FATAL);

		*object2 = *object;
		object2->clearFlag(O_PERM_INV_ITEM);
		object2->clearFlag(O_PERM_ITEM);
		object2->clearFlag(O_TEMP_PERM);

		object2->setFlag(O_WAS_SHOPLIFTED);

		player->addObj(object2);
		player->printColor("You manage to conceal %1P on your person.\n", object2);
		broadcast(isCt, player->getSock(), player->getRoom(), "*DM* %M just shoplifted %1P.", player, object2);
		player->statistics.steal();
	}

	return(0);
}

//*********************************************************************
//						cmdBackstab
//*********************************************************************
// This function allows thieves and assassins to backstab a monster.
// If successful, a damage multiplier is given to the player. The
// player must be successfully hidden for the backstab to work. If
// the backstab fails, then the player is forced to wait double the
// normal amount of time for their next attack.

int cmdBackstab(Player* player, cmd* cmnd) {
	Player	*pTarget=0;
	Creature* target=0;
	int		 m=0, n=0;
	int		disembowel=0, dur=0, dmg=0;
	float	stabMod=0.0, cap=0.0;
	Damage damage;

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

	if(!player->knowsSkill("backstab")) {
		player->print("You don't know how to backstab.\n");
		return(0);
	}


	if(cmnd->num < 2 || player->isBlind()) {
		player->print("Backstab what?\n");
		return(0);
	}

	Object* weapon = player->ready[WIELD - 1];

	if(!weapon && !player->checkStaff("Backstabing requires a weapon.\n"))
		return(0);

	bstring category = weapon->getWeaponCategory();

	if(	category != "slashing" && category != "piercing" &&
		!player->checkStaff("Backstabing requires a slashing or piercing weapon.\n")
	)
		return(0);

	if(	weapon && weapon->flagIsSet(O_NO_BACKSTAB) &&
		!player->checkStaff("%O cannot be used to backstab.\n", weapon)
	)
		return(0);


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

	if(player->checkHeavyRestrict("backstab"))
		return(0);

	target = player->getRoom()->findCreature(player, cmnd->str[1], cmnd->val[1], true, true);
	if(target)
		pTarget = target->getPlayer();
	if(!target || target == player || (pTarget && strlen(cmnd->str[1]) < 3)) {
		player->print("You don't see that here.\n");
		return(0);
	}
	if(!pTarget && target->getMonster()->isEnmCrt(player->name)) {
		player->print("Not while you're already fighting %s.\n", target->himHer());
		return(0);
	}
	if(!player->canAttack(target))
		return(0);

	player->updateAttackTimer();

	if(player->dexterity.getCur() > 180)
		player->modifyAttackDelay(-10);

	player->print("You attempt to backstab %N.\n", target);
	target->print("%M attempts to backstab you!\n", player);
	broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M attempts to backstab %N.", player, target);

	if(target->isMonster())
		target->getMonster()->addEnmCrt(player);

	if(player->breakObject(player->ready[WIELD-1], WIELD)) {
		broadcast(player->getSock(), player->getRoom(), "%s backstab failed.", player->upHisHer());
		player->setAttackDelay(player->getAttackDelay()*2);
		return(0);
	}

	int skillLevel = (int)((player->getWeaponSkill(weapon) + (player->getSkillGained("backstab")*2)) / 3);

	AttackResult result = player->getAttackResult(target, weapon, DOUBLE_MISS, skillLevel);

	if(result == ATTACK_HIT || result == ATTACK_CRITICAL || result == ATTACK_BLOCK || result == ATTACK_GLANCING) {
		if(!player->isHidden() && !player->flagIsSet(P_MISTED))
			result = ATTACK_MISS;

		if(!pTarget && target->flagIsSet(M_NO_BACKSTAB))
			result = ATTACK_MISS;
	}

	player->smashInvis();
	player->unhide();

	int level = (int)player->getSkillLevel("backstab");
	bstring with = Statistics::damageWith(player, player->ready[WIELD-1]);

	if(player->isDm() && result != ATTACK_CRITICAL)
		result = ATTACK_HIT;

	player->statistics.swing();

	if(result == ATTACK_HIT || result == ATTACK_CRITICAL || result == ATTACK_BLOCK || result == ATTACK_GLANCING) {
//		int dmg = 0;

		// Progressive backstab code for thieves and assassins....
		if(player->getClass() == THIEF || player->getSecondClass() == THIEF) {
			if(level < 10)
				stabMod = 2.75;
			else if(level < 16)
				stabMod = 3.25;
			else if(level < 22)
				stabMod = 3.75;
			else if(level < 28)
				stabMod = 4.5;
			else if(level < 36)
				stabMod = 5.0;
			else if(level < 40)
				stabMod = 5.5;
			else
				stabMod = 5.75;
		} else if(player->getClass() == ASSASSIN || player->getSecondClass() == ASSASSIN) {
			if(level < 10)
				stabMod = 3.25;
			else if(level < 13)
				stabMod = 3.75;
			else if(level < 16)
				stabMod = 4;
			else if(level < 19)
				stabMod = 4.25;
			else if(level < 22)
				stabMod = 4.5;
			else if(level < 25)
				stabMod = 5.0;
			else if(level < 28)
				stabMod = 5.25;
			else if(level < 30)
				stabMod = 5.5;
			else if(level < 34)
				stabMod = 6.0;
			else if (level < 38)
				stabMod = 6.25;
			else
				stabMod = 6.5;
		} else
			stabMod = 5.25;



		if(	player->getSecondClass() == THIEF &&
			(player->getClass() == FIGHTER || player->getClass() == MAGE)
		) {
			cap = 3.0;
		}

		if(	player->getSecondClass() == ASSASSIN ||
			(player->getClass() == MAGE && player->getSecondClass() == THIEF)
		) {
			cap = 4.0;
		}

		if(cap > 0)
			stabMod = MAX(cap, stabMod-1);

		// Version 2.43c Update -- I'm multiplying the stabMod by 2.0 to compensate for the increase
		// in armor absorb, and the removal of multiplier for attackPower
		stabMod *= 2.0;


		// Return of 1 means the weapon was shattered or otherwise rendered unsuable
		int drain = 0;
		bool wasKilled = false, meKilled = false;
		if(player->computeDamage(target, weapon, ATTACK_BACKSTAB, result, damage, true, drain, stabMod) == 1) {
			player->unequip(WIELD, UNEQUIP_DELETE);
			weapon = NULL;
			player->computeAttackPower();
		}
		damage.includeBonus();
		n = damage.get();

//		if(mrand(1, 100) <= (5 - p) && weapon &&
//		           !weapon->flagIsSet(O_CURSED)) {
//
//		}

		m = MIN(target->hp.getCur(), damage.get());
		if(!pTarget)
			target->getMonster()->addEnmDmg(player, m);

		if(result == ATTACK_BLOCK) {
			player->printColor("^C%M partially blocked your attack!\n", target);
			target->printColor("^CYou manage to partially block %N's attack!\n", player);
		}


		if(result == ATTACK_CRITICAL)
			player->statistics.critical();
		else
			player->statistics.hit();
		if(target->isPlayer())
			target->getPlayer()->statistics.wasHit();


		disembowel = target->hp.getCur() * 2;

		player->printColor("You backstabbed %N for %s%d^x damage.\n", target, player->customColorize("*CC:DAMAGE*"), damage.get());
		player->checkImprove("backstab", true);
		log_immort(false,player, "%s backstabbed %s for %d damage.\n", player->name, target->name, damage.get());


		target->printColor( "^M%M^x backstabbed you%s for %s%d^x damage.\n", player,
			target->isBrittle() ? "r brittle body" : "", target->customColorize("*CC:DAMAGE*"), damage.get());

		broadcastGroup(false, target, "^M%M^x backstabbed ^M%N^x for *CC:DAMAGE*%d^x damage, %s%s\n",
			player, target, damage.get(), target->heShe(), target->getStatusStr(damage.get()));

		player->statistics.attackDamage(damage.get(), with);

		if(	weapon && weapon->getMagicpower() &&
			weapon->flagIsSet(O_WEAPON_CASTS) &&
			mrand(1,100) <= 50
		)
			n += player->castWeapon(target, weapon, meKilled);

		if(	weapon &&
			(	(weapon->flagIsSet(O_ENVENOMED) && player->getClass() == ASSASSIN && level >=7) ||
				(weapon->flagIsSet(O_ENVENOMED) && player->isCt())
			)
		)
			n += player->checkPoison(target, weapon);

		meKilled = player->doReflectionDamage(damage, target) || meKilled;

		player->doDamage(target, damage.get(), NO_CHECK);
		wasKilled = target->hp.getCur() < 1;

		// Check for disembowel
		if(wasKilled && n > disembowel && mrand(1,100) < 50) {
			switch(mrand(1,4)) {
			case 1:
				player->printColor("^cYou completely disemboweled %N! %s's dead!\n", target, target->upHeShe());
				broadcast(player->getSock(), player->getRoom(), "%M completely disembowels %N! %s's dead!",
					player, target, target->upHeShe());
				if(target->isPlayer())
					target->print("%M completely disemboweled you! You're dead!\n", player);

				break;
			case 2:
				player->printColor("^cYou impaled %N through %s back! %s's dead!\n",
					target, target->hisHer(), target->upHeShe());
				broadcast(player->getSock(), player->getRoom(), "%M impales %N through %s back! %s's dead!",
					player, target, target->hisHer(), target->upHeShe());
				if(target->isPlayer())
					target->print("%M impaled you through the back! You're dead!\n", player);

				break;
			case 3:
				if(weapon) {
					player->printColor("^cThe %s went completely through %N! %s's dead!\n",
						weapon->name, target, target->upHeShe());
					broadcast(player->getSock(), player->getRoom(), "%M's %s goes completely through %N! %s's dead!",
						player, weapon->name, target, target->upHeShe());

					if(target->isPlayer())
						target->print("%M's %s sticks out of your chest! You're dead!\n",
							player, weapon->name);
				}

				break;
			case 4:
				player->printColor("^cYou cut %N in half from behind! %s's dead!\n",
					target, target->upHeShe());
				broadcast(player->getSock(), player->getRoom(), "%M cut %N in half from behind! %s's dead!",
					player, target, target->upHeShe());
				if(target->isPlayer())
					target->print("%M cut you in half from behind! You're dead!\n", player);

				break;
			}
		}

		if(weapon)
			weapon->decShotscur();
		Creature::simultaneousDeath(player, target, wasKilled, meKilled, false);

		// only monsters flee, and not when their attacker was killed
		if(!wasKilled && !meKilled && target->getMonster()) {
			if(target->flee() == 2)
				return(0);
		}

	} else if(result == ATTACK_MISS) {
		player->statistics.miss();
		if(target->isPlayer())
			target->getPlayer()->statistics.wasMissed();
		player->print("You missed.\n");
		player->checkImprove("backstab", false);
		broadcast(player->getSock(), player->getRoom(), "%s backstab failed.", player->upHisHer());
		player->setAttackDelay(mrand(30,90));
	} else if(result == ATTACK_DODGE) {
		target->dodge(player);
	} else if(result == ATTACK_PARRY) {
		int parryResult = target->parry(player);
		if(parryResult == 2) {
			// oh no, we died from a riposte...
			return(0);
		}
	} else if(result == ATTACK_FUMBLE) {
		player->statistics.fumble();
		player->printColor("^gYou FUMBLED your weapon.\n");
		broadcast(player->getSock(), player->getRoom(), "^g%M fumbled %s weapon.", player, player->hisHer());

		if(weapon->flagIsSet(O_ENVENOMED)) {
			if(!player->immuneToPoison() &&
				!player->chkSave(POI, player, -5) && !induel(player, target->getPlayer())
			) {
				player->printColor("^G^#You poisoned yourself!!\n");
				broadcast(player->getSock(), player->getRoom(), "%M poisoned %sself!!",
					player, player->himHer());

				if(weapon->getEffectStrength()) {
					dmg = (mrand(1,3) + (weapon->getEffectStrength()/10));
					player->hp.decrease(dmg);
					player->print("You take %d damage as the poison takes effect!\n", dmg);
				}

				weapon->clearFlag(O_ENVENOMED);

				if(player->hp.getCur() < 1) {
					n = 0;
					player->addObj(weapon);
					weapon = 0;
					player->computeAttackPower();
					player->die(POISON_PLAYER);
					return(0);
				}

				dur = standardPoisonDuration(weapon->getEffectDuration(), player->constitution.getCur());
				player->poison(player, weapon->getEffectStrength(), dur);

			} else {
				player->print("You almost poisoned yourself!\n");
				broadcast(player->getSock(), player->getRoom(), "%M almost poisoned %sself!", player, player->himHer());
			}
		}

		n = 0;
		player->addObj(weapon);
		player->ready[WIELD-1] = 0;
		weapon = 0;
		player->computeAttackPower();
		player->computeAttackPower();
	} else {
		player->printColor("^RError!!! Unhandled attack result: %d\n", result);
	}

	return(0);
}

//*********************************************************************
//						checkPoison
//*********************************************************************
// This will check for envenom poisoning against the target with the given weapon

int Player::checkPoison(Creature* target, Object* weapon) {
	if(!weapon)
		return(0);

	int dmg = 0;
	int level = (int)getSkillLevel("backstab");
	int poisonchance = 50+((level - (target->getLevel()-1))*10);
	poisonchance -= (2*bonus(target->constitution.getCur()));

	if(target->isMonster() && target->flagIsSet(M_WILL_POISON))
		poisonchance /= 2;

	poisonchance = MAX(0,MIN(poisonchance, 80));

	// Can't poison them if they're immune to poison
	if(target->immuneToPoison())
		poisonchance = 0;

	// No poisoning people if they're in a duel
	if(induel(this, target->getPlayer()))
		poisonchance = 0;

	// If they're already poisoned, don't poison them again
	if(target && target->isPoisoned())
		poisonchance = 0;

	// Less of a chance to poison a perm
	if(!target && target->flagIsSet(M_PERMENANT_MONSTER))
		poisonchance = MIN(poisonchance, 10);

	// We can always poison someone if we're a ct
	if(isCt())
		poisonchance = 101;

	if(mrand(1,100) <= poisonchance && !target->chkSave(POI, this, -1)) {
		// 75% chance to use up the poison
		if(mrand (1,100) > 25)
			weapon->clearFlag(O_ENVENOMED);

		printColor("^gYou poisoned %N!\n", target);
		target->printColor( "^g^#%M poisoned you!!\n", this);
		broadcast(getSock(), target->getSock(), target->getRoom(), "%M poisoned %N!", this, target);

		unsigned int dur = standardPoisonDuration(weapon->getEffectDuration(), target->constitution.getCur());

		if(weapon->getEffectStrength()) {
			dmg = (mrand(1,3) + (weapon->getEffectStrength()/10));
			printColor("^gThe poison did ^G%d^g onset damage!\n", dmg);
			target->printColor("^gYou take ^G%d^g damage as the poison takes effect!\n", dmg);
		}

		target->poison(this, weapon->getEffectStrength(), dur);
	}
	return(dmg);
}

//*********************************************************************
//						cmdAmbush
//*********************************************************************
// This function allows rogues to ambush their opponents from
// hiding. It is similar to backstab, but somewhat weakened and
// more limited. -- TC

int cmdAmbush(Player* player, cmd* cmnd) {
	Player	*pCreature=0;
	Creature* creature=0;


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

	if(!player->knowsSkill("ambush")) {
		player->print("You don't know how to ambush people!\n");
		return(0);
	}

	if(cmnd->num < 2 || player->isBlind()) {
		player->print("Ambush whom?\n");
		return(0);
	}

	if(!player->isCt()) {
		if(	!player->ready[WIELD-1] ||
			!player->ready[HELD-1] ||
			player->ready[HELD-1]->getType() != WEAPON
		) {
			player->print("You must have two weapons wielded to properly ambush.\n");
			return(0);
		}

		if(!player->flagIsSet(P_HIDDEN) && !player->flagIsSet(P_MISTED) && !player->isCt()) {
			player->print("How do you expect to ambush when you aren't hiding?\n");
			return(0);
		}

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

		if(player->checkHeavyRestrict("ambush"))
			return(0);
	}


	creature = player->getRoom()->findCreature(player, cmnd->str[1], cmnd->val[1], true, true);
	if(creature)
		pCreature = creature->getPlayer();
	if(!creature || creature == player || (pCreature && strlen(cmnd->str[1]) < 3)) {
		player->print("You don't see that here.\n");
		return(0);
	}


	if(!pCreature && creature->getMonster()->isEnmCrt(player->name) && !player->isCt()) {
		player->print("Not while you're already fighting %s.\n",
		      creature->himHer());
		return(0);
	}

	if(!player->canAttack(creature))
		return(0);

	player->smashInvis();

	if(creature->isMonster()) {
		creature->getMonster()->addEnmCrt(player);
	}

	player->updateAttackTimer();

	if(player->dexterity.getCur() > 180)
		player->modifyAttackDelay(-10);

	player->print("You ambush %N!\n", creature);
	broadcast(player->getSock(), creature->getSock(), player->getRoom(), "%M ambushes %N!", player, creature);
	creature->print("%M ambushes you!\n", player);

	player->unhide();

	player->attackCreature(creature, ATTACK_AMBUSH);

	return(0);
}



//*********************************************************************
//						cmdPickLock
//*********************************************************************
// This function is called when a thief or assassin attempts to pick a
// lock.  If the lock is pickable, there is a chance (depending on the
// player's level) that the lock will be picked.

int cmdPickLock(Player* player, cmd* cmnd) {
	Exit	*exit=0;
	long	i=0, t=0;
	int		chance=0;


	player->clearFlag(P_AFK);


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

	if(!player->knowsSkill("pick")) {
		player->print("You don't know how to pick locks.\n");
		return(0);
	}

	if(!player->isCt()) {
		if(player->flagIsSet(P_SITTING)) {
			player->print("You have to stand up first.\n");
			return(0);
		}
		if(player->isBlind()) {
			player->printColor("^CHow can you pick a lock when blind?\n");
			return(0);
		}

	}


	if(cmnd->num < 2) {
		player->print("Pick what?\n");
		return(0);
	}
	exit = findExit(player, cmnd);

	if(!exit) {
		player->print("You don't see that here.\n");
		return(0);
	}


	Monster* guard = player->getRoom()->getGuardingExit(exit);
	if(guard && !player->checkStaff("%M won't let you pick it.\n", guard))
		return(0);

	if(!exit->flagIsSet(X_LOCKED)) {
		player->print("It's not locked.\n");
		return(0);
	}

	player->unhide();

	if(!player->isCt()) {

		i = LT(player, LT_PICKLOCK);
		t = time(0);

		if(t < i) {
			player->pleaseWait(i-t);
			return(0);
		}

		player->lasttime[LT_PICKLOCK].ltime = t;
		player->lasttime[LT_PICKLOCK].interval = (player->getClass() == ROGUE ? 20 : 10);

	}
	int level = (int)player->getSkillLevel("pick");
	if(player->getClass() == THIEF)
		chance = 10*(level - exit->getLevel()) + (2*bonus((int)player->dexterity.getCur()));
	else if(player->getSecondClass() == THIEF && (player->getClass() == MAGE || player->getClass() == FIGHTER) )
		chance = 5*(level - exit->getLevel()) + (2*bonus((int)player->dexterity.getCur()));
	else if(player->getClass() == ROGUE || (player->getClass() == THIEF && player->getSecondClass() == MAGE))
		chance = 7*(level - exit->getLevel()) + (2*bonus((int)player->dexterity.getCur()));
	else
		chance = 2*(level - exit->getLevel()) + (2*bonus((int)player->dexterity.getCur()));


	if(exit->flagIsSet(X_UNPICKABLE))
		chance = 0;

	if((exit->getLevel() > level) && !player->isCt()) {
		player->print("The lock's mechanism is currently beyond your experience.\n");
		chance = 0;
	}

	chance = MAX(0, chance);

	if(player->isCt())
		chance = 101;

	broadcast(player->getSock(), player->getRoom(), "%M attempts to pick the %s.", player, exit->name);

	if(mrand(1,100) <= chance) {
		log_immort(false,player, "%s picked the %s in room %s.\n", player->name, exit->name,
			player->getRoom()->fullName().c_str());

		player->print("You successfully picked the lock.\n");
		player->checkImprove("pick", true);
		exit->clearFlag(X_LOCKED);
		broadcast(player->getSock(), player->getRoom(), "%s succeeded.", player->upHeShe());
	} else {
		player->print("You failed.\n");
		player->checkImprove("pick", false);
	}

	return(0);
}


//*********************************************************************
//						cmdPeek
//*********************************************************************
// This function allows a thief to peek at the inventory of
// another player.  If successful, they will be able to see it and
// another roll is made to see if they get caught.

int cmdPeek(Player* player, cmd* cmnd) {
	Creature* creature=0;
	Player	*pCreature=0;
	Monster* mCreature=0;
	bstring str = "";
	long	i=0, t=0;
	int		chance=0, goldchance=0, ok=0;

	player->clearFlag(P_AFK);

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

	if(!player->isStaff() && !player->knowsSkill("peek")) {
		player->print("You don't know how to peek at other people's inventories.\n");
		return(0);
	}

	if(cmnd->num < 2) {
		player->print("Peek at who?\n");
		return(0);
	}

	int level = (int)player->getSkillLevel("peek");

	if(player->isBlind()) {
		player->printColor("^CYou can't do that! You're blind!\n");
		return(0);
	}

	creature = player->getRoom()->findCreature(player, cmnd);
	if(!creature || creature == player) {
		player->print("That person is not here.\n");
		return(0);
	}
	mCreature = creature->getMonster();
	pCreature = creature->getPlayer();

	if(player->getClass() == BUILDER) {
		if(pCreature) {
			player->print("You cannot peek players.\n");
			return(0);
		}
	if(!player->canBuildMonsters() && !player->canBuildObjects())
			return(cmdNoAuth(player));
		if(!player->checkBuilder(player->parent_rom)) {
			player->print("Error: Room number not inside any of your alotted ranges.\n");
			return(0);
		}
		if(mCreature && mCreature->info.id && !player->checkBuilder(mCreature->info)) {
			player->print("Error: Monster not inside any of your alotted ranges.\n");
			return(0);
		}
	}

	if(!pCreature && player->isStaff())
		return(dmMobInventory(player, cmnd));


	if(pCreature && pCreature->flagIsSet(P_MISTED)) {
		player->print("You cannot peek at the inventory of a mist.\n");
		return(0);
	}

	if(player->checkHeavyRestrict("peek"))
		return(0);

	i = LT(player, LT_PEEK);
	t = time(0);

	if(i > t && !player->isStaff()) {
		player->pleaseWait(i-t);
		return(0);
	}

	player->lasttime[LT_PEEK].ltime = t;
	player->lasttime[LT_PEEK].interval = 5;

	if(!player->isCt() && creature->isStaff()) {
		player->print("You failed.\n");
		return(0);
	}

	if(!pCreature && (creature->flagIsSet(M_CANT_BE_STOLEN_FROM) || creature->flagIsSet(M_TRADES) || creature->flagIsSet(M_CAN_PURCHASE_FROM)) && !player->isDm()) {
		player->print("%M manages to keep %s inventory hidden from you.\n", creature, creature->hisHer());
		return(0);
	}

	if(cmnd->num > 2 && pCreature && (player->getClass() == THIEF || player->isCt())) {
		peek_bag(player, pCreature, cmnd, 0);
		return(0);
	}

	if(!ok && (player->getClass() == THIEF || player->isStaff()))
		chance = (25 + level*10)-(creature->getLevel()*5);
	else
		chance = (level*10)-(creature->getLevel()*5);

	if(chance<0)
		chance=0;

	if(creature->getLevel() >= level + 6)
		chance = 0;

	if(player->isStaff())
		chance=100;
	if(mrand(1,100) > chance) {
		player->print("You failed.\n");
		if(mrand(1,50) <= 50)
			player->unhide();
		player->checkImprove("peek", false);
		return(0);
	}

	chance = MIN(90, (player->getClass() == ASSASSIN ? (level*4):(15 + level*5)));

	if(mrand(1,100) > chance && !player->isStaff()) {
		creature->print("%M peeked at your inventory.\n", player);
		broadcast(player->getSock(), creature->getSock(), player->getRoom(),
			"%M peeked at %N's inventory.", player, creature);
		player->print("%s noticed!\n", creature->upHeShe());
	} else {
		player->print("%s's oblivious to your rummaging.\n",
		      creature->upHeShe());
	}
	player->checkImprove("peek", true);

	str = listObjects(player, creature->first_obj, player->isStaff());
	if(str != "")
		player->printColor("%s is carrying: %s.\n", creature->upHeShe(), str.c_str());
	else
		player->print("%s isn't holding anything.\n", creature->upHeShe());


	goldchance = (5+(level*5)) - creature->getLevel();
	goldchance = MIN(MAX(1,goldchance),90);

	if(player->isCt())
		goldchance = 101;

	if(mrand(1,100) <= goldchance && !pCreature && !creature->flagIsSet(M_PERMENANT_MONSTER))
		player->print("%s has %ld gold coins.\n", creature->upHeShe(), creature->coins[GOLD]);

	return(0);
}


//*********************************************************************
//						peek_bag
//*********************************************************************

int peek_bag(Player* player, Player* target, cmd* cmnd, int inv) {
	Object	*container=0;
	bstring str = "";
	int		chance=0;

	if(!player->isStaff()) {
		int level = (int)player->getSkillLevel("peek");

		if(level < 10) {
			player->print("You are are not experienced enough at peeking to do that.\n");
			return(0);
		}

		chance = (5 + level*10)-(target->getLevel()*5);
		if(chance<0)
			chance=0;

		if(target->getLevel() >= level + 6)
			chance = 0;

		if(mrand(1,100) > chance) {
			player->print("You failed.\n");
			player->checkImprove("peek", false);
			return(0);
		}
	}

	container = findObject(player, target->first_obj, cmnd, 2);

	if(!container) {
		player->print("%s doesn't have that.\n", target->upHeShe());
		return(0);
	}

	if(container->getType() != CONTAINER) {
		player->print("That isn't a container.\n");
		return(0);
	}

	if(!inv) {
		if(mrand(1,100) > chance && !player->isStaff()) {

			player->print("You manage to peek inside %N's %s.\n", target, container->name);
			target->print("%M managed to peek in your %s!\n", player, container->name);
			broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M peeked at %N's inventory.",
				player, target);
			player->print("%s noticed!\n", target->upHeShe());

		} else {
			player->print("You manage to peek inside %N's %s.\n", target, container->name);
			player->print("%s's oblivious to your rummaging.\n", target->upHeShe());
		}
		player->checkImprove("peek", true);
	}

	if(container->getType() == CONTAINER) {
		str = listObjects(player, container->first_obj, false);
		if(str != "")
			player->printColor("It contains: %s.\n", str.c_str());
		else
			player->print("It is empty.\n");
	}

	return(0);
}