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