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/
/*
 * 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-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 "factions.h"
#include "property.h"
#include "guilds.h"
#include "bank.h"
#include "unique.h"
#include "dm.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 UniqueRoom *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 UniqueRoom *room, const Object* object, bool sell) {
	Money value = Faction::adjustPrice(player, room->getFaction(), object->value, sell);
	value.set(tMIN<unsigned long>(value[GOLD] / 2, MAXPAWN), GOLD);
	return(value);
}

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

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

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<^xfound^e> <^cexit name^e>^x\n"
	"             ^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> [^call^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(Property *p, Player* player, const Guild* guild, UniqueRoom* shop, UniqueRoom* storage) {
	const char* shopSuffix = "'s Shop of Miscellaneous Wares";
	const char* shopStorageSuffix = "'s Shop Storage Room";
	if(guild)
		shop->setName(guild->getName() + shopSuffix);
	else
		shop->setName(player->getName() + shopSuffix);

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

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

	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->getName());
		shop->appendLongDescription(".");
	} else
		shop->appendLongDescription("proprietor.");

	if(guild)
		storage->setName(guild->getName() + shopStorageSuffix);
	else
		storage->setName(player->getName() + shopStorageSuffix);

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

	if(p)
		p->setName(shop->getName());
}

//*********************************************************************
//						shopAssignGuildMessage
//*********************************************************************

void shopAssignGuildMessage(Property* p, const Player* player, const Guild* guild) {
	player->print("This property is now associated with the guild %s.\n", guild->getName().c_str());
	p->appendLog("", "This property is now associated with the guild %s.", guild->getName().c_str());
	broadcastGuild(guild->getNum(), 1, "### %s has assigned the shop located at %s as partially run by the guild.",
		player->getCName(), p->getLocation().c_str());
}

//*********************************************************************
//						shopAssignGuild
//*********************************************************************

bool shopAssignGuild(Property *p, Player* player, const Guild* guild, UniqueRoom* shop, UniqueRoom* storage, bool showMessage) {
	if(!player->getGuild()) {
		*player << "You are not part of a guild.\n";
		return(false);
	}
	if(p->getGuild() == player->getGuild()) {
		*player << "This property is already assigned to your guild.\n";
		return(false);
	}
	if(!guild) {
		*player << "There was an error loading your guild.\n";
		return(false);
	}

	p->setGuild(guild->getNum());
	setupShop(p, player, guild, shop, storage);

	if(showMessage)
		shopAssignGuildMessage(p, player, guild);
	return(true);
}

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

void shopRemoveGuild(Property *p, Player* player, UniqueRoom* shop, UniqueRoom* 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(p, player, 0, shop, storage);
}



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

CatRef shopStorageRoom(const UniqueRoom *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 UniqueRoom* storage) {
	int numObjects=0, numLines=0;
	Object *obj;
	ObjectSet::iterator it;
	for(it = storage->objects.begin() ; it != storage->objects.end() ; ) {
		obj = (*it++);
		numObjects++;
		numLines++;


		while(it != storage->objects.end()) {
			if(playerShopSame(0, obj, (*it))) {
				numObjects++;
				it++;
			} else
				break;
		}

		if(numObjects >= gConfig->getShopNumObjects()) {
			player->print("There are too many items in this shop (%d/%d).\n", numObjects, gConfig->getShopNumObjects());
			player->print("Please remove some before continuing.\n");
			return(true);
		} else if ( numLines >= gConfig->getShopNumLines()) {
			player->print("The number of items in your shop take up too many lines: (%d/%d).\n", numLines, gConfig->getShopNumLines());
			player->print("Please remove some before continuing.\n");
			return(true);

		}
	}

	return(false);
}

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

bool canBuildShop(const Player* player, const UniqueRoom* room) {
	// works for most people
	if(room->flagIsSet(R_BUILD_SHOP))
		return(true);

	// guild foyers can accomodate shops, but only for members of the guild
	if(	player->getGuild() &&
		player->getGuildRank() == GUILD_MASTER &&
		room->flagIsSet(R_GUILD_OPEN_ACCESS) &&
		room->info.isArea("guild"))
	{
		Property* p = gConfig->getProperty(room->info);
		if(p->getType() == PROP_GUILDHALL && player->getGuild() == p->getGuild()) {
			// already hosts a shop?
			for(Exit* exit : room->exits) {
				if(exit->target.room.isArea("shop"))
					return(false);
			}

			// If we pass these checks, too, then we can build a shop here
			return(true);
		}
	}

	return(false);
}

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

int cmdShop(Player* player, cmd* cmnd) {
	UniqueRoom* room = player->getUniqueRoomParent(), *storage=0;
	Object	*deed=0;
	int		action=0;
	int		flags = 0, len = strlen(cmnd->str[1]);
	const Guild* guild=0;
	if(player->getGuild())
		guild = gConfig->getGuild(player->getGuild());

	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(!strcmp(cmnd->str[2], "all")) {
			*player << "Searching for suitable shop locations in this city.\n";
			findRoomsWithFlag(player, player->getUniqueRoomParent()->info, R_BUILD_SHOP);
		} else {
			if(!canBuildShop(player, room))
				*player << "You are unable to build a shop here.\n";
			else
				*player << "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(!canBuildShop(player, room)) {
			*player << "This section of town is not zoned for shops.\n";
			return(0);
		}

		// See if the deed can be built in this room
		CatRef cr = room->info;
		// If we're inside a guild, check the out exit instead
		if(room->info.isArea("guild")) {
			for(Exit* exit : room->exits) {
				if(!exit->target.room.isArea("guild")) {
					cr = exit->target.room;
					break;
				}
			}
		}

		for(Object* obj : player->objects ) {
			if(obj->getName() == "shop deed")
				if(obj->deed.belongs(cr)) {
					deed = obj;
					break; // We got one!
				}
		}
		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->getName()) &&
				!p->isPartialOwner(player->getName())
			)
		) {
			player->print("This is not your shop!\n");
			return(0);
		}
	}

	PartialOwner partial;
	PartialOwner* po=0;
	if(p && !p->isOwner(player->getName())) {
		po = p->getPartialOwner(player->getName());
		// 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 << "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) {
		UniqueRoom *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->getCName(), xname.c_str(), cr.str().c_str());

		shop = new UniqueRoom;
		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 UniqueRoom;
		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
		// sned in a guild if they're inside a guild hall

		room->saveToFile(0);

		p = new Property;
		p->found(player, PROP_SHOP);

		p->addRange(shop->info.area, shop->info.id, shop->info.id+1);

		if(!room->info.isArea("guild")) {
			setupShop(p, player, 0, shop, storage);
		} else {
			shopAssignGuild(p, player, guild, shop, storage, false);
		}

		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->getCName(), room->info.str().c_str(), xname.c_str(), shop->info.str().c_str());
		broadcast(player->getSock(), player->getParent(), "%M just opened a shop!", player );
		if(!player->flagIsSet(P_DM_INVIS))
			broadcast("### %s just opened a shop! It's located at: %s.", player->getCName(), room->getCName());

		if(room->info.isArea("guild"))
			shopAssignGuildMessage(p, player, guild);

		delete shop;
		delete storage;
		player->delObj(deed, true);
		delete deed;
		player->getUniqueRoomParent()->clearFlag(R_BUILD_SHOP);
		player->getUniqueRoomParent()->setFlag(R_WAS_BUILD_SHOP);
		player->getUniqueRoomParent()->saveToFile(0);
		return(0);
	} else if(action == SHOP_GUILD) {
		if(!strncmp(cmnd->str[2], "assign", strlen(cmnd->str[2]))) {
			if(!shopAssignGuild(p, player, guild, player->getUniqueRoomParent(), storage, true))
				return(0);
		} else if(!strncmp(cmnd->str[2], "remove", strlen(cmnd->str[2]))) {
			if(!p->getGuild()) {
				*player << "This property is not assigned to a guild.\n";
				return(0);
			}
			if(shopStaysWithGuild(player->getUniqueRoomParent())) {
				*player << "This property was constructed inside the guild hall.\nIt cannot be removed from the guild.\n";
				return(0);
			}
			broadcastGuild(p->getGuild(), 1, "### %s's shop located at %s is no longer partially run by the guild.",
				player->getCName(), p->getLocation().c_str());
			shopRemoveGuild(p, player, player->getUniqueRoomParent(), storage);
			*player << "This property is no longer associated with a guild.\n";
		} else {
			*player << "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 = player->findObject(player, cmnd, 2);
		int value=0;

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

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

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

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

		limited = Limited::isLimited(obj);
		if(limited && (!p || !p->isOwner(player->getCName()))) {
			*player << "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->getName(), "%s stocked %s for $%d.", player->getCName(), 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->getParent(), "%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 = storage->findObject(player, cmnd, 2);
		int value=0;

		if(!obj) {
			*player << "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->getName(), "%s set the price for %s to $%d.", player->getCName(), 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->getParent(), "%M just updated the prices in this store.", player);
	} else if(action == SHOP_REMOVE) {
		Object *obj = storage->findObject(player, cmnd, 2);

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

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

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

		limited = Limited::isLimited(obj);
		if(limited && (!p || !p->isOwner(player->getName()))) {
			*player << "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->getName(), "%s removed %s.", player->getCName(), 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->getParent(), "%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->getName()) == bstring::npos) {
				*player << "Your shop name must contain your name.\n";
				return(0);
			}
		} else {
			if(!guild) {
				*player << "Error loading guild.\n";
				return(0);
			}
			if(name.find(guild->getName()) == bstring::npos) {
				*player << "Your shop name must contain your guild's name.\n";
				return(0);
			}
		}
		player->getUniqueRoomParent()->setName(name.c_str());
		p->setName(name);
		p->appendLog(player->getName(), "%s renamed the shop to %s.", player->getCName(), player->getUniqueRoomParent()->getCName());
		logn("log.shops", "%s renamed shop %s to %s.\n",
			player->getCName(), player->getUniqueRoomParent()->info.str().c_str(), player->getUniqueRoomParent()->getCName());
		player->print("Shop renamed to '%s'.\n", player->getUniqueRoomParent()->getCName());
		player->getUniqueRoomParent()->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);
}

//*********************************************************************
//						isValidShop
//*********************************************************************
// everything must be set up perfectly for the shop to work

bool isValidShop(const UniqueRoom* shop, const UniqueRoom* storage) {
	// both rooms must exist
	if(!shop || !storage)
		return(false);
	// flags must be set properly
	if(!shop->flagIsSet(R_SHOP))
		return(false);
	if(!storage->flagIsSet(R_SHOP_STORAGE))
		return(false);
	// cannot be itself
	if(shop->info == storage->info)
		return(false);
	// shop must point to storage room
	if(shopStorageRoom(shop) != storage->info)
		return(false);

	return(true);
}

//*********************************************************************
//						cmdList
//*********************************************************************

const char* cannotUseMarker(Player* player, Object* object) {
	if(object->getType() == WEAPON || object->getType() == ARMOR) {
		if(!player->canUse(object, true))
			return(" ^r(x)^x");
	}
	return("");
}

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

int cmdList(Player* player, cmd* cmnd) {
	UniqueRoom* room = player->getUniqueRoomParent(), *storage=0;
	Object* object=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 << "This is not a shop.\n";
		return(0);
	}

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

	// Everything must be set up perfectly
	if(!isValidShop(room, storage)) {
		*player << "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->getUniqueRoomParent()->getFaction())) {
			*player << "The shopkeeper refuses to do business with you.\n";
			return(0);
		}

		storage->addPermObj();
		ObjectSet::iterator it;
		if(!storage->objects.empty()) {
			*player << "You may buy:";
			if(filter != "")
				*player << ColorOn << " ^Y(filtering on \"" << filter << "\")" << ColorOff;
			*player << "\n";
			for(it = storage->objects.begin() ; it != storage->objects.end() ; ) {
				object = (*it++);
				if(doFilter(object, filter))
					continue;

				if(object->getName() == "bail")
					object->value = bailCost(player);

				cost = buyAmount(player, player->getUniqueRoomParent(), 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%s\n",
					objShopName(object, 1, CAP | (player->isEffected("detect-magic") ? MAG : 0), 52).c_str(),
					cost.str().c_str(), cannotUseMarker(player, object));

			}
			*player << "\n";
		}  else {
			*player << "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->getName())) {
			player->print("You are selling:");
			owner = true;
		} else {
			bstring owner = p->getOwner();
			if(p->getGuild()) {
				const Guild* guild = gConfig->getGuild(p->getGuild());
				owner = guild->getName();
			}
			player->print("%s is selling:", owner.c_str());
			if(p->isPartialOwner(player->getName()))
				owner = true;
		}

		if(filter != "")
			player->printColor(" ^Y(filtering on \"%s\")", filter.c_str());
		*player << "\n";
		ObjectSet::iterator it;
		for( it = storage->objects.begin() ; it != storage->objects.end() ; ) {
			object = (*it++);
			n++;
			m=1;

			while(it != storage->objects.end()) {
				if(playerShopSame(player, object, (*it))) {
					m++;
					object = (*it++);
				} else
					break;
			}

			num++;

			if(doFilter(object, filter))
				continue;

			if(n == 1)
				player->printColor("^WNum         Item                        Price      Condition\n");
			player->printColor("%2d> %s $%-9ld %-12s%s", num,
				objShopName(object, m, flags, 35).c_str(),
				object->getShopValue(), getCondition(object).c_str(),
				cannotUseMarker(player, object));
			if(owner)
				player->print(" Profit: %ld", shopProfit(object));
			*player << "\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->getParent()->findMonster(player, cmnd, 2);
	if(!creature) {
		*player << "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 << "That would be too much for you to carry.\n";
		delete object;
		return(0);
	}

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


	if(!Unique::canGet(player, object)) {
		*player << "You are unable to purchase that limited item at this time.\n";
		delete object;
		return(0);
	}
	if(!Lore::canHave(player, object, false)) {
		*player << "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 << "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());
		broadcast(player->getSock(), player->getParent(), "%M pays %N %s for %P.\n", player, creature, cost.str().c_str(), object);
		player->coins.sub(cost);

		player->doHaggling(creature, object, BUY);
        gServer->logGold(GOLD_OUT, player, object->refund, object, "MobPurchase");

        if(object->hooks.executeWithReturn("afterPurchase", player, creature->getId())) {
        	// A return value of true means the object still exists
        	player->printColor("%M says, \"%sHere is your %s.\"\n", creature,
        			Faction::getAttitude(player->getFactionStanding(creature->getPrimeFaction())) < Faction::INDIFFERENT ? "Hrmph. " : "Thank you very much. ",
        			object->getCName());
            Limited::addOwner(player, object);
    		object->setDroppedBy(creature, "MobPurchase");

    		player->addObj(object);

        } else {
        	// A return value of false means the object doesn't exist, and as such the spell was cast, so delete it
        	delete 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->getParent()->findMonster(player, cmnd);
	if(!creature) {
		*player << "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 << "\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 << "\n";
	return(0);
}

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

int cmdBuy(Player* player, cmd* cmnd) {
	UniqueRoom* room = player->getUniqueRoomParent(), *storage=0;
	Object	*object=0, *object2=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 << "You may not buy things from shops.\n";
		return(0);
	}

	if(!room->flagIsSet(R_SHOP)) {
		*player << "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 << "Nothing to buy.\n";
		return(0);
	}

	if(!isValidShop(room, storage)) {
		*player << "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->getName())) {
			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);
		ObjectSet::iterator it, next;
		for( it = storage->objects.begin() ; it != storage->objects.end() && num != n; ) {
			while(it != storage->objects.end()) {
				if((playerShopSame(player, (*it), *(next = boost::next(it))))) {
					it = next;
				} else
					break;
			}
			n++;
			it++;
		}
		if(it == storage->objects.end()) {
			*player << "That isn't being sold here.\n";
			return(0);
		}
		object = (*it);
		
		// 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->getName().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 << "You couldn't possibly carry anymore.\n";
			return(0);
		}

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


		if(!Unique::canGet(player, object, true)) {
			*player << "You are unable to purchase that limited item at this time.\n";
			return(0);
		}
		if(!Lore::canHave(player, object, false)) {
			*player << "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 << "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->getParent(), "%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);
		gServer->logGold(GOLD_OUT, player, Money(object->getShopValue() * TAX, GOLD), object, "TAX");
		deposit = shopProfit(object);

		if(!guild) {
			owner->bank.add(deposit, GOLD);
			Bank::log(owner->getCName(), "PROFIT from sale of %s to %s: %ld [Balance: %s]\n",
				object->getObjStr(NULL, flags, 1).c_str(), player->getCName(), deposit, owner->bank.str().c_str());
			if(online && !owner->flagIsSet(P_DONT_SHOW_SHOP_PROFITS))
				owner->printColor("*** You made $%d profit from the sale of %s^x to %s.\n",
					deposit, object->getObjStr(NULL, flags, 1).c_str(), player->getCName());
			owner->save(online);
		} else {
			guild->bank.add(deposit, GOLD);
			Bank::guildLog(guild->getNum(), "PROFIT from sale of %s to %s: %ld [Balance: %s]\n",
				object->getObjStr(NULL, flags, 1).c_str(), player->getCName(), 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 = storage->findObject(player, cmnd, 1);

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

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

		if(object->getName() == "storage room") {
			if(!room->flagIsSet(R_STORAGE_ROOM_SALE)) {
				*player << "You can't buy storage rooms here.\n";
				return(0);
			}

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

		if(object->getName() == "bail")
			object->value = bailCost(player);

		Money cost = buyAmount(player, player->getUniqueRoomParent(), 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(object->getName() ==  "bail") {
			// Depends on level for bail
			// 2k per lvl to get out
			if(player->coins[GOLD] < cost[GOLD]) {
				*player << "You don't have enough gold to post bail.\n";
				return(0);
			}
		} else {

			if(player->coins[GOLD] < cost[GOLD]) {
				*player << "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 << "No more will fit in your inventory right now.\n";
				return(0);
			}

		}

		// Not buying a storage room or bail
		if(object->getName() != "storage room" && object->getName() != "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->getCName()) < 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->setParent(NULL);
				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 << "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 << "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 << "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);
			}

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

			object2->refund.zero();
			object2->refund.set(cost);
			player->coins.sub(cost);

			// No haggling on deeds!
			if(!isDeed)
			    player->doHaggling(0, object2, BUY);

			// We just did a full priced purchase, we can now haggle again (unless they do another refund)
			if(player->getRoomParent() && player->getRoomParent()->getAsUniqueRoom())
			    player->removeRefundedInStore(player->getRoomParent()->getAsUniqueRoom()->info);


			object2->setDroppedBy(room, "StoreBought");
	        gServer->logGold(GOLD_OUT, player, object2->refund, object2, "StoreBought");

			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 << "\n";

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

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

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

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

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

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

					// Purchase warnings for guildhalls
					if(!player->getGuild())
						player->printColor("^YCaution:^x you must belong to a guild to use a guildhall deed.\n");
					else if(player->getGuildRank() != GUILD_MASTER)
						player->printColor("^YCaution:^x you must be a guildmaster to use a guildhall deed.\n");
					else {
						std::list<Property*>::iterator it;
						for(it = gConfig->properties.begin() ; it != gConfig->properties.end() ; it++) {
							if((*it)->getType() != PROP_GUILDHALL)
								continue;
							if(player->getGuild() != (*it)->getGuild())
								continue;
							if(player->inUniqueRoom() && !player->getUniqueRoomParent()->info.isArea((*it)->getArea()))
								continue;
							player->printColor("^YCaution:^x your guild already owns a guildhall in %s.\n", gConfig->catRefName((*it)->getArea()).c_str());
						}
					}
				}

				if(flag)
					findRoomsWithFlag(player, object2->deed, flag);
			}

			if(object->hooks.executeWithReturn("afterPurchase", player)) {

				Limited::addOwner(player, object2);
				player->addObj(object2);
	        } else {
	        	delete object2;
	        }

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

			player->coins.sub(cost);
			gServer->logGold(GOLD_OUT, player, cost, NULL, "PurchaseStorage");

			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->getParent(), "%M just bought a storage room.", player);
			logn("log.storage", "%s bought storage room %s.\n",
				player->getCName(), cr.str().c_str());

			createStorage(cr, player);

		} 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);
			gServer->logGold(GOLD_OUT, player, cost, exit, "Bail");
			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);
}


//*********************************************************************
//						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->getRoomParent()->flagIsSet(R_PAWN_SHOP)) {
		*player << "This is not a pawn shop.\n";
		return(0);
	}

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

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

	if(!object) {
		*player << "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->objects.empty()) {
		player->print("You don't want to sell that!\nThere's something inside it!\n");
		return(0);
	}

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

	value = sellAmount(player, player->getConstUniqueRoomParent(), 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->getParent(), "%M sells %1P.", player, object);

	object->refund.zero();
	object->refund.set(value);
	player->doHaggling(0, object, SELL);
	gServer->logGold(GOLD_IN, player, object->refund, object, "Pawn");
	player->bug("%s sold %s in room %s.\n", player->getCName(), object->getCName(),
		player->getRoomParent()->fullName().c_str());
	logn("log.commerce", "%s sold %s in room %s.\n", player->getCName(), object->getCName(),
		player->getRoomParent()->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->getRoomParent()->flagIsSet(R_PAWN_SHOP)) {
		*player << "You must be in a pawn shop.\n";
		return(0);
	}

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

	player->unhide();

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

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

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

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

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

	broadcast(player->getSock(), player->getParent(), "%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->getRoomParent()->flagIsSet(R_SHOP)) {
		*player << "This is not a shop.\n";
		return(0);
	}

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

	object = player->findObject(player, cmnd, 1);
	if(!object) {
		*player << "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 << "You cannot return intellectual property.\n";
		return(0);
	}


	
	player->coins.add(object->refund);
	player->printColor("The shopkeep takes the %s from you and returns %s to you.\n", object->getCName(), object->refund.str().c_str());
	broadcast(player->getSock(), player->getParent(), "%M refunds %P.", player, object);
	gServer->logGold(GOLD_IN, player, object->refund, object, "Refund");
	player->delObj(object, true);
	// No further haggling allowed in this store until they buy a full priced item and have left the room
	player->setFlag(P_JUST_REFUNDED);
	if(player->getRoomParent() && player->getRoomParent()->getAsUniqueRoom()) {
	    player->addRefundedInStore(player->getRoomParent()->getAsUniqueRoom()->info);
	}

	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->getParent(), "%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 || doQuestOwner)
			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->getParent()->findMonster(player, cmnd, 2);
	if(!creature) {
		*player << "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 = player->findObject(player, cmnd, 1);

	if(!object) {
		*player << "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++) {
		// 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) {
		int numObjects=0;

		ObjectSet toDelSet;
		ObjectSet::iterator it;
		Object *obj = 0;

		// find the first occurance of the object in their inventory
		for( it = player->objects.begin() ; it != player->objects.end() ; ) {
			obj = (*it);
			if(obj->info == object->info)
				break;
			else
				it++;
		}

		if(it == player->objects.end()) {
			player->print("%M gets confused and cannot trade for that right now.\n", creature);
			failTrade(player, object, creature, trade);
			return(0);
		}
		while( it != player->objects.end() && numObjects != numTrade && (*it)->info == object->info) {
			obj = (*it);
			numObjects++;
			if(!obj->isQuestOwner(player)) {
				player->print("%M says, \"That doesn't belong to you!\"\n", creature);
				failTrade(player, object, creature, trade);
				return(0);
			}
			toDelSet.insert(obj);
			it++;
		}

		if(numObjects != numTrade) {
			*player << "You don't have that many items.\n";
			failTrade(player, object, creature, trade, false);
			return(0);
		}

		// if everything is successful, start removing objects
		for(Object* toDelete : toDelSet) {
			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->getParent(), "%M trades %P to %N.", player,object,creature);


	for(oIt = objects.begin(); oIt != objects.end(); oIt++) {
		player->printColor("%M gives you %P in trade.\n", creature, *oIt);
		(*oIt)->setDroppedBy(creature, "MonsterTrade");
		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 << "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->isEffected("mist") || player->isInvisible()) {
		*player << "You must be visible to everyone in order to auction.\n";
		return(0);
	}
	if(player->getLevel() < 7 && !player->isCt()) {
		*player << "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 privilege.\n");
		return(0);
	}

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

	if(strcmp(cmnd->str[1], "self")) {
		object = player->findObject(player, cmnd, 1);
		if(!object) {
			*player << "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 << "You must sell at least 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 << "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;



	Player* target=0;
	for(std::pair<bstring, Player*> 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(!getRoomParent()->flagIsSet(R_PAWN_SHOP) || !getRoomParent()->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);
	gServer->logGold(GOLD_OUT, this, lastPawn->refund, lastPawn, "Reclaim");

	lastPawn->setFlag(O_RECLAIMED);
	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 this item was sold and reclaimed, no further haggling
	if(trans == SELL && object->flagIsSet(O_RECLAIMED)) {
	    chance = 0;
	}


	// If we've refunded an item in this shop, no haggling until having bought a full priced item again

	if(trans == BUY) {
	    if(getAsPlayer() && flagIsSet(P_JUST_REFUNDED))
	        chance = 0;
	    if(chance > 0 && getRoomParent() && getRoomParent()->getAsUniqueRoom() ) {
            Player* player = getAsPlayer();
            if( player && !player->storesRefunded.empty()) {
                if(player->hasRefundedInStore(getRoomParent()->getAsUniqueRoom()->info)) {
                    chance = 0;
                }
            }
	    }
	}
	if(trans == SELL)
		val = tMIN<unsigned long>(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(), getRoomParent(), "%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.sub(modAmt, GOLD);
				break;
			case SELL:
				printColor("^yYour haggling skills gained you an extra %ld gold coins.\n", modAmt);
				coins.add(modAmt, GOLD);
				object->refund.add(modAmt, GOLD);
				break;
			default:
				break;
			}
		}

	}
}

bool Player::hasRefundedInStore(CatRef& store) {
    if( storesRefunded.empty())
        return(false);
    else {
        std::list<CatRef>::const_iterator it;
        for(it = storesRefunded.begin() ; it != storesRefunded.end() ; it++) {
            if( *it == store) {
                return(true);
            }
        }
    }
    return(false);
}
void Player::addRefundedInStore(CatRef& store) {
    if(hasRefundedInStore(store))
        return;
    else
        storesRefunded.push_back(store);
}
void Player::removeRefundedInStore(CatRef& store) {
    std::list<CatRef>::iterator it;
    for(it = storesRefunded.begin() ; it != storesRefunded.end() ; it++) {
        if( *it == store) {
            storesRefunded.erase(it);
            return;
        }
    }
}