Mud20/accounts/
Mud20/accounts/c/
Mud20/accounts/f/
Mud20/accounts/k/
Mud20/accounts/s/
Mud20/accounts/t/
Mud20/area_current/
Mud20/area_current/newareas/
Mud20/bin/
Mud20/clans/
Mud20/gods/
Mud20/old-sources/
Mud20/player/
Mud20/player/a/del/
Mud20/player/b/
Mud20/player/b/bak/
Mud20/player/b/del/
Mud20/player/f/
Mud20/player/f/bak/
Mud20/player/f/del/
Mud20/player/k/
Mud20/player/k/bak/
Mud20/player/k/del/
Mud20/player/k/dmp/
Mud20/player/m/
Mud20/player/m/bak/
Mud20/player/o/
Mud20/player/o/bak/
Mud20/player/p/
Mud20/player/s/
Mud20/player/s/bak/
Mud20/player/s/del/
Mud20/player/t/
Mud20/player/t/del/
Mud20/player/v/
Mud20/public_html/
Mud20/races/
Mud20/skilltables/
__MACOSX/Mud20/accounts/
__MACOSX/Mud20/accounts/c/
__MACOSX/Mud20/accounts/f/
__MACOSX/Mud20/accounts/k/
__MACOSX/Mud20/accounts/s/
__MACOSX/Mud20/area_current/
__MACOSX/Mud20/area_current/core_areas/
__MACOSX/Mud20/area_current/helps/
__MACOSX/Mud20/area_current/newareas/
__MACOSX/Mud20/backups/
__MACOSX/Mud20/bin/
__MACOSX/Mud20/clans/
__MACOSX/Mud20/gods/
__MACOSX/Mud20/log/
__MACOSX/Mud20/old-sources/
__MACOSX/Mud20/player/
__MACOSX/Mud20/player/a/del/
__MACOSX/Mud20/player/b/
__MACOSX/Mud20/player/b/bak/
__MACOSX/Mud20/player/f/
__MACOSX/Mud20/player/f/bak/
__MACOSX/Mud20/player/f/del/
__MACOSX/Mud20/player/k/
__MACOSX/Mud20/player/k/bak/
__MACOSX/Mud20/player/k/del/
__MACOSX/Mud20/player/k/dmp/
__MACOSX/Mud20/player/m/
__MACOSX/Mud20/player/m/bak/
__MACOSX/Mud20/player/o/
__MACOSX/Mud20/player/o/bak/
__MACOSX/Mud20/player/p/
__MACOSX/Mud20/player/s/
__MACOSX/Mud20/player/s/bak/
__MACOSX/Mud20/player/t/del/
__MACOSX/Mud20/player/v/
__MACOSX/Mud20/public_html/
__MACOSX/Mud20/races/
__MACOSX/Mud20/skilltables/
/***************************************************************************
 * Mud20 1.0 by Todd H. Johnson (Kregor) a derivative of the Open Gaming   *
 * License by Wizards of the Coast. All comments referring to D20, OGL,    *
 * and SRD refer to the System Reference Document for the Open Gaming      *
 * system. Any inclusion of these derivatives must include credit to the   *
 * Mud20 system, the full and complete Open Gaming LIcense, and credit to  *
 * the respective authors. See ../doc/srd.txt for more information.        *
 *                                                                         *
 * Emud  2.2 by Igor van den Hoven, Michiel Lange, and Martin Bethlehem.   *
 *                                                                         *
 * MrMud 1.4 by David Bills, Dug Michael and Martin Gallwey                *
 *                                                                         *
 * Merc  2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael      *
 * Chastain, Michael Quan, and Mitchell Tse.                               *
 *                                                                         *
 * Original Diku Mud copyright (C) 1990 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik St{rfeld, Tom Madsen, and Katje Nyboe.     *
 ***************************************************************************/

/***************************************************************************
 * shops.c: Functions for merchants and banking													   *
 ***************************************************************************/

#include "mud.h"

/*
 * Is there a shopkeeper in the house?
 */
CHAR_DATA *find_keeper( CHAR_DATA *ch )
{
	CHAR_DATA *keeper;

	push_call("find_keeper(%p)",ch);

	for (keeper = ch->in_room->first_person ; keeper ; keeper = keeper->next_in_room)
	{
		if (IS_NPC(keeper) && keeper->pIndexData->pShop)
		{
			break;
		}
	}
	pop_call();
	return keeper;
}

/*
 * Is char a shopkeeper?
 */
bool is_keeper( CHAR_DATA *ch )
{
	push_call("is_keeper(%p)",ch);

	if (IS_NPC(ch) && ch->pIndexData->pShop)
	{
		pop_call();
		return TRUE;
	}

	pop_call();
	return FALSE;
}

/*
 * Is there a banker in the house?
 */
CHAR_DATA *find_banker( CHAR_DATA *ch )
{
	CHAR_DATA *keeper;

	push_call("find_keeper(%p)",ch);

	for (keeper = ch->in_room->first_person ; keeper ; keeper = keeper->next_in_room)
	{
		if (IS_NPC(keeper) && keeper->pIndexData->pShop && IS_SET(keeper->pIndexData->pShop->shop_flags, SHOP_BANK))
		{
			break;
		}
		if (IS_ACT(keeper, ACT_BANK))
		{
			break;
		}
	}
	pop_call();
	return keeper;
}

/*
 * Get the sell/purchase cost of item based
 * on shop buy and sell values
 */
int get_cost( CHAR_DATA *keeper, OBJ_DATA *obj, bool fBuy )
{
	SHOP_DATA *pShop;
	int cost = -1;

	push_call("get_cost(%p,%p,%p)",keeper,obj,fBuy);

	if ((pShop = keeper->pIndexData->pShop) == NULL)
	{
		pop_call();
		return cost;
	}

	if (fBuy)
	{
		cost = obj->cost * pShop->profit_buy  / 100;
	}
	else
	{
		OBJ_DATA *obj2;
		int itype;

		for (itype = 0 ; itype < MAX_TRADE ; itype++)
		{
			if (obj->item_type == pShop->buy_type[itype])
			{
				cost = obj->cost * pShop->profit_sell / 100;
				break;
			}
		}

		for (obj2 = keeper->first_carrying ; obj2 ; obj2 = obj2->next_content)
		{
			if (obj->pIndexData == obj2->pIndexData)
			{
				cost = cost * 8 / 10;
			}
		}
	}

	pop_call();
	return cost;
}

/*
 * The Haggle skill at work - Kregor
 */
int haggle( CHAR_DATA *ch, CHAR_DATA *seller, int cost, bool buy )
{
	int mod;
	push_call("haggle(%p,%p,%p)",ch,seller,cost);

	if (!buy)
		mod = haggle_check(seller, ch) * 2;
	else
		mod = haggle_check(ch, seller) * 2;
		
	mod += reputation_bonus(ch);
		
	mod = URANGE(-20, mod, 20);
	
	cost = cost * (100 + mod) / 100;
	
	pop_call();
	return UMAX(1, cost);
}

/*
 * Format costs into gold/silver/copper.
 * Also added option for short or long amounts - Kregor
 * definitions of currency
 * 10 copper		== 1 silver;
 * 10 silver 	== 1 gold;
 * 100 copper 	== 1 gold;
 */
char *format_coins( int amount, bool fLong )
{
	static char buf[MAX_STRING_LENGTH];
	int gold = amount / 100;
	int silver = amount % 100 / 10;
 	int copper = amount % 100 % 10;

	push_call("format_coins(%p,%p)", amount,fLong);

	buf[0] = '\0';
	
	if (amount == 0)
		cat_sprintf(buf, "none");
		
	if (gold > 0)
	{
		if (fLong)
			cat_sprintf(buf, "{138}%s gold", format_number(gold));
		else
			cat_sprintf(buf, "{138}%s gp", format_number(gold));
		if (silver > 0 || copper > 0)
			cat_sprintf(buf, ", ");
	}
	if (silver > 0)
	{
		if (fLong)
			cat_sprintf(buf, "{078}%d silver", silver);
		else
			cat_sprintf(buf, "{078}%d sp", silver);
		if (copper > 0)
			cat_sprintf(buf, ", ");
	}
	if (copper > 0)
	{
		if (fLong)
			cat_sprintf(buf, "{038}%d copper", copper);
		else
			cat_sprintf(buf, "{038}%d cp", copper);
	}
	cat_sprintf(buf, "{300}");
	
	pop_call();
	return buf;
}

/*
 * give money to char. If purse full,
 * deposit remainder into account.
 */
int gold_transaction(CHAR_DATA *ch, int amount)
{
	int max_account, max_pocket;

	push_call("gold_transaction(%p)",ch,amount);

	if (IS_NPC(ch))
	{
		if (ch->gold + amount < 0)
		{
			pop_call();
			return FALSE;
		}
		ch->gold += amount;
		pop_call();
		return TRUE;
	}

	if (amount < 0)
	{
		amount = 0 - amount;
		if (ch->gold >= amount)
		{
			ch->gold -= amount;
			ch->carry_weight = get_carry_w(ch);

			pop_call();
			return TRUE;
		}
		else if (find_banker(ch) != NULL && ch->gold + ch->pcdata->bank >= amount)
		{
			amount -= ch->gold;
			ch->gold = 0;
			ch->pcdata->bank -= amount;
			ch->carry_weight = get_carry_w(ch);

			pop_call();
			return TRUE;
		}
		else
		{
			pop_call();
			return FALSE;
		}
	}
	else
	{
		max_pocket = ch->level * 1000000 - ch->gold;

		max_account = account_max(ch);

		if (amount <= max_pocket || find_banker(ch) == NULL)
		{
			ch->gold += amount;
		}
		else
		{
			amount -= max_pocket;
			ch->gold = ch->level * 1000000;
			ch->pcdata->bank += amount;
			if (ch->pcdata->bank > max_account)
			{
				ch->gold += ch->pcdata->bank - max_account;
				ch->pcdata->bank = max_account;
			}
		}
		ch->carry_weight = get_carry_w(ch);
		pop_call();
		return TRUE;
	}
}


/*
 * buy an item/pet/corpse
 */
void do_buy( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	char buf[MAX_INPUT_LENGTH];
	CHAR_DATA *keeper = NULL;
	CHAR_DATA *pet;
	ROOM_INDEX_DATA *pRoomIndexNext;
	ROOM_INDEX_DATA *in_room;
	OBJ_DATA *obj, *found_obj;
	int cost, amount, total_cost, cnt, item_no, count;

	push_call("do_buy(%p,%p)",ch,argument);

	if (IS_NPC(ch))
	{
		pop_call();
		return;
	}

	argument = one_argument(argument, arg);

	if (arg[0] == '\0')
	{
		do_list(ch,"");
		pop_call();
		return;
	}

	if (IS_SET(ch->in_room->room_flags, ROOM_PET_SHOP))
	{
		if ((keeper = find_keeper(ch)) == NULL)
		{
			send_to_char("There is nothing for sale here.\n\r",ch);
			pop_call();
			return;
		}
		
		CHECK_KEEPER(ch, keeper);

		pRoomIndexNext = get_room_index(ch->in_room->vnum + 1);

		if (pRoomIndexNext == NULL)
		{
			bug("Do_buy: bad pet shop at vnum %u.", ch->in_room->vnum);
			send_to_char("There is nothing for sale here.\n\r", ch);
			pop_call();
			return;
		}

		in_room = ch->in_room;
		char_from_room(ch);
		char_to_room(ch, pRoomIndexNext->vnum, FALSE);
		pet = get_char_room(ch, arg);
		char_from_room(ch);
		char_to_room(ch, in_room->vnum, FALSE);

		if (pet == NULL || pet->reset == NULL || pet->reset->arg3 != pRoomIndexNext->vnum)
		{
			act("That pet is not available here.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return;
		}

		if (ch->level/2 < pet->level)
		{
			act("$N is too strong for you to purchase now.", ch, NULL, pet, TO_CHAR);
			pop_call();
			return;
		}
		if (get_pet_levels(ch) + pet->level > max_pet_levels(ch))
		{
			act("$N is too strong for you to purchase now.", ch, NULL, pet, TO_CHAR);
			pop_call();
			return;
		}

		if ((cost = haggle(ch, keeper, pet->level * pet->level * 100, TRUE)) > ch->gold)
		{
			act("You cannot afford to buy $N.", ch, NULL, pet, TO_CHAR);
			act("$n haggles over $N.", ch, NULL, pet, TO_ROOM);
			pop_call();
			return;
		}

		pet = create_mobile( pet->pIndexData );
		SET_BIT(pet->act, ACT_PET);

		argument = one_argument(argument, arg);

		if (arg[0] != '\0')
		{
			if (!check_legal_name( ch, arg ))
			{
				junk_mob(pet);
				pop_call();
				return;
			}

			sprintf(buf, "%s, %s", capitalize(arg), pet->short_descr);
			STRFREE(pet->short_descr);
			pet->short_descr = STRALLOC( buf );

			sprintf(buf, "%s %s", arg, pet->name);
			STRFREE(pet->name);
			pet->name = STRALLOC( buf );
		}
		gold_transaction(ch, 0 - cost);
		char_to_room( pet, ch->in_room->vnum, TRUE );
		add_follower( pet, ch );
		act( "You bought $N as a pet.", ch, NULL, pet, TO_CHAR );
		act( "$n bought $N as a pet.", ch, NULL, pet, TO_ROOM );

		pop_call();
		return;
	}
	/*
		handle morgue buying corpses
	*/
	else if (IS_SET(ch->in_room->room_flags, ROOM_MORGUE) && is_name("corpse", arg))
	{
		if ((obj = find_char_corpse(ch, FALSE)) != NULL)
		{
			if (obj->in_room == ch->in_room)
			{
				act("You have already bought your corpse.", ch, NULL, NULL, TO_CHAR);
				pop_call();
				return;
			}

			if (ch->level > 5)
			{
				act("You should be experienced enough to rescue your own corpse. Get friends to help.", ch, NULL, NULL, TO_CHAR);
				pop_call();
				return;
			}

			cost = haggle(ch, keeper, obj->value[3], TRUE);
			
			if (!gold_transaction(ch, 0 - cost))
			{
				act("You do not have enough coin to purchase your corpse.", ch, NULL, NULL, TO_CHAR);
				pop_call();
				return;
			}

			act( "$You pay $t to $n.", keeper, format_coins(cost, TRUE), ch, TO_VICT);
			act( "$N pays $t to $n.", keeper, format_coins(cost, TRUE), ch, TO_NOTVICT);
			act( "$n disappears and returns with your corpse.", keeper, NULL, ch, TO_VICT);
			act( "$n disappears and returns with $N's corpse.", keeper, NULL, ch, TO_NOTVICT);

			ch->pcdata->corpse_room = ch->in_room->vnum;
			obj_to_room(obj, ch->in_room->vnum);

			save_char_obj(ch, NORMAL_SAVE);

			pop_call();
			return;
		}
		else
		{
			act("You do not have a corpse here.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return;
		}
	}
	else if ((keeper = find_keeper(ch)) == NULL)
	{
		for (keeper = ch->in_room->first_person ; keeper ; keeper = keeper->next_in_room)
		{
			if (IS_NPC(keeper) && keeper->pIndexData->vnum == keeper->in_room->area->judge)
			{
				break;
			}
		}
		if (!keeper)
		{
			act("There is nothing here for you to buy.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return;
		}
		
		pRoomIndexNext = get_room_index(ch->in_room->area->storeroom);

		if (pRoomIndexNext == NULL)
		{
			bug("Do_buy: bad impound room at vnum %u.", ch->in_room->vnum);
			act("There is nothing here for you to buy.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return;
		}

		if (!is_number(arg))
		{
			in_room = ch->in_room;
			char_from_room(ch);
			char_to_room(ch, pRoomIndexNext->vnum, FALSE);
			obj = get_obj_list(ch, arg, ch->in_room->first_content);
			char_from_room(ch);
			char_to_room(ch, in_room->vnum, FALSE);

			if (obj == NULL)
			{
				act("That item is not here.", ch, NULL, NULL, TO_CHAR);
				pop_call();
				return;
			}
		}
		else
		{
			for (item_no = 1, obj = pRoomIndexNext->first_content ; obj ; obj = obj->next_content)
			{
				if (item_no == atoi(arg))
				{
					break;
					item_no++;
				}
			}
			if (!obj)
			{
				act("There is no item by that number.", ch, NULL, NULL, TO_CHAR);
				pop_call();
				return;
			}
		}
		if (!IS_OWNER(obj, ch))
		{
			act("%p is not yours to pay out.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return;
		}
		
		cost = obj_cost(obj) / 2;

		if (!gold_transaction(ch, 0 - cost))
		{
			act("You cannot afford to pay out your $p.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return;
		}

		obj_from_room( obj );
		obj_to_char( obj, ch );
		act( "You pay the fine to $N, who gives you $p.", ch, obj, keeper, TO_CHAR );
		act( "$n pays $N for $p.", ch, obj, keeper, TO_ROOM );

		pop_call();
		return;
	}
	else
	{
		obj	= NULL;
		found_obj	= NULL;
		amount = item_no = 1;
		count = -1;

		if ((keeper = find_keeper(ch)) == NULL)
		{
			send_to_char("There is nothing to purchase here.\n\r", ch);
			pop_call();
			return;
		}
		
		CHECK_KEEPER(ch, keeper);

		if (is_number(arg))
		{
			if (argument[0] != '\0')
			{
				amount = UMAX(1, atol(arg));
				strcpy(arg, argument);
			}
			else
			{
				count = atol(arg);
			}
		}

		if ((obj = get_obj_carry_keeper(keeper, arg, ch)) == NULL)
		{
			for (item_no = 1, obj = keeper->first_carrying ; obj ; obj = obj->next_content)
			{
				if (obj->wear_loc == WEAR_NONE &&  can_see_obj(ch, obj)
				&& (cost = get_cost(keeper, obj, TRUE) * 120 / 100) > 0)
				{
					if (count == item_no)
					{
						strcpy(arg, obj->name);
						break;
					}
					item_no++;
				}
			}
			if (!obj)
			{
				act("$N does not have $t to sell you.", ch, format("%s %s", a_an(arg), arg), keeper, TO_CHAR);
				pop_call();
				return;
			}
		}

		if ((cost = haggle(ch, keeper, get_cost(keeper, obj, TRUE), TRUE)) > ch->gold)
		{
			act("You cannot afford to buy $p from $N.", ch, obj, keeper, TO_CHAR);
			act("$n haggles with $N over $p.", ch, obj, keeper, TO_ROOM);
			pop_call();
			return;
		}
		
		if (IS_OBJ_TYPE(obj, ITEM_CART))
		{
			if (amount > 1)
			{
				send_to_char("You can only buy one cart.\n\r", ch);
				pop_call();
				return;
			}
			if (ch->pcdata->cart)
			{
				send_to_char("You already own a cart.\n\r", ch);
				pop_call();
				return;
			}
			if (obj->reset)
			{
				obj = create_object(obj->pIndexData, 0);
				if (IS_OBJ_STAT(obj, ITEM_MAGIC))
					obj->identified = TRUE;
			}
			ch->pcdata->cart = obj;
			ch->pcdata->cart_room	= ch->in_room->vnum;
			obj->owned_by = ch->pcdata->pvnum;
			obj_to_room(obj, ch->in_room->vnum);
			
			act( "You buy $p for $T.", ch, found_obj, format_coins(cost, FALSE), TO_CHAR);
			act( "$n buys $p.", ch, found_obj, NULL, TO_ROOM);
			mprog_buy_trigger(keeper, ch, obj);
			
			pop_call();
			return;
		}

		if (ch->carry_number >= can_carry_n(ch))
		{
			send_to_char("You cannot carry that many items.\n\r", ch);
			pop_call();
			return;
		}

		if (get_carry_w(ch) + get_obj_weight(obj) > can_carry_w(ch) * 2)
		{
			send_to_char("You cannot carry that much weight.\n\r", ch);
			pop_call();
			return;
		}

		found_obj = obj;

		for (total_cost = cnt = 0 ; cnt < amount ; cnt++)
		{
			if ((obj = get_obj_carry_keeper(keeper, arg, ch)) == NULL)
			{
				break;
			}

			if (ch->gold < total_cost + cost)
			{
				break;
			}

			if (ch->carry_number >= can_carry_n(ch))
			{
				break;
			}

			if (get_carry_w(ch) + get_obj_weight(obj) > can_carry_w(ch) * 2)
			{
				break;
			}

			if (obj->reset)
			{
				obj = create_object(obj->pIndexData, 0);
				if (IS_OBJ_STAT(obj, ITEM_MAGIC))
					obj->identified = TRUE;
			}
			obj_to_char(obj, ch);
			mprog_buy_trigger(keeper, ch, obj);
			total_cost += cost;
		}
		if (amount == 1)
		{
			act( "You buy $p for $T.", ch, found_obj, format_coins(total_cost, FALSE), TO_CHAR);
			act( "$n buys $p.", ch, found_obj, NULL, TO_ROOM);
		}
		else
		{
			ch_printf_color(ch, "You buy %d %s for %s.\n\r", cnt, short_to_name(OBJD(found_obj, keeper), cnt), format_coins(total_cost, FALSE));
			sprintf(buf, "$n buys %d %s.", cnt, short_to_name(found_obj->short_descr, cnt));
			act( buf, ch, found_obj, NULL, TO_ROOM);
		}
		gold_transaction(ch, 0 - total_cost);

		pop_call();
		return;
	}
}

/*
 * List the inventory of a shopkeeper
 */
void do_list( CHAR_DATA *ch, char *argument )
{
	ROOM_INDEX_DATA *pRoomIndexNext;
	OBJ_DATA  *obj;
	CHAR_DATA *pet;
	CHAR_DATA *keeper;
	int cost, item_no;
	bool found, fLong;
	char buf[MAX_INPUT_LENGTH], t1[80];
	char buf2[MAX_INPUT_LENGTH];
	char arg1[MAX_INPUT_LENGTH];

	push_call("do_list(%p,%p)",ch,argument);

	if (IS_NPC(ch))
	{	
		pop_call();
		return;
	}
		
	if (IS_SET(ch->in_room->room_flags, ROOM_PET_SHOP))
	{
		if ((keeper = find_keeper(ch)) == NULL)
		{
			send_to_char( "There is nothing to purchase here.\n\r", ch);
			pop_call();
			return;
		}
		
		CHECK_KEEPER(ch, keeper);

		pRoomIndexNext = get_room_index(ch->in_room->vnum + 1);

		if (pRoomIndexNext == NULL)
		{
			bug("do_list: bad pet shop at vnum %u.", ch->in_room->vnum);
			send_to_char("You cannot do that here.\n\r", ch);
			pop_call();
			return;
		}

		found = FALSE;

		for (*buf = '\0', pet = pRoomIndexNext->first_person ; pet ; pet = pet->next_in_room)
		{
			if (pet->reset != NULL && pet->reset->arg3 == pRoomIndexNext->vnum)
			{
				if (!found )
					found = TRUE;

				cost = pet->level * pet->level * 120;
				cat_sprintf(buf, "  %-40s %-36s\n\r", str_resize(pet->short_descr, t1, -40), format_coins(cost, FALSE) );
			}
		}
		if (!found)
		{
			act("%n is all out of pets for now.", keeper, NULL, NULL, TO_ROOM);
			pop_call();
			return;
		}
		ch_printf_color(ch, "%s\n\r", buf);
	}
	else if (IS_SET(ch->in_room->room_flags, ROOM_MORGUE))
	{
		if ((obj = find_char_corpse(ch, FALSE)) != NULL)
		{
			act("It will cost $t to retrieve your remains.", ch, format_coins(obj->value[3],FALSE), NULL, TO_CHAR);
		}
		else
		{
			act("Your corpse is not available here.", ch, NULL, NULL, TO_CHAR);
		}
	}
	else if ((keeper = find_keeper(ch)) == NULL)
	{
		for (keeper = ch->in_room->first_person ; keeper ; keeper = keeper->next_in_room)
		{
			if (IS_NPC(keeper) && keeper->pIndexData->vnum == keeper->in_room->area->judge)
			{
				break;
			}
		}
		if (!keeper)
		{
			act("There is nothing here for you to buy.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return;
		}
		
		pRoomIndexNext = get_room_index(ch->in_room->area->storeroom);

		if (pRoomIndexNext == NULL)
		{
			bug("Do_list: bad impound room at vnum %u.", ch->in_room->vnum);
			act("There is nothing here for you to buy.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return;
		}

		for (item_no = 1, found = FALSE, obj = pRoomIndexNext->first_content ; obj ; obj = obj->next_content)
		{
			if (IS_OWNER(obj, ch))
			{
				found = TRUE;
				cost = obj_cost(obj) / 2;

				sprintf(buf2, "%s", str_resize(OBJD(obj, keeper), t1, -40));
				cat_sprintf(buf, "  %2d{068}]{300} %-40s %s\n\r{300}", item_no, buf2, format_coins(cost,FALSE));
				item_no++;
			}
		}
		if (!found)
		{
			strcpy(buf, "You have nothing confiscated here.");
		}
		send_to_char_color(buf, ch);
	}
	else
	{
		cost  = 0;
		found = FALSE;
		fLong = FALSE;

		argument = one_argument( argument, arg1 );
		
		if (!strcasecmp(arg1, "long"))
			fLong = TRUE;

		CHECK_KEEPER(ch, keeper);

		sprintf(buf, "%s shows you %s wares.\n\r", capitalize(keeper->short_descr), his_her[URANGE(0, keeper->sex, 2)]);
		for (item_no = 1, obj = keeper->first_carrying ; obj ; obj = obj->next_content)
		{
			if (obj->wear_loc == WEAR_NONE &&  can_see_obj(ch, obj)
			&& (cost = get_cost(keeper, obj, TRUE) * 120 / 100) > 0)
			{
				if (!found)
					found = TRUE;

				if (fLong)
				{
					cat_sprintf(buf, "  %2d{068}]{300} %s\n\r{300}", item_no, OBJD(obj, keeper));
					item_no++;
				}
				else
				{
					sprintf(buf2, "%s", str_resize(OBJD(obj, keeper), t1, -40));
					cat_sprintf(buf, "  %2d{068}]{300} %-40s %s\n\r{300}", item_no, buf2, format_coins(cost,FALSE));
					item_no++;
				}
			}
		}
		if (!found)
		{
			strcpy(buf, "There is nothing to purchase here.\n\r");
		}
		send_to_char_color(buf, ch);
	}
	pop_call();
	return;
}

/*
 * Sell an item to a shop
 */
void do_sell( CHAR_DATA *ch, char *argument )
{
	char buf[MAX_STRING_LENGTH];
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *keeper;
	OBJ_DATA *obj;
	int cost;

	push_call("do_sell(%p,%p)",ch,argument);

	one_argument( argument, arg );

	if ((keeper = find_keeper(ch)) == NULL)
	{
		send_to_char("You cannot sell anything here.\n\r", ch);
		pop_call();
		return;
	}

	if ( arg[0] == '\0' )
	{
		send_to_char( "Sell what?\n\r", ch );

		pop_call();
		return;
	}

	CHECK_KEEPER(ch, keeper);

	if ((obj = get_obj_carry(ch,arg)) == NULL)
	{
		command(keeper, do_sayto, "%s You don't have that item.", short_to_name(get_name(ch),1));
		pop_call();
		return;
	}

	if (!can_drop_obj(ch, obj))
	{
		send_to_char( "You cannot let go of it.\n\r", ch );
		pop_call();
		return;
	}

	if (obj->owned_by)
	{
		command(keeper, do_say, "Sorry, I do not buy personalized items.");
		pop_call();
		return ;
	}
	
	// Safety against selling full containers - Kregor
	if (obj->first_content)
	{
		send_to_char( "You need to empty it before you sell it.\n\r", ch );
		pop_call();
		return;
	}

	if ((cost = get_cost(keeper, obj, FALSE)) < 1 || haggle(ch, keeper, cost, FALSE) <= 1)
	{
		act( "$n looks uninterested in $p.", keeper, obj, ch, TO_VICT );
		pop_call();
		return;
	}

	sprintf(buf, "You sell $p for %s.", format_coins(cost,FALSE));
	act( buf, ch, obj, NULL, TO_CHAR );
	act( "$n sells $p.", ch, obj, NULL, TO_ROOM );

	give_gold(ch, cost);

	obj_to_char(obj, keeper);

	pop_call();
	return;
}

/*
 * Get shopkeeper's offer on an item.
 */
void do_value( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *keeper;
	OBJ_DATA *obj;
	int cost;

	push_call("do_value(%p,%p)",ch,argument);

	one_argument( argument, arg );

	if ((keeper = find_keeper(ch)) == NULL)
	{
		send_to_char("You cannot sell anything here.\n\r",ch);
		pop_call();
		return;
	}

	if (arg[0] == '\0')
	{
		send_to_char("What are you offering to sell?\n\r", ch);
		pop_call();
		return;
	}

	CHECK_KEEPER(ch, keeper);

	if ((obj = get_obj_carry(ch, arg)) == NULL)
	{
		command(keeper, do_sayto, "%s You don't have that item.", short_to_name(get_name(ch),1));
		pop_call();
		return;
	}

	if (!can_drop_obj(ch, obj))
	{
		send_to_char( "You cannot let go of it.\n\r", ch );
		pop_call();
		return;
	}

	if ((cost = get_cost(keeper, obj, FALSE)) < -1 || cost * 80 / 100 < 1)
	{
		act( "$n looks uninterested in $p.", keeper, obj, ch, TO_ROOM );
		pop_call();
		return;
	}
	command(keeper, do_sayto, "%s I will offer you %s for %s.", short_to_name(get_name(ch),1), ansi_strip(ansi_translate(format_coins(cost,TRUE))), OBJD(obj, keeper));
	pop_call();
	return;
}

/*
 * Pay shopkeeper fee to identify an object for you
 */
void do_identify( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];

	push_call("do_identify(%p,%p)",ch,argument);

	argument = one_argument( argument, arg );

	{
		CHAR_DATA *keeper;
		OBJ_DATA *obj;
		int cost;

		if ((keeper = find_keeper(ch)) == NULL)
		{
			send_to_char( "You must be in a shop to have items identified.\n\r", ch );
			pop_call();
			return;
		}

		if (!IS_SET(keeper->pIndexData->pShop->shop_flags, SHOP_IDENTIFY))
		{
			send_to_char( "This shop does not offer to identify items.\n\r", ch );
			pop_call();
			return;
		}

		if (arg[0] == '\0')
		{
			send_to_char( "Have what identified?\n\r", ch );
			pop_call();
			return;
		}
		
		CHECK_KEEPER(ch, keeper);

		if ((obj = get_obj_here(ch, arg)) == NULL)
		{
			command(keeper, do_sayto, "%s You don't seem to have a '%s' on you.", short_to_name(get_name(ch),1), arg);
			pop_call();
			return;
		}

		if (!IS_OBJ_STAT(obj, ITEM_MAGIC))
		{
			command(keeper, do_sayto, "%s Identifying that would be a waste of your coin.", short_to_name(get_name(ch),1));
			pop_call();
			return;
		}
		
		cost = haggle(ch, keeper, 10000, TRUE);

		if (!gold_transaction(ch, 0 - cost))
		{
			command(keeper, do_sayto, "%s I will charge you %s to identify %s.", short_to_name(get_name(ch),1), ansi_strip(ansi_translate(format_coins(cost,TRUE))), obj->short_descr);
			command(keeper, do_sayto, "%s Which, I see, you cannot afford.", short_to_name(get_name(ch),1));
			pop_call();
			return;
		}

		act( "You pay $T to have $p identified.", ch, obj, format_coins(cost,TRUE), TO_CHAR);
		act( "$n pays $T to have $p identified.", ch, obj, format_coins(cost,TRUE), TO_ROOM );

		keeper->gold += cost;

		show_identify_string(obj, ch);

		pop_call();
		return;
	}
}

/*
 * Find the corpse of a character for moruge to retrieve.
 */
OBJ_DATA * find_char_corpse (CHAR_DATA *ch, bool method)
{
	OBJ_DATA *corpse;

	push_call("find_char_corpse(%p,%p)",ch,method);

	/* find the player's first corpse with objects in it */

	if (method)
	{
		for (corpse = obj_index[OBJ_VNUM_CORPSE_PC]->first_instance ; corpse ; corpse = corpse->next_instance)
		{
			if (corpse->first_content
			&&  corpse->owned_by == ch->pcdata->pvnum
			&& !IS_SET(corpse->extra_flags, ITEM_NOT_VALID))
			{
				break;
			}
		}
	}
	else
	{
		corpse = ch->pcdata->corpse;
	}

	if (corpse != NULL)
	{
		OBJ_DATA *obj;

		if (ch->level <= 5)
		{
			corpse->value[3] = ch->level * ch->level * 65;
		}
		else
		{
			corpse->value[3] = ch->level * ch->level * ch->level;

			for (obj = corpse->first_content ; obj ; obj = obj->next_content)
			{
				corpse->value[3] += obj->cost / 10;
			}
		}
	}
	pop_call();
	return corpse;
}

/*
 * DO_BANK
 *
 * This code manages the character's bank account in pcdata - Presto 1997
 */
void do_bank( CHAR_DATA *ch, char *argument )
{
	CHAR_DATA *target;
	CHAR_DATA *mob;
	char choice[MAX_STRING_LENGTH];
	char buf[MAX_STRING_LENGTH];
	char buf1[MAX_STRING_LENGTH];
	char amt[MAX_STRING_LENGTH];
	char coin[MAX_STRING_LENGTH];
	char arg[MAX_STRING_LENGTH];

	int  amount      = 0;

	push_call("do_bank(%p,%p)",ch,argument);

	if ((mob = find_banker(ch)) == NULL)
	{
		send_to_char( "There's no banker here.\n\r", ch );
		pop_call();
		return;
	}

	ch->pcdata->bank = URANGE(0, ch->pcdata->bank, account_max(ch));

	argument = one_argument(argument, choice);
	argument = one_argument(argument, amt);
	argument = one_argument(argument, coin);

	if (choice[0] == '\0')
	{
		send_to_char("Do you wish to deposit, withdraw, or transfer?\n\r", ch);
	}

	if (!strcasecmp(amt, "all"))
	{
		amount = -1;
	}
	else
	{
		amount = atoi(amt);

		if (amount < 0)
		{
			amount = 0;
		}
	}

	if (*choice == '\0' || *choice == ' ')
	{
		*choice = 'b';
	}

	switch (*choice)
	{
		case 'd':
			if (amount == -1)
			{
				amount = ch->gold;
			}
			if (amount == 0 || (strcasecmp(coin, "gold") && strcasecmp(coin, "silver") && strcasecmp(coin, "copper")))
			{
				send_to_char("Deposit how much of what type coin, or deposit it all?\n\r", ch);
				pop_call();
				return;
			}
			if (amount > 0 && !strcasecmp(coin, "gold"))
			{
				amount *= 100;
				sprintf(buf1, "{138}%d gold %s{300}", amount/100, amount > 100 ? "pieces" : "piece");
			}
			else if (amount > 0 && !strcasecmp(coin, "silver"))
			{
				amount *= 10;
				sprintf(buf1, "{078}%d silver %s{300}", amount/10, amount > 10 ? "pieces" : "piece");
			}
			else if (amount > 0 && !strcasecmp(coin, "copper"))
			{
				amount *= 1;
				sprintf(buf1, "{038}%d copper %s{300}", amount, amount > 1 ? "pieces" : "piece");
			}
			if (amount > ch->gold)
			{
				send_to_char("You don't have that much gold.\n\r", ch);
				pop_call();
				return;
			}
			else
			{
				if (ch->pcdata->bank + amount > account_max(ch))
				{
					amount = account_max(ch) - ch->pcdata->bank;
				}
				ch->gold -= amount;
				ch->pcdata->bank += amount;
				sprintf(buf, "You deposit %s into your account.\n\rYour account balance is now %s.\n\r", buf1, format_coins(ch->pcdata->bank,TRUE));
				send_to_char_color(buf, ch);
				ch->carry_weight = get_carry_w(ch);
			}
			break;

		case 'w':
			if (amount == -1)
			{
				amount = ch->pcdata->bank;
			}
			if (amount > ch->pcdata->bank)
			{
				send_to_char("You don't have that much gold in your account.\n\r", ch);
				pop_call();
				return;
			}
			if (amount == 0 || (strcasecmp(coin, "gold") && strcasecmp(coin, "silver") && strcasecmp(coin, "copper")))
			{
				send_to_char("Withdraw how much of what type coin, or withdraw it all?\n\r", ch);
				pop_call();
				return;
			}
			if (amount > 0 && !strcasecmp(coin, "gold"))
			{
				amount *= 100;
				sprintf(buf1, "{138}%d gold %s{300}", amount/100, amount > 100 ? "pieces" : "piece");
			}
			else if (amount > 0 && !strcasecmp(coin, "silver"))
			{
				amount *= 10;
				sprintf(buf1, "{078}%d silver %s{300}", amount/10, amount > 10 ? "pieces" : "piece");
			}
			else if (amount > 0 && !strcasecmp(coin, "copper"))
			{
				amount *= 1;
				sprintf(buf1, "{038}%d copper %s{300}", amount, amount > 1 ? "pieces" : "piece");
			}
			ch->gold += amount;
			ch->pcdata->bank -= amount;
			ch->carry_weight = get_carry_w(ch);
			sprintf(buf, "You withdraw %s from your account.\n\rYour account balance is now %s.\n\r", buf1, format_coins(ch->pcdata->bank,TRUE));
			send_to_char_color(buf, ch);
			break;

		case 'b':
			sprintf(buf, "You have %s in your account.\n\r",
			format_coins(ch->pcdata->bank,TRUE));
			cat_sprintf(buf, "Your maximum balance is %s\n\r", format_coins(account_max(ch),TRUE));
			send_to_char_color(buf, ch);
			break;

		case 't': case 'T':
			argument = one_argument(argument, arg);
			if (*arg == '\0')
			{
				send_to_char("Transfer how much money to whom?\n\r", ch);
				pop_call();
				return;
			}

			if ((target = get_char_room(ch, arg)) == NULL)
			{
				send_to_char("They must be in the bank with you.\n\r", ch);
				pop_call();
				return;
			}

			if (IS_NPC(target))
			{
				send_to_char("Only players have accounts, silly.\n\r", ch);
				pop_call();
				return;
			}
			if (amount == -1)
			{
				send_to_char("You cannot transfer all your money.\n\r", ch);
				pop_call();
				return;
			}
			if (amount == 0 || (strcasecmp(coin, "gold") && strcasecmp(coin, "silver") && strcasecmp(coin, "copper")))
			{
				send_to_char("Transfer how much of what type coin to whom?\n\r", ch);
				pop_call();
				return;
			}
			if (amount > 0 && !strcasecmp(coin, "gold"))
			{
				amount *= 100;
				sprintf(buf1, "{138}%d gold %s{300}", amount/100, amount > 100 ? "pieces" : "piece");
			}
			else if (amount > 0 && !strcasecmp(coin, "silver"))
			{
				amount *= 10;
				sprintf(buf1, "{078}%d silver %s{300}", amount/10, amount > 10 ? "pieces" : "piece");
			}
			else if (amount > 0 && !strcasecmp(coin, "copper"))
			{
				amount *= 1;
				sprintf(buf1, "{038}%d copper %s{300}", amount, amount > 1 ? "pieces" : "piece");
			}
			if (ch->pcdata->bank < amount)
			{
				send_to_char("You don't have enough gold in your account.\n\r", ch);
				pop_call();
				return;
			}

			if (target->pcdata->bank + amount > account_max(target))
			{
				send_to_char("Their account cannot hold that much.\n\r", ch);
				amount = account_max(target) - target->pcdata->bank;
			}
			ch->pcdata->bank     -= amount;
			target->pcdata->bank += amount;
			ch_printf_color(ch, "You transfer %s into %s's account.\n\rYour account balance is now %s.\n\r", buf1, PERS(target,ch), format_coins(ch->pcdata->bank,TRUE));
			ch_printf_color(target, "%s transfers %s into your account.\n\rYour account balance is now %s.\n\r", PERS(ch,target), buf1, format_coins(target->pcdata->bank,TRUE));
			break;

		default:
			send_to_char("Usage: bank <deposit|withdraw|balance|transfer> [<amount><coin>|all] [target]\n\r", ch);
			break;
	}
	pop_call();
	return;
}


int account_max(CHAR_DATA* ch)
{
	int amt;
	
	push_call("account_max(%p)",ch);

	amt = (ch->level * 5) * 2000000;
	
	pop_call();
	return amt;
}


/*
 * Give an amount of coins (in copper) to char
 */
int give_gold(CHAR_DATA* ch, int amount)
{
	int num;

	push_call("give_gold(%p,%p)",ch,amount);

	if (!IS_NPC(ch) && ch->pcdata->clan && ch->pcdata->clan->tax > 0 && ch->pcdata->clan->tax <= 100)
	{
		num = (float) amount * ((float) ch->pcdata->clan->tax / 100);
		if (num > 0 && ch->pcdata->clan->coffers < max_coffer(ch->pcdata->clan))
		{
			if (ch->pcdata->clan->coffers + num > max_coffer(ch->pcdata->clan))
			{
				num = max_coffer(ch->pcdata->clan) - ch->pcdata->clan->coffers;
			}
			ch->pcdata->clan->coffers += num;
			amount -= num;
			ch->gold += amount;
			ch->carry_weight = get_carry_w(ch);

			pop_call();
			return num;
		}
		else
		{
			ch->gold += amount;
			ch->carry_weight = get_carry_w(ch);

			pop_call();
			return 0;
		}
	}
	else
	{
		ch->gold += amount;
		ch->carry_weight = get_carry_w(ch);
	}
	pop_call();
	return 0;
}


void do_rent( CHAR_DATA *ch, char *argument )
{
	char buf[MAX_STRING_LENGTH];
	CHAR_DATA *keeper = NULL;
	CHAR_DATA *gch;
	INN_DATA *pInn;
	int iRoom, cost, count;

	push_call("do_rent(%p,%p)",ch,argument);

	for (keeper = ch->in_room->first_person ; keeper ; keeper = keeper->next_in_room)
	{
		if (!IS_NPC(keeper))
			continue;
		if ((pInn = keeper->pIndexData->pInn) != NULL)
		{
			break;
		}
	}
	if (!keeper)
	{
		send_to_char("You cannot rent a room here.\n\r", ch);
		pop_call();
		return;
	}
	if (!can_see(keeper, ch))
	{
		send_to_char("The shopkeeper can't see you to trade with you!\n\r", ch);
		pop_call();
		return;
	}
	
	if (is_grudging(keeper, ch))
	{
		pop_call();
		return;
	}
		
	if (argument[0] == '\0')
	{
		act( "$N shows you a list of rooms and their rates.", ch, NULL, keeper, TO_CHAR);
		act( "$N shows $n a list of rooms and their rates.", ch, NULL, keeper, TO_ROOM);
		
		for (iRoom = 0 ; iRoom < MAX_INN_ROOMS ; iRoom++)
		{
			if (pInn->room[iRoom] > 0)
			{
				ch_printf_color(ch, "   {078}%d{200}] {178}%-36s %s \n\r",
					iRoom+1, str_resize(room_index[pInn->room[iRoom]]->name, buf, -36),
					format_coins(pInn->rent[iRoom], TRUE));
			}
		}
		ch_printf_color(ch, "Syntax: rent <room number>\n\r");
		ch_printf_color(ch, "(group with others to rent the same room.)\n\r");
		pop_call();
		return;
	}
	if (is_number(argument))
	{
		if ((iRoom = atoi(argument)-1) < 0 || iRoom >= MAX_INN_ROOMS || pInn->room[iRoom] == -1)
		{
			send_to_char("That is not a valid room number.\n\r", ch);
			pop_call();
			return;
		}
		if (room_index[pInn->room[iRoom]] == NULL)
		{
			log_build_printf(keeper->pIndexData->vnum, "do_rent: NULL room found for vnum $d", pInn->room[iRoom]);
			pop_call();
			return;
		}
		if ((cost = pInn->rent[iRoom]) > ch->gold)
		{
			command(keeper, do_say, "That room costs %s,", format_coins(cost, TRUE));
			command(keeper, do_say, "which it seems you cannot afford.");
			pop_call();
			return;
		}
		if (room_index[pInn->room[iRoom]]->first_person)
		{
			command(keeper, do_say, "Sorry, that room is rented.");
			pop_call();
			return;
		}

		gold_transaction(ch, 0 - cost);
		act( "You pay $t to $N.", ch, format_coins(cost, FALSE), keeper, TO_CHAR);
		act( "$n pays $t to $N.", ch, format_coins(cost, FALSE), keeper, TO_ROOM);

		for (count = 0, gch = ch->in_room->first_person ; gch ; gch = gch->next_in_room)
		{
			if (!is_same_group(gch, ch))
				continue;
			if (gch == ch)
				continue;
			char_from_room(gch);
			char_to_room(gch, pInn->room[iRoom], TRUE);
			act( "You are shown to your room.", gch, NULL, NULL, TO_CHAR);
			do_look(gch,"");
			count++;
		}
		if (count)
			act( "$n's party is shown to their room.", ch, NULL, NULL, TO_ROOM);
		else
			act( "$n is shown to $s room.", ch, NULL, NULL, TO_ROOM);
		act( "You are shown to your room.", ch, NULL, NULL, TO_CHAR);
		char_from_room(ch);
		char_to_room(ch, pInn->room[iRoom], TRUE);
		do_look(ch, "");
	}
	else
	{
		ch_printf_color(ch, "Syntax: rent <room number>\n\r");
	}
	pop_call();
	return;
}

void do_stable( CHAR_DATA *ch, char *argument )
{
	CHAR_DATA *keeper = NULL;
	CHAR_DATA *victim;
	STABLE_DATA *pStable;
	int cost;

	push_call("do_stable(%p,%p)",ch,argument);

	for (keeper = ch->in_room->first_person ; keeper ; keeper = keeper->next_in_room)
	{
		if (!IS_NPC(keeper))
			continue;
		if ((pStable = keeper->pIndexData->pStable) != NULL)
		{
			break;
		}
	}
	if (!keeper)
	{
		send_to_char("You cannot stable your pets here.\n\r", ch);
		pop_call();
		return;
	}
	if (!can_see(keeper, ch))
	{
		send_to_char("The shopkeeper can't see you to trade with you!\n\r", ch);
		pop_call();
		return;
	}
	
	if (is_grudging(keeper, ch))
	{
		pop_call();
		return;
	}
		
	cost = pStable->rent;

	if (argument[0] == '\0')
	{
		command(keeper, do_say, "Who do you wish to stable?");
		command(keeper, do_say, "The cost for each is %s.", ansi_strip(ansi_translate(format_coins(cost, TRUE))));
		pop_call();
		return;
	}
	
	if ((victim = get_char_room(ch, argument)) == NULL)
	{
		send_to_char("That pet doesn't seem to be here.\n\r", ch);
		pop_call();
		return;
	}
	
	if (cost > ch->gold)
	{
		command(keeper, do_say, "The cost to stable is %s.", format_coins(cost, TRUE));
		command(keeper, do_say, "which it seems you cannot afford.");
		pop_call();
		return;
	}
	
	gold_transaction(ch, 0 - cost);
	act( "You pay $t to $N.", ch, format_coins(cost, FALSE), keeper, TO_CHAR);
	act( "$n pays $t to $N.", ch, format_coins(cost, FALSE), keeper, TO_ROOM);
	act( "$n leads $N into the stables.", keeper, NULL, victim, TO_ALL);

	command(keeper, do_sayto, "%s You may CLAIM your pet when you want it.", short_to_name(ch->name, 1));

	char_from_room(victim);
	char_to_room(victim, pStable->room, TRUE);
	do_look(victim, "");
	
	pop_call();
	return;
}

void do_claim( CHAR_DATA *ch, char *argument )
{
	CHAR_DATA *keeper = NULL;
	CHAR_DATA *victim;
	STABLE_DATA *pStable;
	ROOM_INDEX_DATA *room;
	int count;

	push_call("do_claim(%p,%p)",ch,argument);

	for (keeper = ch->in_room->first_person ; keeper ; keeper = keeper->next_in_room)
	{
		if (!IS_NPC(keeper))
			continue;
		if ((pStable = keeper->pIndexData->pStable) != NULL)
		{
			break;
		}
	}
	if (!keeper)
	{
		send_to_char("You are in no place to claim anything.\n\r", ch);
		pop_call();
		return;
	}
	if (!can_see(keeper, ch))
	{
		send_to_char("The shopkeeper can't see you to trade with you!\n\r", ch);
		pop_call();
		return;
	}
	
	if (is_grudging(keeper, ch))
	{
		pop_call();
		return;
	}
		
	if ((room = room_index[pStable->room]) == NULL)
	{
		log_build_printf(keeper->pIndexData->vnum, "do_claim: NULL room found for vnum $d", pStable->room);
		pop_call();
		return;
	}

	if (argument[0] == '\0')
	{
		for (count = 0, victim = room->first_person ; victim ; victim = victim->next_in_room)
		{
			if (!IS_ACT(victim, ACT_PET))
				continue;
			if (victim->master != ch)
				continue;
			ch_printf_color(ch, "   {078}%d{200}] {178}%-36s\n\r",
				count+1, get_name(victim));
			count++;
		}
		if (!count)
			act("You do not have any pets stabled here.", ch, NULL, NULL, TO_CHAR);
		else
			ch_printf_color(ch, "Syntax: claim <pet name or number>\n\r");
		pop_call();
		return;
	}

	for (count = 0, victim = room->first_person ; victim ; victim = victim->next_in_room)
	{
		if (!IS_ACT(victim, ACT_PET))
			continue;
		if (victim->master != ch)
			continue;
		count++;
		if (is_multi_name_list_short(argument, get_name(victim)))
			break;
		if (is_number(argument) && atoi(argument) == count)
			break;
	}
	if (!victim)
	{
		send_to_char("That pet doesn't seem to be stabled here.\n\r", ch);
		pop_call();
		return;
	}

	act( "$n leads $N from the stables.", keeper, NULL, victim, TO_ALL);

	char_from_room(victim);
	char_to_room(victim, keeper->in_room->vnum, TRUE);
	do_look(victim, "");
	
	pop_call();
	return;
}

/*
 * The all new, much improved HEAL command for healer mobs.
 * Adding spells to the list as easy as adding their names
 * to the table here.
 * Yes, the costs are expensive as hell, based on d20 srd
 * standard prices, with a faith discount and subject
 * to haggling. Kregor 3/4/08
 */
char *  const   healer_spells [] =
{
	"cure light",
	"cure moderate",
	"cure serious",
	"cure critical",
	"heal",
	"lesser restoration",
	"neutralize poison",
	"refresh",
	"remove blindness",
	"remove curse",
	"restoration",
	"*"
};

void do_heal(CHAR_DATA *ch, char *argument)
{
	CHAR_DATA *mob;
	char buf[MAX_STRING_LENGTH];
	int class, value, sn, cost, x;

	push_call("(do_heal%p,%p)",ch,argument);

	/*
		check for healer
	*/
	for (mob = ch->in_room->first_person; mob; mob = mob->next_in_room)
	{
		if (IS_NPC(mob) && IS_SET(mob->act, ACT_IS_HEALER))
		{
			break;
		}
	}

	if (mob == NULL)
	{
		send_to_char( "There are no healing services here.\n\r", ch );
		pop_call();
		return;
	}
	
	if (in_combat(mob) || who_fighting(mob) != NULL)
	{
		send_to_char( "The healer is a little busy right now.\n\r", ch );
		pop_call();
		return;
	}
	if (!IS_AWAKE(ch))
	{
		send_to_char( "In your dreams, or what?\n\r", ch );
		pop_call();
		return;
	}
	if (!IS_AWAKE(mob))
	{
		send_to_char( "In their dreams, or what?\n\r", ch );
		pop_call();
		return;
	}
	if (is_grudging(mob, ch))
	{
		pop_call();
		return;
	}
	if (!can_see(mob, ch))
	{
		act("$N cannot trade with someone $E cannot see.", ch, NULL, mob, TO_CHAR);
		pop_call();
		return;
	}

	sn = -1;
	
	if (argument[0] == '\0')
	{
		act( "$N offers the following spells.", ch, NULL, mob, TO_CHAR);
	
		for (buf[0] = '\0', x = 0 ; x < 64 ; x++)
		{
			if (!strcmp("*", healer_spells[x]))
			{
				break;
			}
			
			if ((sn = skill_lookup(healer_spells[x])) == -1)
			{
				bug( "do_heal: bad sn on spell name '%s'\n\r", healer_spells[x]);
				continue;
			}
			
			if ((class = multi(mob, sn)) == -1)
				continue;

			if (get_spell_circle(mob, sn) == -1)
				continue;

			// cost of spells is 10 gold per spell level times caster level
			cost = get_spell_circle(mob, sn) * 1000;
			cost *= class_level(mob, class);

			if (ch->god == mob->god)
				cost /= cost * 2 / 3;
			
			cat_sprintf(buf, "  {178}%-24s {078}(spell level: %d) {138}%s\n\r", skill_table[sn].name, get_spell_circle(mob, sn), format_coins(cost * 120 / 100, FALSE));
		}
		cat_sprintf(buf, "Syntax: heal <spell name>.\n\r");
		send_to_char_color(buf, ch);
		pop_call();
		return;
	}
		
	if ((value = get_flag(argument, healer_spells)) == -1)
	{
		ch_printf_color(ch, "Type 'heal' for a list of spells.\n\r");
		pop_call();
		return;
	}
	if ((sn = skill_lookup(healer_spells[value])) == -1)
	{
		ch_printf_color(ch, "That doesn't seem to be an available spell.\n\r");
		pop_call();
		return;
	}
	if ((class = multi(mob, sn)) == -1)
	{
		ch_printf_color(ch, "That doesn't seem to be an available spell.\n\r");
		pop_call();
		return;
	}
	if (IS_SET(mob->action, ACTION_STANDARD))
	{
		ch_printf_color(ch, "Just give them a little time between castings, eh?\n\r");
		pop_call();
		return;
	}
	
	cost = get_spell_circle(mob, sn) * 1000;
	cost *= class_level(mob, class);
	/* set up healers for haggling too */
	cost = haggle(ch, mob, cost, TRUE);

	if (ch->god == mob->god)
		cost = cost * 2 / 3;

	if (!gold_transaction(ch, 0 - cost))
	{
		act("You cannot afford that prayer.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return;
	}

	if (ch->god == mob->god)
		act("You are entitled to a lower charge, as a fellow faithful.", ch, NULL, NULL, TO_CHAR);

	act( "You pay $t to $N.", ch, format_coins(cost, TRUE), mob, TO_CHAR);
	act( "$n pays $t to $N.", ch, format_coins(cost, TRUE), mob, TO_ROOM);
	
	command(mob, do_cast, "'%s' %s", skill_table[sn].name, short_to_name(get_name(ch),1));

	pop_call();
	return;
}