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/
/*
 * commerce.cpp
 *	 Functions to handle buying/selling/trading from shops/monsters.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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 "factions.h"
#include "property.h"
#include "guilds.h"
#include "unique.h"

#define TAX .06

//*********************************************************************
//						buyAmount
//*********************************************************************

Money buyAmount(const Player* player, const Monster *monster, const Object* object, bool sell) {
	Money cost = Faction::adjustPrice(player, monster->getPrimeFaction(), object->value, true);
	cost.set(MAX(10,cost[GOLD]), GOLD);
	return(cost);
}

Money buyAmount(const Player* player, const Room *room, const Object* object, bool sell) {
	Money cost = Faction::adjustPrice(player, room->getFaction(), object->value, true);
	cost.set(MAX(10,cost[GOLD]), GOLD);
	return(cost);
}

//*********************************************************************
//						sellAmount
//*********************************************************************

Money sellAmount(const Player* player, const Room *room, const Object* object, bool sell) {
	Money value = Faction::adjustPrice(player, room->getFaction(), object->value, sell);
	value.set(MIN(value[GOLD] / 2, MAXPAWN), GOLD);
	return(value);
}

//*********************************************************************
//						bailCost
//*********************************************************************

Money bailCost(const Player* player) {
	Money cost;
	cost.set(2000*player->getLevel(), GOLD);
	return(cost);
}

//*********************************************************************
//						Dice
//*********************************************************************

Dice::Dice() {
	clear();
}
Dice::Dice(unsigned short n, unsigned short s, short p) {
	number = sides = plus = 0;
	setNumber(n);
	setSides(s);
	setPlus(p);
}
void Dice::clear() {
	number = sides = plus = 0;
}
bool Dice::operator==(const Dice& d) const {
	if(	number != d.getNumber() ||
		plus != d.getPlus() ||
		sides != d.getSides()
	)
		return(false);
	return(true);
}
bool Dice::operator!=(const Dice& d) const {
	return(!(*this==d));
}

void Dice::load(xmlNodePtr curNode) {
	xmlNodePtr childNode = curNode->children;
	clear();

	while(childNode) {
		if(NODE_NAME(childNode, "Number")) setNumber(xml::toNum<unsigned short>(childNode));
		else if(NODE_NAME(childNode, "Sides")) setSides(xml::toNum<unsigned short>(childNode));
		else if(NODE_NAME(childNode, "Plus")) setPlus(xml::toNum<short>(childNode));

		childNode = childNode->next;
	}
}

void Dice::save(xmlNodePtr curNode, const char* name) const {
	if(!number && !sides && !plus)
		return;
	xmlNodePtr childNode = xml::newStringChild(curNode, name);

	xml::saveNonZeroNum(childNode, "Number", number);
	xml::saveNonZeroNum(childNode, "Sides", sides);
	xml::saveNonZeroNum(childNode, "Plus", plus);
}

int Dice::roll() const {
	return(dice(number, sides, plus));
}
int Dice::average() const {
	return((number + number * sides) / 2 + plus);
}
int Dice::low() const {
	return(number + plus);
}
int Dice::high() const {
	return(number * sides + plus);
}
bstring Dice::str() const {
	std::ostringstream oStr;
	oStr << number << "d" << sides;
	if(plus)
		oStr << (plus > 0 ? "+" : "-") << plus;
	return(oStr.str());
}

unsigned short Dice::getNumber() const { return(number); }
unsigned short Dice::getSides() const { return(sides); }
short Dice::getPlus() const { return(plus); }

void Dice::setNumber(unsigned short n) { number = n; }
void Dice::setSides(unsigned short s) { sides = s; }
void Dice::setPlus(short p) { plus = p; }

//*********************************************************************
//						Money
//*********************************************************************

Money::Money() {
	zero();
}
void Money::load(xmlNodePtr curNode) {
	zero();
	xml::loadNumArray<unsigned long>(curNode, m, "Coin", MAX_COINS+1);
}

void Money::save(const char* name, xmlNodePtr curNode) const {
	saveULongArray(curNode, name, "Coin", m, MAX_COINS+1);
}

bool Money::isZero() const {
	for(Coin i = MIN_COINS; i < MAX_COINS; i = (Coin)((int)i + 1))
		if(m[i])
			return(false);
	return(true);
}
void Money::zero() { ::zero(m, sizeof(m)); }
bool Money::operator==(const Money& mn) const {
	for(Coin i = MIN_COINS; i < MAX_COINS; i = (Coin)((int)i + 1))
		if(m[i] != mn.get(i))
			return(false);
	return(true);
}
bool Money::operator!=(const Money& mn) const{
	return(!(*this==mn));
}
unsigned long Money::operator[](Coin c) const { return(m[c]); }
unsigned long Money::get(Coin c) const { return(m[c]); }
void Money::set(unsigned long n, Coin c) { m[c] = MIN(2000000000, n); }
void Money::add(unsigned long n, Coin c) { set(m[c] + n, c); }
void Money::sub(unsigned long n, Coin c) { set(m[c] - n, c); }
void Money::set(Money mn) {
	for(Coin i = MIN_COINS; i < MAX_COINS; i = (Coin)((int)i + 1))
		set(mn[i], i);
}
void Money::add(Money mn) {
	for(Coin i = MIN_COINS; i < MAX_COINS; i = (Coin)((int)i + 1))
		add(mn[i], i);
}
void Money::sub(Money mn) {
	for(Coin i = MIN_COINS; i < MAX_COINS; i = (Coin)((int)i + 1))
		sub(mn[i], i);
}
bstring Money::str() const {
	bool found=false;
	std::stringstream oStr;

	for(Coin i = MIN_COINS; i < MAX_COINS; i = (Coin)((int)i + 1)) {
		if(m[i]) {
			if(found)
				oStr << ", ";
			found = true;
			oStr << m[i] << " " << coinNames(i).toLower() << " coin" << (m[i] != 1 ? "s" : "");
		}
	}

	if(!found)
		oStr << "0 coins";

	return(oStr.str());
}

bstring Money::coinNames(Coin c) {
	switch(c) {
	case COPPER:
		return("Copper");
	case SILVER:
		return("Silver");
	case GOLD:
		return("Gold");
	case PLATINUM:
		return("Platinum");
	case ALANTHIUM:
		return("Alanthium");
	default:
		break;
	}
	return("");
}
const int SHOP_FOUND = 1,
	SHOP_STOCK = 2,
	SHOP_PRICE = 3,
	SHOP_REMOVE = 4,
	SHOP_NAME = 5,
	SHOP_GUILD = 6;

const char *shopSyntax =
	"Syntax: shop ^e<^xstock^e> <^citem^e> [^c$price^e]^x\n"
	"             ^e<^xprice^e> <^citem^e> <^c$price^e>^x\n"
	"             ^e<^xremove^e> <(^citem^e>^x\n"
	"             ^e<^xname^e> <^cshop name^e>^x\n"
	"             ^e<^xguild^e> <^xassign^e|^xremove^e>^x\n"
	"             ^e<^xsurvey^e>^x\n"
	"             ^e<^xhelp^e>^x\n"
	"\n"
	"             ^e<^xhire^e> <^cplayer^e>       -^x add a partial owner\n"
	"             ^e<^xfire^e> <^cplayer^e>       -^x remove a partial owner\n"
	"             ^e<^xflags^e>^x               ^e-^x view / set flags on partial owners\n"
	"             ^e<^xlog^e>^x ^e<^xdelete^e|^xrecord^e>^x ^e-^x view / delete / set log type\n"
	"             ^e<^xdestroy^e>^x             ^e-^x close this shop down\n"
	"\n";

//*********************************************************************
//						shopProfit
//*********************************************************************

long shopProfit(Object* object) {
	return((long)(object->getShopValue() - (TAX * object->getShopValue())));
}


//*********************************************************************
//						getCondition
//*********************************************************************

bstring getCondition(Object* object) {
	int percent = -1;

	// possible division by 0
	if(object->getShotscur() > 0 && object->getShotsmax() > 0)
		percent = 100 * object->getShotscur() / object->getShotsmax();

	if(	object->getType() == WEAPON ||
		object->getType() == ARMOR ||
		object->getType() == LIGHTSOURCE ||
		object->getType() == WAND ||
		object->getType() == KEY ||
		object->getType() == POISON
	) {
		if(percent >= 90)
			return("Pristine");
		else if(percent >= 75)
			return("Excellent");
		else if(percent >= 60)
			return("Good");
		else if(percent >= 45)
			return("Slightly Scratched");
		else if(percent >= 30)
			return("Badly Dented");
		else if(percent >= 10)
			return("Horrible");
		else if(percent >= 1)
			return("Terrible");
		else if(object->getShotscur() < 1)
			return("Broken");
	}
	return("Pristine");
}

//*********************************************************************
//						setupShop
//*********************************************************************

void setupShop(Player* player, Guild* guild, Room* shop, Room* storage) {
	if(guild)
		sprintf(shop->name, "%s", guild->name.c_str());
	else
		sprintf(shop->name, "%s", player->name);
	strcat(shop->name, "'s Shop of Miscellaneous Wares");

	// handle shop description
	shop->setShortDescription("You are in ");
	if(guild)
		shop->appendShortDescription(guild->name);
	else
		shop->appendShortDescription(player->name);
	shop->appendShortDescription("'s shop of magnificent wonders.");

	// switch to long description
	shop->setLongDescription("In ");
	if(guild)
		shop->appendLongDescription(guild->name);
	else
		shop->appendLongDescription(player->name);

	shop->appendLongDescription("'s shop, you may find many things which you need.\n");
	if(guild)
		shop->appendLongDescription("The guild");
	else
		shop->appendLongDescription(player->upHeShe());

	shop->appendLongDescription(" should be keeping it well stocked in order to maintain\ngood business. Should you have any complaints, you should go to\nthe nearest post office and mudmail the ");
	if(guild) {
		shop->appendLongDescription("the guild contact, ");
		shop->appendLongDescription(player->name);
		shop->appendLongDescription(".");
	} else
		shop->appendLongDescription("proprietor.");

	if(guild)
		sprintf(storage->name, "%s", guild->name.c_str());
	else
		sprintf(storage->name, "%s", player->name);
	strcat(storage->name, "'s Shop Storage Room");

	shop->saveToFile(0);
	storage->saveToFile(0);
}

//*********************************************************************
//						shopRemoveGuild
//*********************************************************************

void shopRemoveGuild(Property *p, Player* player, Room* shop, Room* storage) {
	p->setGuild(0);
	p->appendLog("", "This property is no longer associated with a guild.");

	if(!shop || !storage) {
		CatRef cr = p->ranges.front().low;
		if(loadRoom(cr, &shop)) {
			cr.id++;
			if(!loadRoom(cr, &storage))
				return;
		}
	}

	setupShop(player, 0, shop, storage);
}



//*********************************************************************
//						shopStorageRoom
//*********************************************************************

CatRef shopStorageRoom(Room *shop) {
	CatRef cr;
	if(shop->getTrapExit().id && !shop->flagIsSet(R_LOG_INTO_TRAP_ROOM))
		cr = shop->getTrapExit();
	else {
		cr = shop->info;
		cr.id++;
	}
	return(cr);
}

//*********************************************************************
//						playerShopSame
//*********************************************************************

bool playerShopSame(Player* player, Object* obj1, Object* obj2) {
	return(	obj1->showAsSame(player, obj2) &&
			getCondition(obj1) == getCondition(obj2) &&
			obj1->getShopValue() == obj2->getShopValue()
	);
}

//*********************************************************************
//						objShopName
//*********************************************************************

bstring objShopName(Object* object, int m, int flags, int pad) {
	bstring name = object->getObjStr(NULL, flags, m);
	pad -= stripColor(name).getLength();
	for(int i=0; i<pad; i++)
		name += " ";
	return(name);
}

//*********************************************************************
//						tooManyItemsInShop
//*********************************************************************

bool tooManyItemsInShop(const Player* player, const Room* storage) {
	int numObjects=0, numLines=0;
	otag *op = storage->first_obj;

	while(op) {
		numObjects++;
		numLines++;

		while(op->next_tag) {
			if(playerShopSame(0, op->obj, op->next_tag->obj)) {
				numObjects++;
				op = op->next_tag;
			} else
				break;
		}

		if(numObjects >= gConfig->getShopNumObjects() || numLines >= gConfig->getShopNumLines()) {
			player->print("There are too many items in this shop.\n");
			player->print("Please remove some before continuing.\n");
			return(true);
		}
		op = op->next_tag;
	}

	return(false);
}

//*********************************************************************
//						cmdShop
//*********************************************************************

int cmdShop(Player* player, cmd* cmnd) {
	Room* room = player->parent_rom, *storage=0;
	Object	*deed=0;
	otag	*op;
	int		action=0;
	int		flags = 0, len = strlen(cmnd->str[1]);

	if(!len) {
		player->printColor(shopSyntax);
		return(0);
	}

	if(!strncmp(cmnd->str[1], "help", len)) {
		strcpy(cmnd->str[1], "shops");
		return(cmdHelp(player, cmnd));
	} else if(
		!strcmp(cmnd->str[1], "hire") ||
		!strcmp(cmnd->str[1], "fire") ||
		!strcmp(cmnd->str[1], "flags") ||
		!strcmp(cmnd->str[1], "log") ||
		!strcmp(cmnd->str[1], "destroy")
	) {
		// give them a cross-command shortcut
		return(cmdProperties(player, cmnd));
	}

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

	if(!strncmp(cmnd->str[1], "survey", len)) {
		if(!room->flagIsSet(R_BUILD_SHOP))
			player->print("You are unable to build a shop here.\n");
		else
			player->print("This site is open for shop construction.\n");
		return(0);
	}

	if(cmnd->num < 3) {
		player->printColor(shopSyntax);
		return(0);
	}

	flags |= MAG;

	if(!strncmp(cmnd->str[1], "found", len)) {
		if(player->getLevel() < MINSHOPLEVEL && !player->isDm()) {
			player->print("You must be level %d to found a shop!\n", MINSHOPLEVEL);
			return(0);
		}
		if(!room->flagIsSet(R_BUILD_SHOP)) {
			player->print("This section of town isn't zoned for shops!\n");
			return(0);
		}
		op = player->first_obj;
		while(op) {
			deed = op->obj;
			if(!strcmp(deed->name, "shop deed"))
				if(deed->deed.belongs(room->info))
					break; // We got one!

			op = op->next_tag;
			deed = NULL;
		}
		if(!deed) {
			player->print("You need a shop deed to build a shop in this town!\nVisit the local real estate office.\n");
			return(0);
		}
		action = SHOP_FOUND;
	} else if(	!strncmp(cmnd->str[1], "stock", len) ||
				!strncmp(cmnd->str[1], "add", len) ||
				!strcmp(cmnd->str[1], "sell")
	)
		action = SHOP_STOCK;
	else if(!strncmp(cmnd->str[1], "price", len) || !strncmp(cmnd->str[1], "value", len))
		action = SHOP_PRICE;
	else if(!strncmp(cmnd->str[1], "remove", len))
		action = SHOP_REMOVE;
	else if(!strncmp(cmnd->str[1], "name", len))
		action = SHOP_NAME;
	else if(!strncmp(cmnd->str[1], "guild", len))
		action = SHOP_GUILD;


	Property* p = gConfig->getProperty(room->info);

	if(action != SHOP_FOUND) {
		if(	!p ||
			p->getType() != PROP_SHOP ||
			(	!player->isDm() &&
				!p->isOwner(player->name) &&
				!p->isPartialOwner(player->name)
			)
		) {
			player->print("This is not your shop!\n");
			return(0);
		}
	}

	PartialOwner partial;
	PartialOwner* po=0;
	if(p && !p->isOwner(player->name)) {
		po = p->getPartialOwner(player->name);
		// get default flags if not on the list
		if(!po) {
			partial.defaultFlags(p->getType());
			po = &partial;
		}
	}

	if(!loadRoom(shopStorageRoom(room), &storage)) {
		if(	action == SHOP_STOCK ||
			action == SHOP_REMOVE ||
			action == SHOP_PRICE ||
			action == SHOP_NAME
		) {
			player->print("The storage room to this shop could not be loaded!\n");
			return(0);
		}
	}

	// see if they have the authorization to do the command they want to do
	if(po) {
		if(action == SHOP_NAME || action == SHOP_GUILD) {
			player->print("Only the owner of this shop may perform that action.\n");
			return(0);
		}
		if(	(action == SHOP_STOCK && !po->flagIsSet(PROP_SHOP_CAN_STOCK)) ||
			(action == SHOP_REMOVE && !po->flagIsSet(PROP_SHOP_CAN_REMOVE)) ||
			(action == SHOP_PRICE && (!po->flagIsSet(PROP_SHOP_CAN_STOCK) || !po->flagIsSet(PROP_SHOP_CAN_REMOVE)))
		) {
			if(!player->checkStaff("You don't have the proper authorization to perform that action.\n"))
				return(0);
		}
	}

	bool limited=false;
	if(action == SHOP_FOUND) {
		Room *shop=0;
		storage = 0;

		bstring xname = getFullstrText(cmnd->fullstr, 2);
		xname.Replace("_", " ");

		if(!Property::goodExit(player, room, "Shop", xname))
			return(0);

		CatRef	cr = gConfig->getAvailableProperty(PROP_SHOP, 2);
		if(cr.id < 1) {
			player->print("Sorry, you can't found new shops right now, try again later!\n");
			return(0);
		}

		logn("log.shop", "%s just founded a shop! (%s - %s).\n", player->name, xname.c_str(), cr.str().c_str());

		shop = new Room;
		shop->info = cr;

		link_rom(shop, room->info, "out");
		link_rom(room, shop->info, xname.c_str());
		shop->setFlag(R_SHOP);
		shop->setFlag(R_INDOORS);

		storage = new Room;
		storage->info = shop->info;
		storage->info.id++;

		link_rom(storage, shop->info, "out");
		storage->setFlag(R_SHOP_STORAGE);
		storage->setTrapExit(shop->info);

		// everything to do with the player's name
		setupShop(player, 0, shop, storage);

		room->saveToFile(0);

		p = new Property;
		p->setOwner(player->name);
		p->setName(shop->name);
		p->setDateFounded();
		p->setLocation(room->name);
		p->addRange(shop->info.area, shop->info.id, shop->info.id+1);
		p->setType(PROP_SHOP);
		gConfig->addProperty(p);

		player->print("Congratulations! You are now the owner of a brand new shop.\n");
		logn("log.shops", "*** %s just built a shop! (%s - %s) (Shop %s).\n",
			player->name, room->info.str().c_str(), xname.c_str(), shop->info.str().c_str());
		broadcast(player->getSock(), player->getRoom(), "%M just opened a shop!", player );
		if(!player->flagIsSet(P_DM_INVIS))
			broadcast("### %s just opened a shop! It's located at: %s.", player->name, room->name);

		delete shop;
		delete storage;
		player->delObj(deed, true);
		delete deed;
		player->parent_rom->clearFlag(R_BUILD_SHOP);
		player->parent_rom->setFlag(R_WAS_BUILD_SHOP);
		player->parent_rom->saveToFile(0);
		return(0);
	} else if(action == SHOP_GUILD) {
		if(!strncmp(cmnd->str[2], "assign", strlen(cmnd->str[2]))) {
			if(!player->getGuild()) {
				player->print("You are not part of a guild.\n");
				return(0);
			}
			if(p->getGuild() == player->getGuild()) {
				player->print("This property is already assigned to your guild.\n");
				return(0);
			}
			Guild* guild = gConfig->getGuild(player->getGuild());
			if(!guild) {
				player->print("There was an error loading your guild.\n");
				return(0);
			}
			p->setGuild(guild->num);
			setupShop(player, guild, player->parent_rom, storage);
			player->print("This property is now associated with the guild %s.\n", guild->name.c_str());
			p->appendLog("", "This property is now associated with the guild %s.", guild->name.c_str());
			broadcastGuild(guild->num, 1, "### %s has assigned the shop located at %s as partially run by the guild.",
				player->name, p->getLocation().c_str());
		} else if(!strncmp(cmnd->str[2], "remove", strlen(cmnd->str[2]))) {
			if(!p->getGuild()) {
				player->print("This property is not assigned to a guild.\n");
				return(0);
			}
			broadcastGuild(p->getGuild(), 1, "### %s's shop located at %s is no longer partially run by the guild.",
				player->name, p->getLocation().c_str());
			shopRemoveGuild(p, player, player->parent_rom, storage);
			player->print("This property is no longer associated with a guild.\n");
		} else {
			player->print("Command not understood.\n");
			player->printColor("You may ^Wassign^x this shop to a guild or ^Wremove^x it from a guild.\n");
			return(0);
		}
		gConfig->saveProperties();
	} else if(action == SHOP_STOCK) {
		Object *obj = findObject(player, player->first_obj, cmnd, 2);
		int value=0;

		if(!obj) {
			player->print("You don't have any object with that name.\n");
			return(0);
		}

		if(obj->flagIsSet(O_NO_DROP) || obj->getType() == LOTTERYTICKET) {
			player->print("That item cannot be stocked.\n");
			return(0);
		}

		if(obj->first_obj) {
			player->print("You need to empty it before it can be stocked.\n");
			return(0);
		}

		if(obj->flagIsSet(O_STARTING)) {
			player->print("Starting items cannot be stocked.\n");
			return(0);
		}

		limited = Limited::isLimited(obj);
		if(limited && (!p || !p->isOwner(player->name))) {
			player->print("You may only handle limited items in shops where you are the primary owner.\n");
			return(0);
		}

		if(obj->getShopValue()) {
			player->print("Why would you want to sell second hand trash?\n");
			return(0);
		}
		if(obj->getShotscur() < 1 && obj->getShotscur() != obj->getShotsmax() && obj->getType() != CONTAINER) {
			player->print("Why would you want to sell such trash in your shop?\n");
			return(0);
		}

		if(tooManyItemsInShop(player, storage))
			return(0);

		if(cmnd->num > 3 && cmnd->str[3][0] == '$') {
			value = atoi(cmnd->str[3]+1);
			value = MAX(0, value);
		}
		if(!value)
			value = obj->value[GOLD];
		obj->setShopValue(value);
		obj->setFlag(O_PERM_ITEM);

		player->delObj(obj);
		obj->addToRoom(storage);
		p->appendLog(player->name, "%s stocked %s for $%d.", player->name, obj->getObjStr(NULL, flags, 1).c_str(), obj->getShopValue());
		player->printColor("You stock %s in the store for $%d.\n", obj->getObjStr(NULL, flags, 1).c_str(), obj->getShopValue());
		broadcast(player->getSock(), player->getRoom(), "%M just stocked something in this store.", player);
		// obj->shopValue
		if(limited)
			player->save(true);
		storage->saveToFile(0);
	} else if(action == SHOP_PRICE) {
		Object *obj = findObject(player, storage->first_obj, cmnd, 2);
		int value=0;

		if(!obj) {
			player->print("You're not selling anything with that name.\n");
			return(0);
		}

		if(cmnd->str[3][0] == '$') {
			value = atoi(cmnd->str[3]+1);
			value = MAX(0, value);
		}
		if(cmnd->str[3][0] != '$' || !value) {
			player->printColor(shopSyntax);
			return(0);
		}
		obj->setShopValue(value);
		p->appendLog(player->name, "%s set the price for %s to $%d.", player->name, obj->getObjStr(NULL, flags, 1).c_str(), obj->getShopValue());
		player->printColor("You set the price for %s to $%d.\n", obj->getObjStr(NULL, flags, 1).c_str(), obj->getShopValue());
		broadcast(player->getSock(), player->getRoom(), "%M just updated the prices in this store.", player);
	} else if(action == SHOP_REMOVE) {
		Object *obj = findObject(player, storage->first_obj, cmnd, 2);

		if(!obj) {
			player->print("You're not selling anything with that name.\n");
			return(0);
		}

		if(player->getWeight() + obj->getActualWeight() > player->maxWeight()) {
			player->print("That'd be too heavy for you to carry right now.\n");
			return(0);
		}

		if(player->tooBulky(obj->getActualBulk())) {
			player->print("That would be too bulky for you to carry.\n");
			return(0);
		}

		limited = Limited::isLimited(obj);
		if(limited && (!p || !p->isOwner(player->name))) {
			player->print("You may only handle unique items in shops where you are the primary owner.\n");
			return(0);
		}

		obj->deleteFromRoom();
		player->addObj(obj);
		p->appendLog(player->name, "%s removed %s.", player->name, obj->getObjStr(NULL, flags, 1).c_str());
		player->printColor("You remove %s from your store.\n", obj->getObjStr(NULL, flags, 1).c_str());
		broadcast(player->getSock(), player->getRoom(), "%M just removed something from this store.", player);

		obj->clearFlag(O_PERM_ITEM);
		obj->setShopValue(0);
		if(limited)
			player->save(true);
		storage->saveToFile(0);
	} else if(action == SHOP_NAME) {
		bstring name = getFullstrText(cmnd->fullstr, 2);

		if(!Property::goodNameDesc(player, name, "Rename shop to what?", "room name"))
			return(0);

		if(!p->getGuild()) {
			if(name.find(player->name) == bstring::npos) {
				player->print("Your shop name must contain your name.\n");
				return(0);
			}
		} else {
			Guild* guild = gConfig->getGuild(p->getGuild());
			if(!guild) {
				player->print("Error loading guild.\n");
				return(0);
			}
			if(name.find(guild->name) == bstring::npos) {
				player->print("Your shop name must contain your guild's name.\n");
				return(0);
			}
		}

		strcpy(player->parent_rom->name, name.c_str());
		p->setName(name);
		p->appendLog(player->name, "%s renamed the shop to %s.", player->name, player->parent_rom->name);
		logn("log.shops", "%s renamed shop %s to %s.\n",
			player->name, player->parent_rom->info.str().c_str(), player->parent_rom->name);
		player->print("Shop renamed to '%s'.\n", player->parent_rom->name);
		player->parent_rom->saveToFile(0);

		return(0);
	} else {
		player->printColor(shopSyntax);
	}

	return(0);
}

//*********************************************************************
//						doFilter
//*********************************************************************

bool doFilter(const Object* object, const bstring& filter) {
	if(filter == "")
		return(false);
	if(filter == object->getSubType())
		return(false);
	if(	(filter == "weapon" && object->getType() == WEAPON) ||
		(filter == "armor" && object->getType() == ARMOR) ||
		(filter == "potion" && object->getType() == POTION) ||
		(filter == "scroll" && object->getType() == SCROLL) ||
		(filter == "wand" && object->getType() == WAND) ||
		(filter == "container" && object->getType() == CONTAINER) ||
		(filter == "key" && object->getType() == KEY) ||
		((filter == "light" || filter == "lightsource") && object->getType() == LIGHTSOURCE) ||
		(filter == "song" && object->getType() == SONGSCROLL) ||
		(filter == "poison" && object->getType() == POISON) ||
		(filter == "bandage" && object->getType() == BANDAGE)
	)
		return(false);
	if(filter != "none" && filter == object->getWeaponCategory())
		return(false);
	return(true);
}

//*********************************************************************
//						cmdList
//*********************************************************************
// This function allows a player to list the items for sale within a
// shop.

int cmdList(Player* player, cmd* cmnd) {
	Room* room = player->parent_rom, *storage=0;
	Object* object=0;
	otag	*op=0;
	int		n=0;
	bstring filter = "";

	// interchange list and selection
	if(cmnd->num == 2 && cmnd->str[1][0] != '-')
		return(cmdSelection(player, cmnd));

	if(cmnd->num == 2)
		filter = &cmnd->str[1][1];

	player->clearFlag(P_AFK);

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

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

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

	if(!loadRoom(shopStorageRoom(room), &storage)) {
		player->print("Nothing to buy.\n");
		return(0);
	}

	if(!storage->flagIsSet(R_SHOP_STORAGE)) {
		player->print("This is not a shop.\n");
		return(0);
	}

	Property* p = gConfig->getProperty(room->info);

	// not a player run shop
	if(!p || p->getType() != PROP_SHOP) {
		Money cost;

		if(!Faction::willDoBusinessWith(player, player->parent_rom->getFaction())) {
			player->print("The shopkeeper refuses to do business with you.\n");
			return(0);
		}

		storage->addPermObj();
		op = storage->first_obj;
		if(op) {
			player->print("You may buy:");
			if(filter != "")
				player->printColor(" ^Y(filtering on \"%s\")", filter.c_str());
			player->print("\n");
			while(op) {
				object = op->obj;
				op = op->next_tag;

				if(doFilter(object, filter))
					continue;

				if(!strcmp(object->name, "bail"))
					object->value = bailCost(player);

				cost = buyAmount(player, player->parent_rom, object, true);

				// even if they love you, lottery tickets are the same price
				if(object->getType() == LOTTERYTICKET) {
					object->value.set(gConfig->getLotteryTicketPrice(), GOLD);
					cost = object->value;
				}

				player->printColor("   %s   Cost: %s\n",
					objShopName(object, 1, CAP | (player->isEffected("detect-magic") ? MAG : 0), 52).c_str(),
					cost.str().c_str());
			}
			player->print("\n");
		} else
			player->print("There is nothing for sale.\n");
	} else {
		// We've got a player run shop here...different formating them
		int num = 0, flags = 0, m=0;
		bool owner=false;

		flags |= CAP;
		flags |= MAG;

		if(p->isOwner(player->name)) {
			player->print("You are selling:");
			owner = true;
		} else {
			player->print("%s is selling:", p->getOwner().c_str());
			if(p->isPartialOwner(player->name))
				owner = true;
		}

		if(filter != "")
			player->printColor(" ^Y(filtering on \"%s\")", filter.c_str());
		player->print("\n");

		op = storage->first_obj;
		while(op) {
			n++;
			m=1;

			while(op->next_tag) {
				if(playerShopSame(player, op->obj, op->next_tag->obj)) {
					m++;
					op = op->next_tag;
				} else
					break;
			}

			object = op->obj;
			op = op->next_tag;

			num++;

			if(doFilter(object, filter))
				continue;

			if(n == 1)
				player->printColor("^WNum         Item                        Price      Condition\n");
			player->printColor("%2d> %s $%-9ld %-12s", num,
				objShopName(object, m, flags, 35).c_str(),
				object->getShopValue(), getCondition(object).c_str());
			if(owner)
				player->print(" Profit: %ld", shopProfit(object));
			player->print("\n");
		}
		if(!n)
			player->print("Absolutely nothing!\n");
	}

	return(0);
}

//*********************************************************************
//						cmdPurchase
//*********************************************************************
// purchase allows a player to buy an item from a monster.  The
// purchase item flag must be set, and the monster must have an
// object to sell.  The object for sale is determined by the first
// object listed in carried items.

int cmdPurchase(Player* player, cmd* cmnd) {
	Monster	*creature=0;
	Object	*object=0;
	int		maxitem=0;
	CatRef	obj_num[10];
	Money cost;
	int 	i=0, j=0, found=0, match=0;


	if(cmnd->num == 2)
		return(cmdBuy(player, cmnd));


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

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

	if(cmnd->num < 3 ) {
		player->print("Syntax: purchase <item> <monster>\n");
		return(0);
	}

	creature = player->getRoom()->findMonster(player, cmnd, 2);
	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->flagIsSet(M_CAN_PURCHASE_FROM)) {
		player->print("You cannot purchase objects from %N.\n",creature);
		return(0);
	}

	for(i=0; i<10; i++) {
		if(creature->carry[i].info.id > 0) {
			found = 0;
			for(j=0; j< maxitem; j++)
				if(creature->carry[i].info == obj_num[j])
					found = 1;
			if(!found) {
				maxitem++;
				obj_num[i] = creature->carry[i].info;
			}
		}
	}

	if(!maxitem) {
		player->print("%M has nothing to sell.\n",creature);
		return(0);
	}

	found = 0;
	for(i=0; i<maxitem; i++) {
		if(loadObject(creature->carry[i].info, &object)) {
			if(player->canSee(object) && keyTxtEqual(object, cmnd->str[1])) {
				match++;
				if(match == cmnd->val[1]) {
					found = 1;
					break;
				} else
					delete object;
			} else
				delete object;
		}
	}

	if(!found) {
		player->print("%M says, \"Sorry, I don't have any of those to sell.\"\n", creature);
		return(0);
	}

	object->init();

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

	if(player->tooBulky(object->getActualBulk())) {
		player->print("That would be too bulky.\nClean your inventory first.\n");
		delete object;
		return(0);
	}


	if(!Unique::canGet(player, object)) {
		player->print("You are unable to purchase that limited item at this time.\n");
		delete object;
		return(0);
	}
	if(!Lore::canHave(player, object, false)) {
		player->print("You cannot purchased that item.\nIt is a limited item of which you cannot carry any more.\n");
		delete object;
		return(0);
	}
	if(!Lore::canHave(player, object, true)) {
		player->print("You cannot purchased that item.\nIt contains a limited item that you cannot carry.\n");
		delete object;
		return(0);
	}



	cost = buyAmount(player, creature, object, true);

	if(player->coins[GOLD] < cost[GOLD]) {
		player->print("%M says \"The price is %s, and not a copper piece less!\"\n", creature, cost.str().c_str());
		delete object;
	} else {

		player->print("You pay %N %s gold pieces.\n", creature, cost.str().c_str());
		player->printColor("%M says, \"%sHere is your %s.\"\n", creature,
			Faction::getAttitude(player->getFactionStanding(creature->getPrimeFaction())) < Faction::INDIFFERENT ? "Hrmph. " : "Thank you very much. ",
			object->name);
		broadcast(player->getSock(), player->getRoom(), "%M pays %N %s for %P.\n", player, creature, cost.str().c_str(), object);
		player->coins.sub(cost);

		player->doHaggling(creature, object, BUY);

		Limited::addOwner(player, object);
		player->addObj(object);
	}

	return(0);
}


//*********************************************************************
//						cmdSelection
//*********************************************************************
// The selection command lists all the items  a monster is selling.
// The monster needs the M_CAN_PURCHASE_FROM flag set to denote it can sell.

int cmdSelection(Player* player, cmd* cmnd) {
	Monster	*creature=0;
	Object	*object=0;
	CatRef	obj_list[10];
	int 	i=0, j=0, found=0, maxitem=0;
	bstring filter = "";

	// interchange list and selection
	if(cmnd->num == 1 || cmnd->str[1][0] == '-')
		return(cmdList(player, cmnd));

	if(cmnd->num == 3)
		filter = &cmnd->str[2][1];

	player->clearFlag(P_AFK);

	if(!player->ableToDoCommand())
		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->flagIsSet(M_CAN_PURCHASE_FROM)) {
		player->print("%M is not selling anything.\n",creature);
		return(0);
	}

	for(i=0; i<10; i++) {
		if(creature->carry[i].info.id > 0) {
			found = 0;
			for(j=0; j< maxitem; j++)
				if(creature->carry[i].info == obj_list[j])
					found = 1;

			if(!found) {
				maxitem++;
				obj_list[i] = creature->carry[i].info;
			}
		}
	}

	if(!maxitem) {
		player->print("%M has nothing to sell.\n", creature);
		return(0);
	}

	player->print("%M is currently selling:", creature);
	if(filter != "")
		player->printColor(" ^Y(filtering on \"%s\")", filter.c_str());
	player->print("\n");

	Money cost;
	for(i=0; i<maxitem; i++) {
		if(!creature->carry[i].info.id || !loadObject(creature->carry[i].info, &object)) {
			player->print("%d) Out of stock.\n", i+1);
		} else {
			if(!doFilter(object, filter)) {
				cost = buyAmount(player, creature, object, true);

				player->printColor("%d) %s    %s\n", i+1, objShopName(object, 1, 0, 35).c_str(),
					cost.str().c_str());
			}
			delete object;
		}
	}

	player->print("\n");
	return(0);
}

//*********************************************************************
//						cmdBuy
//*********************************************************************
// This function allows a player to buy something from a shop.

int cmdBuy(Player* player, cmd* cmnd) {
	Room* room = player->parent_rom, *storage=0;
	Object	*object=0, *object2=0;
	otag	*op=0;
	int		num=0, n=1;

	if(cmnd->num == 3)
		return(cmdPurchase(player, cmnd));

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


	if(!player->ableToDoCommand())
		return(0);
	if(player->getClass() == BUILDER) {
		player->print("You may not buy things from shops.\n");
		return(0);
	}

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

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

	if(!loadRoom(shopStorageRoom(room), &storage)) {
		player->print("Nothing to buy.\n");
		return(0);
	}

	if(!storage->flagIsSet(R_SHOP_STORAGE)) {
		player->print("This is not a shop.\n");
		return(0);
	}

	//*********************************************************************
	Property* p = gConfig->getProperty(room->info);

	if(p && p->getType() == PROP_SHOP) {
		// We have a player run shop here!!!
		Player* owner=0;
		Guild*	guild=0;
		int		flags=0;
		long	deposit=0;
		bool	online=false;

		if(p->isOwner(player->name)) {
			player->print("Why would you want to buy stuff from your own shop?\n");
			return(0);
		}
		flags |= MAG;

		if(cmnd->str[1][0] != '#' || !cmnd->str[1][1]) {
			player->print("What item would you like to buy?\n");
			return(0);
		}
		num = atoi(cmnd->str[1]+1);

		op = storage->first_obj;
		while(op && num != n) {
			while(op->next_tag) {
				if((playerShopSame(player, op->obj, op->next_tag->obj))) {
					op = op->next_tag;
				} else
					break;
			}
			n++;
			op = op->next_tag;
		}
		if(!op) {
			player->print("That isn't being sold here.\n");
			return(0);
		}
		object = op->obj;

		// load the guild a little bit earlier
		if(p->getGuild())
			guild = gConfig->getGuild(p->getGuild());

		if(player->coins[GOLD] < object->getShopValue()) {
			if(guild)
				player->print("You can't afford %s's outrageous prices!\n", guild->name.c_str());
			else
				player->print("You can't afford %s's outrageous prices!\n", p->getOwner().c_str());
			return(0);
		}

		if(player->getWeight() + object->getActualWeight() > player->maxWeight()) {
			player->print("You couldn't possibly carry anymore.\n");
			return(0);
		}

		if(player->tooBulky(object->getActualBulk()) ) {
			player->print("No more will fit in your inventory right now.\n");
			return(0);
		}


		if(!Unique::canGet(player, object, true)) {
			player->print("You are unable to purchase that limited item at this time.\n");
			return(0);
		}
		if(!Lore::canHave(player, object, false)) {
			player->print("You cannot purchased that item.\nIt is a limited item of which you cannot carry any more.\n");
			return(0);
		}
		if(!Lore::canHave(player, object, true)) {
			player->print("You cannot purchased that item.\nIt contains a limited item that you cannot carry.\n");
			return(0);
		}



		player->printColor("You buy a %s.\n", object->getObjStr(NULL, flags, 1).c_str());
		broadcast(player->getSock(), player->getRoom(), "%M just bought %1P.", player, object);
		object->clearFlag(O_PERM_INV_ITEM);
		object->clearFlag(O_PERM_ITEM);
		object->clearFlag(O_TEMP_PERM);

		player->coins.sub(object->getShopValue(), GOLD);

		if(object->getType() == LOTTERYTICKET || object->getType() == BANDAGE)
			object->setShopValue(0);


		owner = gServer->findPlayer(p->getOwner().c_str());
		if(!owner) {
			if(!loadPlayer(p->getOwner().c_str(), &owner)) {
				loge("Shop (%s) without a valid owner (%s).\n",
					room->info.str().c_str(), p->getOwner().c_str());
				player->save(true);
				return(0);
			}
		} else
			online = true;



		object->deleteFromRoom();
		Limited::transferOwner(owner, player, object);
		player->addObj(object);

		deposit = shopProfit(object);

		if(!guild) {
			owner->bank.add(deposit, GOLD);
			banklog(owner->name, "PROFIT from sale of %s to %s: %ld [Balance: %s]\n",
				object->getObjStr(NULL, flags, 1).c_str(), player->name, deposit, owner->bank.str().c_str());
			if(online && !owner->flagIsSet(P_DONT_SHOW_SHOP_PROFITS))
				owner->print("*** You made $%d profit from the sale of %s to %s.\n",
					deposit, object->getObjStr(NULL, flags, 1).c_str(), player->name);
			owner->save(online);
		} else {
			guild->bank.add(deposit, GOLD);
			guildBankLog(guild->num, "PROFIT from sale of %s to %s: %ld [Balance: %s]\n",
				object->getObjStr(NULL, flags, 1).c_str(), player->name, deposit, guild->bank.str().c_str());
			gConfig->saveGuilds();
		}

		if(!online)
			free_crt(owner);
		player->save(true);
		return(0);
		//*********************************************************************
	} else {
		// Not a player run shop
		object = findObject(player, storage->first_obj, cmnd);

		if(!object) {
			player->print("That's not being sold.\n");
			return(0);
		}

		if(!Faction::willDoBusinessWith(player, player->parent_rom->getFaction())) {
			player->print("The shopkeeper refuses to do business with you.\n");
			return(0);
		}

		if(!strcmp(object->name, "storage room")) {
			if(!room->flagIsSet(R_STORAGE_ROOM_SALE)) {
				player->print("You can't buy storage room's here.\n");
				return(0);
			}

			CatRef	cr = gConfig->getSingleProperty(player, PROP_STORAGE);
			if(cr.id) {
				player->print("You are already affiliated with a storage room.\nOnly one is allowed per player.\n");
				return(0);
			}
		}

		if(!strcmp(object->name, "bail"))
			object->value = bailCost(player);

		Money cost = buyAmount(player, player->parent_rom, object, true);

		// even if they love you, lottery tickets are the same price
		if(object->getType() == LOTTERYTICKET) {
			object->value.set(gConfig->getLotteryTicketPrice(), GOLD);
			cost = object->value;
		}

		if(!strcmp(object->name, "bail")) {
			// Depends on level for bail
			// 2k per lvl to get out
			if(player->coins[GOLD] < cost[GOLD]) {
				player->print("You don't have enough gold to post bail.\n");
				return(0);
			}
		} else {

			if(player->coins[GOLD] < cost[GOLD]) {
				player->print("You don't have enough gold.\n");
				return(0);
			}

			if( player->getWeight() + object->getActualWeight() > player->maxWeight() &&
				!player->checkStaff("You can't carry anymore.\n") )
				return(0);

			if(player->tooBulky(object->getActualBulk()) ) {
				player->print("No more will fit in your inventory right now.\n");
				return(0);
			}

		}

		// Not buying a storage room or bail
		if(strcmp(object->name, "storage room") && strcmp(object->name, "bail")) {

			player->unhide();
			if(object->getType() == LOTTERYTICKET) {
				if(gConfig->getLotteryEnabled() == 0) {
					player->print("Sorry, lottery tickets are not currently being sold.\n");
					return(0);
				}

				if(createLotteryTicket(&object2, player->name) < 0) {
					player->print("Sorry, lottery tickets are not currently being sold.\n");
					return(0);
				}
			// Not a lottery Ticket
			} else {
				object2 = new Object;
				if(!object2)
					merror("buy", FATAL);

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

			if(object2)
				object2->init();

			if(!Unique::canGet(player, object)) {
				player->print("You are unable to purchase that limited item at this time.\n");
				if(object2)
					delete object2;
				return(0);
			}
			if(!Lore::canHave(player, object, false)) {
				player->print("You cannot purchased that item.\nIt is a limited item of which you cannot carry any more.\n");
				if(object2)
					delete object2;
				return(0);
			}
			if(!Lore::canHave(player, object, true)) {
				player->print("You cannot purchased that item.\nIt contains a limited item that you cannot carry.\n");
				if(object2)
					delete object2;
				return(0);
			}

			bool isDeed = object2->deed.low.id || object2->deed.high;

			// buying a deed
			if(isDeed && player->getLevel() < MINSHOPLEVEL) {
				player->print("You must be at least level %d to buy a shop deed.\n", MINSHOPLEVEL);
				if(object2)
					delete object2;
				return(0);
			}

			Limited::addOwner(player, object2);
			player->addObj(object2);
			player->printColor("You bought %1P.\n", object2);

			player->coins.sub(cost);

			player->doHaggling(0, object2, BUY);

			if(object2->getType() == LOTTERYTICKET || object2->getType() == BANDAGE)
				object2->value.zero();

			player->print("You have %ld gold left.", player->coins[GOLD]);
			if(object2->getType() == LOTTERYTICKET)
				player->print(" Your numbers are %02d %02d %02d %02d %02d  (%02d).", object2->getLotteryNumbers(0),
						object2->getLotteryNumbers(1), object2->getLotteryNumbers(2),
						object2->getLotteryNumbers(3), object2->getLotteryNumbers(4), object2->getLotteryNumbers(5));
			player->print("\n");

			if(strcmp(object2->name, "storage room") && strcmp(object2->name, "bail") && object2->getType() != LOTTERYTICKET)
				object2->setFlag(O_JUST_BOUGHT);

			broadcast(player->getSock(), player->getRoom(), "%M bought %1P.", player, object2);

			player->bug("%s just bought %s for %s in room %s.\n",
				player->name, object2->name, cost.str().c_str(), player->getRoom()->fullName().c_str());

			logn("log.commerce", "%s just bought %s for %s in room %s.\n",
				player->name, object2->name, cost.str().c_str(), player->getRoom()->fullName().c_str());

			if(isDeed) {
				int flag=0;
				bstring type = "";
				bstring name = object2->name;

				if(name == "shop deed") {
					type = "shop";
					flag = R_BUILD_SHOP;
				} else if(name.left(14) == "guildhall deed") {
					type = "guildhall";
					flag = R_BUILD_GUILDHALL;
				}

				if(flag) {
					player->printColor("^WNote:^x Please wait while the mud searches for available space to build a %s.\n", type.c_str());
					player->print("If it cannot find empty space, you may refund the deed.\n");
					gServer->isAvailableSpace(player->name, object2->deed, flag);
				}
			}

		} else if(!strcmp(object->name, "storage room")) {

			CatRef	cr = gConfig->getAvailableProperty(PROP_STORAGE, 1);
			if(cr.id < 1) {
				player->print("The shopkeeper says, \"Sorry, I don't have any more rooms available!\"\n");
				return(0);
			}
			// TODO: Dom: hardcoded?
			player->coins.sub(250000, GOLD);

			player->print("The shopkeeper says, \"Congratulations, you are now the proud owner of a new storage room. Don't forget, to get in you must also buy a key.\"\n");
			player->print("You have %s left.\n", player->coins.str().c_str());
			broadcast(player->getSock(), player->getRoom(), "%M just bought a storage room.", player);
			logn("log.storage", "%s bought storage room %s.\n",
				player->name, cr.str().c_str());

			createStorage(cr, player->name);

		} else {
			// Buying bail to get outta jail!
			Exit	*exit=0;

			// Find the cell door
			exit = findExit(player, "cell", 1);
			player->unhide();

			if(!exit || !exit->flagIsSet(X_LOCKED)) {
				player->print("You are in no need of bail!\n");
				return(0);
			}

			exit->clearFlag(X_LOCKED);
			exit->ltime.ltime = time(0);
			player->coins.sub(cost);
			player->print("You bail yourself out for %s gold.\n", cost.str().c_str());
			broadcast("### %M just bailed %sself out of jail!", player, player->himHer());
		}
	}
	return(0);
}



//*********************************************************************
//							isAvailableSpace
//*********************************************************************

bstring Config::isAvailableSpace(Range range, int flag) {
	if(range.low.id > range.high)
		return("0");

	Room* room=0;
	CatRef cr;
	int high = range.high;

	cr.setArea(range.low.area);
	cr.id = range.low.id;
	if(range.low.id == -1 && range.high == -1) {
		cr.id = 1;
		high = RMAX;
	}

	for(; cr.id < high; cr.id++) {
		if(!loadRoom(cr, &room))
			continue;
		if(room->flagIsSet(flag))
			return(cr.rstr());
	}
	return("0");
}

void Server::isAvailableSpace(bstring mover, Range range, int flag) {
	// prepare to split the mud process
	int fds[2];
	if(pipe(fds) == -1) {
		printf("RS: Error with pipe!\n");
		abort();
	}
	int pid = fork();
	if(!pid) {
		// this is the child process
		// Close the reading end, we'll only be writing
		close(fds[0]);
		// Remap stdout to fds[1] so cout will print to fds[1] and we can
		// read it in from the mud
		if(dup2(fds[1], STDOUT_FILENO) != STDOUT_FILENO) {
			std::cout << "RS: Error with dup2.\n";
			return;
		}
		std::cout << gConfig->isAvailableSpace(range, flag);
		exit(0);
	} else {
		// this is the parent process
		// Close the writing end, we'll only be reading
		close(fds[1]);
		nonBlock(fds[0]);
		std::cout << "Watching Child FindSpace for(" << mover << ") running with pid " << pid
			 	  << " reading from fd " << fds[0] << std::endl;
		// Let the server know we're monitoring this child process
		gServer->addChild(pid, CHILD_FIND_SPACE, fds[0], mover);
	}
}

void Server::isAvailableSpace(bstring mover, bool isSpace) {
	const Player* player = gServer->findPlayer(mover);
	if(player) {
		if(isSpace) {
			player->printColor("^WThere is available space.\n");
		} else {
			player->printColor("^WNo available space was found. You should refund the deed.\n");
		}
	}
}

//*********************************************************************
//						cmdSell
//*********************************************************************
// This function will allow a player to sell an object in a pawn shop

int cmdSell(Player* player, cmd* cmnd) {
	Object	*object=0;
	bool	poorquality=false;
	Money value;


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

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

	if(!player->getRoom()->flagIsSet(R_PAWN_SHOP)) {
		player->print("This is not a pawn shop.\n");
		return(0);
	}

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

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

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

	if(object->flagIsSet(O_KEEP)) {
		player->printColor("%O is currently in safe keeping. Unkeep it to sell it.\n", object);
		return(0);
	}

	if(object->first_obj) {
		player->print("You don't want to sell that!\nThere's something inside it!\n");
		return(0);
	}

	if(!Faction::willDoBusinessWith(player, player->parent_rom->getFaction())) {
		player->print("The shopkeeper refuses to do business with you.\n");
		return(0);
	}

	value = sellAmount(player, player->parent_rom, object, false);

	player->computeLuck();
	// Luck for sale of items
	//	gold = ((Ply[fd].extr->getLuck()*gold)/100);

	if((object->getType() == WEAPON || object->getType() == ARMOR) && object->getShotscur() <= object->getShotsmax()/8)
		poorquality = true;

	if((object->getType() == WAND || object->getType() > MISC || object->getType() == KEY) && object->getShotscur() < 1)
		poorquality = true;

	if(	value[GOLD] < 20 ||
		poorquality ||
		object->getType() == SCROLL ||
		// objects flagged as eatable/drinkable can be sold
		(object->getType() == POTION && !object->flagIsSet(O_EATABLE) && !object->flagIsSet(O_DRINKABLE)) ||
		object->getType() == SONGSCROLL ||
		object->flagIsSet(O_STARTING)
	) {
		switch(mrand(1,5)) {
		case 1:
			player->print("The shopkeep says, \"I'm not interested in that.\"\n");
			break;
		case 2:
			player->print("The shopkeep says, \"I don't want that.\"\n");
			break;
		case 3:
			player->print("The shopkeep says, \"I don't have any use for that.\"\n");
			break;
		case 4:
			player->print("The shopkeep says, \"You must be joking; that's worthless!\"\n");
			break;
		default:
			player->print("The shopkeep says, \"I won't buy that crap from you.\"\n");
			break;
		}
		return(0);
	}

	// This prevents high level thieves from possibly cashing in on shoplifting.
	if(object->flagIsSet(O_WAS_SHOPLIFTED)) {
		player->print("The shopkeep says, \"That was stolen from a shop somewhere. I won't buy it!\"\n");
		return(0);
	}

	if(object->flagIsSet(O_NO_PAWN)) {
		player->print("The shopkeep says, \"I refuse to buy that! Get out!\"\n");
		return(0);
	}

	player->printColor("The shopkeep gives you %s for %P.\n", value.str().c_str(), object);
	broadcast(player->getSock(), player->getRoom(), "%M sells %1P.", player, object);

	object->refund.zero();
	object->refund.set(value);
	player->doHaggling(0, object, SELL);

	player->bug("%s sold %s in room %s.\n", player->name, object->name,
		player->getRoom()->fullName().c_str());
	logn("log.commerce", "%s sold %s in room %s.\n", player->name, object->name,
		player->getRoom()->fullName().c_str());


	player->coins.add(value);
	player->delObj(object, true);
	player->setLastPawn(object);
	return(0);
}

//*********************************************************************
//						cmdValue
//*********************************************************************
// This function allows a player to find out the pawn-shop value of an
// object, if they are in the pawn shop.

int cmdValue(Player* player, cmd* cmnd) {
	Object	*object=0;
	Money value;


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

	if(!player->getRoom()->flagIsSet(R_PAWN_SHOP)) {
		player->print("You must be in a pawn shop.\n");
		return(0);
	}

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

	player->unhide();

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

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

	if(!Faction::willDoBusinessWith(player, player->parent_rom->getFaction())) {
		player->print("The shopkeeper refuses to do business with you.\n");
		return(0);
	}

	value = sellAmount(player, player->parent_rom, object, false);

	player->printColor("The shopkeep says, \"%O's worth %s.\"\n", object, value.str().c_str());

	broadcast(player->getSock(), player->getRoom(), "%M gets %P appraised.", player, object);

	return(0);
}



//*********************************************************************
//						cmdRefund
//*********************************************************************

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


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

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

	if(!player->getRoom()->flagIsSet(R_SHOP)) {
		player->print("This is not a shop.\n");
		return(0);
	}

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

	object = findObject(player, player->first_obj, cmnd);
	if(!object) {
		player->print("You don't have that in your inventory.\n");
		return(0);
	}

	if(!object->flagIsSet(O_JUST_BOUGHT) || (object->getShotscur() < object->getShotsmax()) || object->getShopValue()) {
		player->print("The shopkeep says, \"I don't return money for used goods!\"\n");
		return(0);
	}

	if(object->getRecipe()) {
		player->print("You cannot return intellectual property.\n");
		return(0);
	}

	if(!object->refund.isZero())
		object->value.add(object->refund);

	player->coins.add(object->value);
	player->printColor("The shopkeep takes the %s from you and returns %s to you.\n", object->name, object->value.str().c_str());
	broadcast(player->getSock(), player->getRoom(), "%M refunds %P.", player, object);

	player->delObj(object, true);
	delete object;
	return(0);
}

//*********************************************************************
//						failTrade
//*********************************************************************

void failTrade(const Player* player, const Object* object, const Monster* target, Object* trade=0, bool useHeShe=true) {
	if(useHeShe)
		player->printColor("%s gives you back %P.\n", target->upHeShe(), object);
	else
		player->printColor("%M gives you back %P.\n", target, object);
	broadcast(player->getSock(), player->getRoom(), "%M tried to trade %P with %N.",
		player, object, target);
	if(trade)
		delete trade;
}

//*********************************************************************
//						canReceiveObject
//*********************************************************************
// Determine if the player can receive the object (as a result of a trade
// or completing a quest. This code does NOT handle bulk/weight, as that
// is tracked on a broader scale, rather than for each item.

bool canReceiveObject(const Player* player, Object* object, const Monster* monster, bool isTrade, bool doQuestOwner, bool maybeTryAgainMsg) {
	bstring tryAgain = "";
	// if the result of the quest/trade is a random item and we fail based on that random item,
	// they may be able to retry the quest/trade.
	if(maybeTryAgainMsg) {
		if(isTrade)
			tryAgain = "As the result of the trade is a random item, you may be able to attempt the trade again.\n";
		else
			tryAgain = "As the quest reward is a random item, you may be able to attempt to complete the quest again.\n";
	}

	// check for unique items
	if(!Unique::canGet(player, object)) {
		if(isTrade)
			player->print("%M cannot trade for that right now.\n", monster);
		else
			player->print("You cannot complete that quest right now.\nThe unique item reward is already in the game.\n");
		if(tryAgain != "")
			player->print(tryAgain.c_str());
		return(false);
	}

	// check for lore items
	if(!Lore::canHave(player, object, false)) {
		if(isTrade)
			player->printColor("%M cannot trade for that right now, as you already have %P.\n", monster, object);
		else
			player->printColor("You cannot complete that quest right now, as you already have %P.\n", object);
		if(tryAgain != "")
			player->print(tryAgain.c_str());
		return(false);
	}

	// check for quest items
	if(	object->getQuestnum() && player->questIsSet(object->getQuestnum()-1) &&
		!player->checkStaff("You have already fulfilled that quest.\n")
	) {
		if(tryAgain != "")
			player->print(tryAgain.c_str());
		return(false);
	}

	// Quest results set player ownership, so this will always be true. If this is a trade,
	// this will be true if the object the player is giving the monster is owned by them.
	if(doQuestOwner)
		object->setQuestOwner(player);
	return(true);
}

//*********************************************************************
//						abortItemList
//*********************************************************************
// If we cannot give them the objects for whatever reason, we must
// delete all the objects we have already loaded.

void abortItemList(std::list<Object*> &objects) {
	Object* object=0;

	while(objects.size()) {
		object = objects.front();
		delete object;
		objects.pop_front();
	}

	objects.clear();
}

//*********************************************************************
//						prepareItemList
//*********************************************************************
// When attempting to trade or complete a quest, we first check to see if they are
// able to receive all of the items as a result. If they cannot, we prevent them
// from completing the trade or quest, give them a reason why and return false. If
// they are able to receive all the items, we return a list of objects they will
// receive in the "objects" parameter.

bool prepareItemList(const Player* player, std::list<Object*> &objects, Object* object, const Monster* monster, bool isTrade, bool doQuestOwner, int totalBulk) {
	std::list<CatRef>::const_iterator it;
	Object* toGive=0;
	// if they're getting a random result of a quest or trade,
	// they might be able to try again should it fail
	bool maybeTryAgainMsg = !object->flagIsSet(O_LOAD_ALL) && object->randomObjects.size();

	// count the bulk we will be getting rid of if this transaction is successful
	if(totalBulk > 0)
		totalBulk *= -1;

	object->init();

	// if they can't get this object, we need to delete everything
	// we've already loaded so far
	if(!canReceiveObject(player, object, monster, isTrade, doQuestOwner, maybeTryAgainMsg)) {
		abortItemList(objects);
		delete object;
		return(false);
	}

	if(object->flagIsSet(O_LOAD_ALL) && object->randomObjects.size()) {
		// in this case, we give them a lot of items
		for(it = object->randomObjects.begin(); it != object->randomObjects.end(); it++) {
			if(!loadObject(*it, &toGive))
				continue;
			objects.push_back(toGive);
			totalBulk += toGive->getActualBulk();
			// if they can't get this object, we need to delete everything
			// we've already loaded so far
			if(!canReceiveObject(player, toGive, monster, isTrade, doQuestOwner, false)) {
				abortItemList(objects);
				delete object;
				return(false);
			}
		}
		// they aren't actually getting the "object" item
		delete object;
	} else {
		// the simple, common scenario
		objects.push_back(object);
		totalBulk += object->getActualBulk();
	}

	// check total bulk: if they can't get the objects, we need to delete everything
	// we've already loaded so far
	if(player->tooBulky(totalBulk)) {
		if(isTrade)
			player->print("What %N wants to give you is too bulky for you.\n", monster);
		abortItemList(objects);
		return(false);
	}

	return(true);
}

//*********************************************************************
//						cmdTrade
//*********************************************************************

int cmdTrade(Player* player, cmd* cmnd) {
	Monster	*creature=0;
	Object	*object=0, *trade=0;
	Carry	invTrade, invResult;
	int		i=0, numTrade = cmnd->val[0];
	bool	badNumTrade=false, found=false, hasTrade=false, doQuestOwner=false;
	int		flags = player->displayFlags();

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

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

	if(cmnd->num < 3) {
		player->print("Syntax: trade [num] <item> <monster>\n");
		return(0);
	}

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

	if(!creature->flagIsSet(M_TRADES)) {
		player->print("You can't trade with %N.\n", creature);
		return(0);
	}

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

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


	if(object->flagIsSet(O_BROKEN_BY_CMD)) {
		player->print("Who would want that? It's broken!\n");
		return(0);
	}

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

	/*
	for(i=0; i<5; i++) {
		if(creature->carry[i].info.id > 0) {
			found = 0;
			for(j=0; j< maxitem; j++)
				if(creature->carry[i].info == obj_list[j][0].info)
					found = 1;

			if(!found) {
				maxitem++;
				obj_list[i][0] = creature->carry[i];
				obj_list[i][1] = creature->carry[i+5];
			}
		}
	}

	if(!maxitem) {
		player->print("%M has nothing to trade.\n", creature);
		return(0);
	}
	*/

	for(i=0; i<5; i++) {
		// See if they have ANY items to trade - we'll give them a special message if they don't
		if(!creature->carry[i].info.id)
			continue;
		hasTrade = true;
		// If this the item we want?
		if(creature->carry[i].info != object->info)
			continue;

		found = true;
		invTrade = creature->carry[i];
		invResult = creature->carry[i+5];

		if(invTrade.numTrade > 1 && invTrade.numTrade != numTrade) {
			// If we found a match, but it didnt have all the items we wanted,
			// flag this so we can print a message about it later.
			badNumTrade = true;
		} else {
			// If we found an exact match, stop now and ignore any previous partial matches
			badNumTrade = false;
			break;
		}
	}

	if(!hasTrade) {
		player->print("%M has nothing to trade.\n", creature);
		return(0);
	}

	if(!found || !object->info.id || ((object->getShotscur() <= object->getShotsmax()/10) && object->getType() != MISC)) {
		player->print("%M says, \"I don't want that!\"\n", creature);
		failTrade(player, object, creature);
		return(0);
	}

	if(badNumTrade) {
		player->print("%M says, \"That's now how many I want!\"\n", creature);
		failTrade(player, object, creature);
		return(0);
	}

	if(!object->isQuestOwner(player)) {
		player->print("%M says, \"That doesn't belong to you!\"\n", creature);
		failTrade(player, object, creature);
		return(0);
	}

	if(!loadObject((invResult.info), &trade)) {
		player->print("%M says, \"Sorry, I don't have anything for you in return for that. Keep it.\"\n", creature);
		failTrade(player, object, creature);
		return(0);
	}

	std::list<Object*> objects;
	std::list<Object*>::const_iterator oIt;

	// If the quest owner is set on the item we are giving, set the quest owner
	// on the item(s) we are receiving.
	doQuestOwner = (object->getQuestOwner() != "");

	if(!prepareItemList(player, objects, trade, creature, true, doQuestOwner, object->getActualBulk())) {
		// in the event of failure, prepareItemList() will delete the trade object
		failTrade(player, object, creature);
		return(0);
	}

	// are they trying to trade multiple objects
	if(numTrade > 1) {
		otag *marker=0, *op = player->first_obj;
		Object *toDelete=0;
		int numObjects=0;

		// find the first occurance of the object
		while(op && op->obj->info != object->info)
			op = op->next_tag;
		if(!op) {
			player->print("%M gets confused and cannot trade for that right now.\n", creature);
			failTrade(player, object, creature, trade);
			return(0);
		}

		// save our position to make this faster
		marker = op;
		// see if we have enough objects
		while(op && op->obj->info == object->info && numObjects != numTrade) {
			numObjects++;
			if(!op->obj->isQuestOwner(player)) {
				player->print("%M says, \"That doesn't belong to you!\"\n", creature);
				failTrade(player, object, creature, trade);
				return(0);
			}
			op = op->next_tag;
		}
		if(numObjects != numTrade) {
			player->print("You don't have that many items.\n");
			failTrade(player, object, creature, trade, false);
			return(0);
		}

		// if everything is successful, start from the marker and start removing objects
		while(numObjects) {
			toDelete = marker->obj;
			marker = marker->next_tag;
			player->delObj(toDelete, true);
			creature->addObj(toDelete);
			numObjects--;
		}
	} else {
		// the simple, common scenario
		player->delObj(object, true);
		creature->addObj(object);
	}

	player->printColor("%M says, \"Thank you for retrieving %s for me.\n", creature, object->getObjStr(player, flags, numTrade).c_str());
	player->printColor("For it, I will reward you.\"\n");

	broadcast(player->getSock(), player->getRoom(), "%M trades %P to %N.", player,object,creature);

	/*
	if(trade->flagIsSet(O_LOAD_ALL) && trade->randomObjects.size()) {
		// in this case, we give them a lot of items
		std::list<CatRef>::const_iterator it;
		for(it = trade->randomObjects.begin(); it != trade->randomObjects.end(); it++) {
			if(!loadObject(*it, &addTrade))
				continue;
			endTrade(player, creature, addTrade, doQuestOwner);
		}
		// they aren't actually getting the "trade" item
		delete trade;
	} else {
		// the simple, common scenario
		endTrade(player, creature, trade, doQuestOwner);
	}
	 */

	for(oIt = objects.begin(); oIt != objects.end(); oIt++) {
		player->printColor("%M gives you %P in trade.\n", creature, *oIt);
		doGetObject(*oIt, player);
	}

	if(creature->ttalk[0])
		player->print("%M says, \"%s\"\n", creature, creature->ttalk);

	return(0);
}


//*********************************************************************
//						cmdAuction
//*********************************************************************

int cmdAuction(Player* player, cmd* cmnd) {
	int			i=0, flags=0;
	long		amnt=0, batch=1, each=0;
	Object		*object=0;

	player->clearFlag(P_AFK);

	if(player->getClass() == BUILDER || player->inJail()) {
		player->print("You cannot do that.\n");
		return(PROMPT);
	}

	if(cmnd->num < 3) {
		player->print("Syntax: auction (item)[which|#num] $(amount) [each|total]\n");
		player->print("        auction self $(amount)\n");
		return(0);
	}

	if(player->flagIsSet(P_MISTED) || player->isInvisible()) {
		player->print("You must be visible to everyone in order to auction.\n");
		return(0);
	}
	if(player->getLevel() < 7 && !player->isCt()) {
		player->print("You must be at least level 7 to auction items.\n");
		return(0);
	}
	if(player->flagIsSet(P_CANT_BROADCAST)) {
		player->print("Due to abuse, you no longer have that privilage.\n");
		return(0);
	}

	// use i to point to where the money will be
	i = 2;

	if(strcmp(cmnd->str[1], "self")) {
		object = findObject(player, player->first_obj, cmnd);
		if(!object) {
			player->print("That object is not in your inventory.\n");
			return(0);
		}

		// batch auction
		if(cmnd->str[2][0] == '#') {
			i = 3;
			batch = atoi(&cmnd->str[2][1]);

			if(batch < 1) {
				player->print("You must sell atleast 1 item.\n");
				return(0);
			}
			if(batch > 100) {
				player->print("That's too many!\n");
				return(0);
			}
			if(cmnd->num==5 && !strcmp(cmnd->str[4], "each"))
				each = 1;
			else
				each = -1;
		}
	}

	if(cmnd->str[i][0] != '$') {
		player->print("Syntax: auction (item)[which|#num] $(amount) [each|total]\n");
		player->print("        auction self $(amount)\n");
		return(0);
	}

	amnt = atol(&cmnd->str[i][1]);

	if(amnt <= 0) {
		player->print("A price must be specified to auction.\n");
		return(0);
	}
	if( amnt < 500 &&
		!player->checkStaff("Items must be auctioned for at least 500 gold.\n")
	)
		return(0);


	if(player->isEffected("detect-magic"))
		flags |= MAG;




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

		if(!target->isConnected())
			continue;
		if(target->flagIsSet(P_NO_AUCTIONS))
			continue;

		if(!strcmp(cmnd->str[1], "self")) {
			target->printColor("*** %M is auctioning %sself for %ldgp.\n", player, player->himHer(), amnt);
			continue;
		}
		target->printColor("*** %M is auctioning %s for %ldgp%s.\n",
			player, object->getObjStr(NULL, flags, batch).c_str(), amnt, each ? each==1 ? " each" : " total" : "");
	}

	return(0);
}

//*********************************************************************
//						setLastPawn
//*********************************************************************

void Player::setLastPawn(Object* object) {
	// uniques and quests cannot be reclaimed
	if(object && (object->flagIsSet(O_UNIQUE) || object->getQuestnum())) {
		delete object;
		return;
	}
	if(lastPawn) {
		delete lastPawn;
		lastPawn = 0;
	}
	lastPawn = object;
}

//*********************************************************************
//						restoreLastPawn
//*********************************************************************

bool Player::restoreLastPawn() {
	if(!getRoom()->flagIsSet(R_PAWN_SHOP) || !getRoom()->flagIsSet(R_DUMP_ROOM)) {
		print("You must be in a pawn shop to reclaim items.\n");
		return(false);
	}
	if(!lastPawn) {
		print("You have no items to reclaim.\n");
		return(false);
	}
	printColor("You negotiate with the shopkeeper to reclaim %P.\n", lastPawn);
	if(lastPawn->refund[GOLD] > coins[GOLD]) {
		print("You don't have enough money to reclaim that item!\n");
		return(false);
	}
	if(!Unique::canGet(this, lastPawn) || !Lore::canHave(this, lastPawn)) {
		print("You currently cannot reclaim that limited object.\n");
		return(false);
	}
	coins.sub(lastPawn->refund);
	printColor("You give the shopkeeper %d gold and reclaim %P.\n", lastPawn->refund[GOLD], lastPawn);
	lastPawn->refund.zero();
	doGetObject(lastPawn, this, true, true);
	lastPawn = 0;
	return(true);
}

//*********************************************************************
//						cmdReclaim
//*********************************************************************

int cmdReclaim(Player* player, cmd* cmnd) {
	player->restoreLastPawn();
	return(0);
}


//********************************************************************
//						doHaggling
//********************************************************************
// This function is called whenever money changes hands for a player. It is specifically
// written for clerics of the god Jakar, but is being designed so it can be applied to
// all players in general if desired. It will make a player haggle to get more money if
// selling, and pay less money if buying, up to 30% discount. -TC

void Creature::doHaggling(Creature *vendor, Object* object, int trans) {
	int		chance=0, npcVendor=0, roll=0, discount=0, lck=0;
	float	amt=0.0, percent=0.0;
	long	modAmt = 0, val = 0;


	if(vendor)
		npcVendor = 1;

	if(trans != 0 && trans != 1) {
		broadcast(::isDm, "^g*** Invalid function call: doHaggle. No transaction type specified. Aborting.");
		return;
	}

	/* Remove this to allow all players to haggle */
	if(!(isCt() ||
	        getClass() == BARD ||
	        (getClass() == CLERIC && getDeity() == JAKAR)) )
		return;

	if(chkSave(LCK, 0, -1))
		lck = 25;
	else
		lck = 0;

	if(npcVendor) {
		chance = 50 + lck + 5*((int)vendor->getLevel() - (int)getLevel());

	} else
		chance = MIN(95,(getLevel()*2 + saves[LCK].chance/10));

	if(trans == SELL)
		val = MIN(MAXPAWN,object->value[GOLD]/2);
	else
		val = object->value[GOLD]/2;

	roll = mrand(1,100);

	if(isCt()) {
		print("haggle chance: %d\n", chance);
		print("roll: %d\n", roll);
	}
	if(roll <= chance) {

		discount = MAX(1,mrand(getLevel()/2, 1+getLevel()));

		percent = (float)(discount);
		percent /= 100;

		amt = (float)val;
		amt *= percent;

		modAmt = (long)amt;

		if(modAmt >= 10000)
			modAmt += (mrand(1,10000)-1);
		else if(modAmt >= 100)
			modAmt += (mrand(1,1000)-1);
		else if(modAmt >= 100)
			modAmt += (mrand(1,100)-1);
		else if(modAmt >= 10)
			modAmt += (mrand(1,10)-1);


		if(isCt()) {
			print("discount: %d%\n", discount);
			print("percent: %f, amt: %f\n", percent, amt);
			print("modAmt: %ld\n", modAmt);
		}

		if(modAmt) {
			if(npcVendor)
				broadcast(getSock(), getRoom(), "%M haggles over prices with %N.", this, vendor);
			switch(trans) {
			case BUY:
				printColor("^yYour haggling skills saved you %ld gold coins.\n", modAmt);
				coins.add(modAmt, GOLD);
				object->refund.set(object->value);
				object->refund.sub(modAmt, GOLD);
				break;
			case SELL:
				printColor("^yYour haggling skills gained you an extra %ld gold coins.\n", modAmt);
				object->refund.set(object->value);
				object->refund.add(modAmt, GOLD);
				coins.add(modAmt, GOLD);
				break;
			default:
				break;
			}
		}

	}
}