roh/conf.old/area/
roh/config/code/python/
roh/config/game/area/
roh/config/game/signs/
roh/help/dmhelp/
roh/help/help/
roh/log/
roh/log/staff/
roh/monsters/ocean/
roh/objects/misc/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.47e/
/*
 * rogues.cpp
 *   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-2012 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->getParent(), "%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->getParent()->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->getMaster()->getCName(), creature->himHer());
		creature->getMaster()->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(amount, GOLD);

	gServer->logGold(GOLD_OUT, player, Money(amount, GOLD), creature, "Bribe");

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

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

		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->getRoomParent()->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);
}

//*********************************************************************
//						canSearch
//*********************************************************************

bool canSearch(const Player* player) {
	if(!player->ableToDoCommand())
		return(false);

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

	if(player->flagIsSet(P_SITTING)) {
		player->print("You must stand up first!\n");
		return(false);
	}

	return(true);
}

//*********************************************************************
//						doSearch
//*********************************************************************

void doSearch(Player* player, bool immediate) {
	BaseRoom* room = player->getRoomParent();
	int		chance=0;
	bool	found=false, detectMagic = player->isEffected("detect-magic");

	player->clearFlag(P_AFK);

	if(!canSearch(player))
		return;

	player->unhide();


	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;

	
	for(Exit* ext : room->exits) {
		if(	(ext->flagIsSet(X_SECRET) && mrand(1,100) <= (chance + searchMod(ext->getSize()))) ||
			(ext->isConcealed(player) && mrand(1,100) <= 5))
		{
			// canSee doesnt handle DescOnly
			if(player->canSee(ext) && !ext->flagIsSet(X_DESCRIPTION_ONLY)) {
				found = true;
				player->printColor("You found an exit: %s^x.\n", ext->getCName());

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

	for(Object* obj : room->objects) {
		if(	obj->flagIsSet(O_HIDDEN) &&
			player->canSee(obj) &&
			mrand(1,100) <= (chance + searchMod(obj->getSize())) )
		{
			found = true;
			player->printColor("You found %1P.\n", obj);
		}
	}

	for(Player* ply : room->players) {
		if(	ply->flagIsSet(P_HIDDEN) &&
			player->canSee(ply) &&
			mrand(1,100) <= (chance + searchMod(ply->getSize())))
		{
			found = true;
			player->print("You found %s hiding.\n", ply->getCName());
		}
	}

	for(Monster* mons : room->monsters) {
		if(	mons->flagIsSet(M_HIDDEN) &&
			player->canSee(mons) &&
			mrand(1,100) <= (chance + searchMod(mons->getSize())))
		{
			found = true;
			player->print("You found %1N hiding.\n", mons);
		}
	}

	// 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->inAreaRoom() && player->getAreaRoomParent()->spawnHerbs()) {
			player->print("You found some herbs!\n");
			found = true;
		}
	}


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

void doSearch(const DelayedAction* action) {
	doSearch(action->target->getAsPlayer(), false);
}

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

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

	player->clearFlag(P_AFK);

	if(!canSearch(player))
		return(0);
	if(gServer->hasAction(player, ActionSearch)) {
		player->print("You are already searching!\n");
		return(0);
	}

	i = LT(player, LT_SEARCH);

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

	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;
	
	player->interruptDelayedActions();

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

	// big rooms take longer to search
	if(player->getRoomParent()->getSize() >= SIZE_GARGANTUAN) {
		// doSearch calls unhide, no need to do it twice
		player->unhide();

		player->print("You begin searching.\n");
		gServer->addDelayedAction(doSearch, player, 0, ActionSearch, player->lasttime[LT_SEARCH].interval);
	} else {
		doSearch(player, true);
	}
	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;
	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
	for(Object *obj : room->objects) {
		if(obj->flagIsSet(O_DISPOSABLE))
			return(false);
	}

	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->setDroppedBy(room, "SpawnHerb");
			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 don't know how to hide effectively.\n");
		return(0);
	}
	if(player->flagIsSet(P_SITTING)) {
		player->print("You cannot do that while sitting down.\n");
		return(0);
	}

	if(player->isEffected("mist")) {
		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;
	if(	player->getClass() == THIEF ||
		player->getClass() == ASSASSIN ||
		player->getClass() == ROGUE ||
		player->getClass() == RANGER
	)
		player->lasttime[LT_HIDE].interval = 5;
	else
		player->lasttime[LT_HIDE].interval = 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->getRoomParent()->isOutdoors())
			chance += 20;

		if(	(player->getRoomParent()->flagIsSet(R_DARK_AT_NIGHT) && !isDay()) ||
			player->getRoomParent()->flagIsSet(R_DARK_ALWAYS) ||
			player->getRoomParent()->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->getRoomParent()->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->isEffected("mist")) {
			player->setFlag(P_HIDDEN);
			player->print("You slip into the shadows unnoticed.\n");
			player->checkImprove("hide", true);
		} else {
			player->unhide();
			broadcast(player->getSock(), player->getParent(), "%M tries to hide in the shadows.", player);
			player->checkImprove("hide", false);
		}

		return(0);
	}

	object = player->getRoomParent()->findObject(player, cmnd, 1);

	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->getRoomParent(), 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->getParent(), "%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);
}

//*********************************************************************
//						doScout
//*********************************************************************

bool doScout(Player* player, const Exit* exit) {
	BaseRoom *room=0;


	if(exit->flagIsSet(X_STAFF_ONLY) && !player->isStaff()) {
		player->print("A magical force prevents you from seeing into the room.\n");
		return(false);
	}


	Move::getRoom(player, exit, &room, true);

	if(player->getClass() == BUILDER && room && !room->isConstruction()) {
		player->print("A magical force prevents you from seeing into the room.\n");
		return(false);
	}

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

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

	return(true);
}

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

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

	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->getRoomParent()->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->getRoomParent());


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


	alwaysSucceed = (exit->flagIsSet(X_CAN_LOOK) || exit->flagIsSet(X_LOOK_ONLY));

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

	const Monster* guard = player->getRoomParent()->getGuardingExit(exit, player);
	if(guard && !player->checkStaff("%M %s.\n", guard, guard->flagIsSet(M_FACTION_NO_GUARD) ? "doesn't like you enough to let you scout there" : "won't let you scout there"))
		return(0);

	if(!alwaysSucceed) {
		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);
	}

	if(!alwaysSucceed)
		player->checkImprove("scout", true);
	player->printColor("You scout the %s^x exit.\n", exit->getCName());

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

	doScout(player, exit);
	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 = player->findObject(player, cmnd, 1);

	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 = player->findObject(player, 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->getParent(), "%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.

bool isValidShop(const UniqueRoom* shop, const UniqueRoom* storage);

int cmdShoplift(Player* player, cmd* cmnd) {
	UniqueRoom	*room=0, *storage=0;
	Object	*object=0, *object2=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->getUniqueRoomParent();

	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->isEffected("berserk")) {
		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->isEffected("mist")) {
		player->print("You can't shoplift while you are a mist.\n");
		return(0);
	}



	loadRoom(shopStorageRoom(room), &storage);

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


	
	storage->addPermCrt();
	for(Monster* mons : storage->monsters) {
		if(mons->getLevel() >= 10 && mons->flagIsSet(M_PERMENANT_MONSTER))
			guarded++;
	}

	// 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.
	for(Monster* mons : room->monsters) {
		if(mons->flagIsSet(M_ATTACKING_SHOPLIFTER) && !player->isDm()) {
			player->print("The shopkeep or shop's guards are too alert right now.\n");
			return(0);
		}
	}


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

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

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

	if(object->getName() == "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->interruptDelayedActions();

	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);

		for(Monster* mons : room->monsters) {
			if(mons->flagIsSet(M_PERMENANT_MONSTER)) {

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

				mons->addEnemy(player, true);
				mons->diePermCrt();
			}
		}
		MonsterSet::iterator mIt = storage->monsters.begin();

		while(mIt != storage->monsters.end()) {

		    Monster *mons = (*mIt++);
			if(mons->flagIsSet(M_PERMENANT_MONSTER)) {
				if(!storage->players.empty())
					broadcast(mons->getSock(), mons->getRoomParent(),
						"%M runs out after a shoplifter.", mons);
				else
					gServer->addActive(mons);
				mons->clearFlag(M_PERMENANT_MONSTER);  // This is all done to prevent people from getting
				mons->clearFlag(M_FAST_WANDER);  // killed mostly by others baiting these mobs in.
				mons->clearFlag(M_DEATH_SCENE);
				mons->addPermEffect("detect-invisible");
				mons->addPermEffect("true-sight");
				mons->setFlag(M_BLOCK_EXIT);
				mons->setFlag(M_WILL_ASSIST);
				mons->setFlag(M_WILL_BE_ASSISTED);
				mons->clearFlag(M_AGGRESSIVE_GOOD);
				mons->clearFlag(M_AGGRESSIVE_EVIL);
				mons->clearFlag(M_AGGRESSIVE);

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

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

				mons->addEnemy(player, true);
				mons->diePermCrt();
				mons->deleteFromRoom();
				mons->addToRoom(room);
			}
		}

		// 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);
		object2->setDroppedBy(room, "Shoplift");

		player->addObj(object2);
		player->printColor("You manage to conceal %1P on your person.\n", object2);
		broadcast(isCt, player->getSock(), player->getRoomParent(), "*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		 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(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);

    if(!(target = player->findVictim(cmnd, 1, true, false, "Backstab what?\n", "You don't see that here.\n")))
		return(0);

	if(target)
		pTarget = target->getAsPlayer();

	if(!pTarget && target->getAsMonster()->isEnemy(player)) {
		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->getParent(), "%M attempts to backstab %N.", player, target);

	if(target->isMonster())
		target->getAsMonster()->addEnemy(player);

	if(player->breakObject(player->ready[WIELD-1], WIELD)) {
		broadcast(player->getSock(), player->getParent(), "%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->isEffected("mist"))
			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 = tMIN<float>(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(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->getAsPlayer()->statistics.wasHit();


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

		player->printColor("You backstabbed %N for %s%d^x damage.\n", target, player->customColorize("*CC:DAMAGE*").c_str(), damage.get());
		player->checkImprove("backstab", true);
		log_immort(false,player, "%s backstabbed %s for %d damage.\n", player->getCName(), target->getCName(), 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*").c_str(), 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 && weapon->getChargesCur() > 0)
		{
		    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->getParent(), "%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->getParent(), "%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->getCName(), target, target->upHeShe());
					broadcast(player->getSock(), player->getParent(), "%M's %s goes completely through %N! %s's dead!",
						player, weapon->getCName(), target, target->upHeShe());

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

				break;
			case 4:
				player->printColor("^cYou cut %N in half from behind! %s's dead!\n",
					target, target->upHeShe());
				broadcast(player->getSock(), player->getParent(), "%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, false, false);

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

	} else if(result == ATTACK_MISS) {
		player->statistics.miss();
		if(target->isPlayer())
			target->getAsPlayer()->statistics.wasMissed();
		player->print("You missed.\n");
		player->checkImprove("backstab", false);
		broadcast(player->getSock(), player->getParent(), "%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->getParent(), "^g%M fumbled %s weapon.", player, player->hisHer());

		if(weapon->flagIsSet(O_ENVENOMED)) {
			if(!player->immuneToPoison() &&
				!player->chkSave(POI, player, -5) && !induel(player, target->getAsPlayer())
			) {
				player->printColor("^G^#You poisoned yourself!!\n");
				broadcast(player->getSock(), player->getParent(), "%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->getParent(), "%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->mFlagIsSet(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->getAsPlayer()))
		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->getRoomParent(), "%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(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->isEffected("mist") && !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);
	}


    if(!(creature = player->findVictim(cmnd, 1, true, false, "Ambush whom?\n", "You don't see that here.\n")))
		return(0);

    if(creature)
		pCreature = creature->getAsPlayer();

	if(!pCreature && creature->getAsMonster()->isEnemy(player) && !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->getAsMonster()->addEnemy(player);
	}

	player->updateAttackTimer();

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

	player->print("You ambush %N!\n", creature);
	broadcast(player->getSock(), creature->getSock(), player->getRoomParent(), "%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);
	}


	const Monster* guard = player->getRoomParent()->getGuardingExit(exit, player);
	if(guard && !player->checkStaff("%M %s.\n", guard, guard->flagIsSet(M_FACTION_NO_GUARD) ? "doesn't like you enough to let you pick it" : "won't let you pick it"))
		return(0);

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

	if(exit->isWall("wall-of-force")) {
		player->printColor("The %s^x is blocked by a wall of force.\n", exit->getCName());
		return(0);
	}
	// were they killed by exit effect damage?
	if(exit->doEffectDamage(player))
		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->getParent(), "%M attempts to pick the %s^x.", player, exit->getCName());

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

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

		Hooks::run(player, "succeedPickExit", exit, "succeedPickByCreature");

	} else {
		player->print("You failed.\n");
		player->checkImprove("pick", false);

		Hooks::run(player, "failPickExit", exit, "failPickByCreature");
	}

	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->getParent()->findCreature(player, cmnd);
	if(!creature || creature == player) {
		player->print("That person is not here.\n");
		return(0);
	}
	mCreature = creature->getAsMonster();
	pCreature = creature->getAsPlayer();

	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->getUniqueRoomParent())) {
			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->isEffected("mist")) {
		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->getRoomParent(),
			"%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 = creature->listObjects(player, 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 = target->findObject(player, 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->getCName());
			target->print("%M managed to peek in your %s!\n", player, container->getCName());
			broadcast(player->getSock(), target->getSock(), player->getParent(), "%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->getCName());
			player->print("%s's oblivious to your rummaging.\n", target->upHeShe());
		}
		player->checkImprove("peek", true);
	}

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

	return(0);
}