/***************************************************************************
* 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;
}