/*************************************************************************** * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik Strfeldt, Tom Madsen, and Katja Nyboe. * * * * Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * * * In order to use any part of this Merc Diku Mud, you must comply with * * both the original Diku license in 'license.doc' as well the Merc * * license in 'license.txt'. In particular, you may not remove either of * * these copyright notices. * * * * Much time and thought has gone into this software and you are * * benefitting. We hope that you share your changes too. What goes * * around, comes around. * **************************************************************************/ /*************************************************************************** * ROM 2.4 is copyright 1993-1998 Russ Taylor * * ROM has been brought to you by the ROM consortium * * Russ Taylor (rtaylor@hypercube.org) * * Gabrielle Taylor (gtaylor@hypercube.org) * * Brian Moore (zump@rom.org) * * By using this code, you have agreed to follow the terms of the * * ROM license, in the file Rom24/doc/rom.license * **************************************************************************/ /* JH 3/22/2004 11:25PM * Shops.c: Buying and selling code. Includes buy, sell, value, list, * * and shop assist functions. Mainly from act_obj.c * */ #if defined(macintosh) #include <types.h> #else #include <sys/types.h> #endif #include <cstdio> #include <cstring> #include <cstdlib> #include "merc.h" #include "interp.h" /* * Local functions. */ CHAR_DATA *find_keeper args ((CHAR_DATA * ch)); int get_cost args ((CHAR_DATA * keeper, OBJ_DATA * obj, bool fBuy)); void obj_to_keeper args ((OBJ_DATA * obj, CHAR_DATA * ch)); OBJ_DATA *get_obj_keeper args ((CHAR_DATA * ch, CHAR_DATA * keeper, char *argument)); OBJ_DATA *get_obj_keeper_menu args ((CHAR_DATA * ch, CHAR_DATA * keeper, int which)); extern MENU_DATA *menu_list; /* * Shopping commands. */ CHAR_DATA *find_keeper (CHAR_DATA * ch) { /*char buf[MAX_STRING_LENGTH]; */ CHAR_DATA *keeper; SHOP_DATA *pShop; pShop = NULL; for (keeper = ch->in_room->people; keeper; keeper = keeper->next_in_room) { if (IS_NPC (keeper) && (pShop = keeper->pIndexData->pShop) != NULL) break; } if (pShop == NULL) { send_to_char ("You can't do that here.\n\r", ch); return NULL; } /* * Undesirables. * if ( !IS_NPC(ch) && IS_SET(ch->act, PLR_KILLER) ) { do_function(keeper, &do_say, "Killers are not welcome!"); sprintf(buf, "%s the KILLER is over here!\n\r", ch->name); do_function(keeper, &do_yell, buf ); return NULL; } if ( !IS_NPC(ch) && IS_SET(ch->act, PLR_THIEF) ) { do_function(keeper, &do_say, "Thieves are not welcome!"); sprintf(buf, "%s the THIEF is over here!\n\r", ch->name); do_function(keeper, &do_yell, buf ); return NULL; } */ /* * Shop hours. */ if (time_info.hour < pShop->open_hour) { do_function (keeper, &do_say, "Sorry, I am closed. Come back later."); return NULL; } if (time_info.hour > pShop->close_hour) { do_function (keeper, &do_say, "Sorry, I am closed. Come back tomorrow."); return NULL; } /* * Invisible or hidden people. */ if (!can_see (keeper, ch)) { do_function (keeper, &do_say, "I don't trade with folks I can't see."); return NULL; } return keeper; } /* insert an object at the right spot for the keeper */ void obj_to_keeper (OBJ_DATA * obj, CHAR_DATA * ch) { OBJ_DATA *t_obj, *t_obj_next; /* see if any duplicates are found */ for (t_obj = ch->carrying; t_obj != NULL; t_obj = t_obj_next) { t_obj_next = t_obj->next_content; if (obj->pIndexData == t_obj->pIndexData && !str_cmp (obj->short_descr, t_obj->short_descr)) { /* if this is an unlimited item, destroy the new one */ if (IS_OBJ_STAT (t_obj, ITEM_INVENTORY)) { extract_obj (obj); return; } obj->cost = t_obj->cost; /* keep it standard */ break; } } if (t_obj == NULL) { obj->next_content = ch->carrying; ch->carrying = obj; } else { obj->next_content = t_obj->next_content; t_obj->next_content = obj; } obj->carried_by = ch; obj->in_room = NULL; obj->in_obj = NULL; ch->carry_number += get_obj_number (obj); ch->carry_weight += get_obj_weight (obj); } /* get an object from a shopkeeper's list (menu version)*/ OBJ_DATA *get_obj_keeper_menu (CHAR_DATA * ch, CHAR_DATA * keeper, int which) { int item_pos = 0, count = 0, cost = 0; OBJ_DATA *obj; for (obj = keeper->carrying; obj; obj = obj->next_content) { if (obj->wear_loc == WEAR_NONE && can_see_obj (ch, obj) && (cost = get_cost (keeper, obj, TRUE)) > 0) { item_pos++; if (item_pos == which) return obj; if (!IS_OBJ_STAT (obj, ITEM_INVENTORY)) { count = 1; while (obj->next_content != NULL && obj->pIndexData == obj->next_content->pIndexData && !str_cmp (obj->short_descr, obj->next_content->short_descr)) { obj = obj->next_content; count++; } } } } return NULL; } /* get an object from a shopkeeper's list */ OBJ_DATA *get_obj_keeper (CHAR_DATA * ch, CHAR_DATA * keeper, char *argument) { char arg[MAX_INPUT_LENGTH]; OBJ_DATA *obj; int number; int count; number = number_argument (argument, arg); count = 0; for (obj = keeper->carrying; obj != NULL; obj = obj->next_content) { if (obj->wear_loc == WEAR_NONE && can_see_obj (keeper, obj) && can_see_obj (ch, obj) && is_name (arg, obj->name)) { if (++count == number) return obj; /* skip other objects of the same name */ while (obj->next_content != NULL && obj->pIndexData == obj->next_content->pIndexData && !str_cmp (obj->short_descr, obj->next_content->short_descr)) obj = obj->next_content; } } return NULL; } int get_cost (CHAR_DATA * keeper, OBJ_DATA * obj, bool fBuy) { SHOP_DATA *pShop; int cost; if (obj == NULL || (pShop = keeper->pIndexData->pShop) == NULL) return 0; if (fBuy) { cost = obj->cost * pShop->profit_buy / 100; } else { OBJ_DATA *obj2; int itype; cost = 0; for (itype = 0; itype < MAX_TRADE; itype++) { if (obj->item_type == pShop->buy_type[itype]) { cost = obj->cost * pShop->profit_sell / 100; break; } } if (!IS_OBJ_STAT (obj, ITEM_SELL_EXTRACT)) for (obj2 = keeper->carrying; obj2; obj2 = obj2->next_content) { if (obj->pIndexData == obj2->pIndexData && !str_cmp (obj->short_descr, obj2->short_descr)) { if (IS_OBJ_STAT (obj2, ITEM_INVENTORY)) cost /= 2; else cost = cost * 3 / 4; } } } if (obj->item_type == ITEM_STAFF || obj->item_type == ITEM_WAND) { if (obj->value[1] == 0) cost /= 4; else cost = cost * obj->value[2] / obj->value[1]; } return cost; } void do_buy (CHAR_DATA * ch, char *argument) { char buf[MAX_STRING_LENGTH]; int cost, roll; if (argument[0] == '\0') { send_to_char ("Buy what?\n\r", ch); return; } if (IS_SET (ch->in_room->room_flags, ROOM_PET_SHOP)) { char arg[MAX_INPUT_LENGTH]; char buf[MAX_STRING_LENGTH]; CHAR_DATA *pet; ROOM_INDEX_DATA *pRoomIndexNext; ROOM_INDEX_DATA *in_room; smash_tilde (argument); if (IS_NPC (ch)) return; argument = one_argument (argument, arg); pRoomIndexNext = get_room_index (ch->in_room->vnum + 1); if (pRoomIndexNext == NULL) { bug ("Do_buy: bad pet shop at vnum %d.", ch->in_room->vnum); send_to_char ("Sorry, you can't buy that here.\n\r", ch); return; } in_room = ch->in_room; ch->in_room = pRoomIndexNext; pet = get_char_room (ch, NULL, arg); ch->in_room = in_room; if (pet == NULL || !IS_SET (pet->act, ACT_PET)) { send_to_char ("Sorry, you can't buy that here.\n\r", ch); return; } if (ch->pet != NULL) { send_to_char ("You already own a pet.\n\r", ch); return; } cost = 10 * pet->level * pet->level; if ((ch->silver + 10 * ch->gold) < cost) { send_to_char ("You can't afford it.\n\r", ch); return; } if (ch->level < pet->level) { send_to_char ("You're not powerful enough to master this pet.\n\r", ch); return; } /* haggle */ roll = number_percent (); if (roll < get_skill (ch, gsn_dummy)) { cost -= cost / 2 * roll / 100; sprintf (buf, "You haggle the price down to %d coins.\n\r", cost); send_to_char (buf, ch); check_improve (ch, gsn_dummy, TRUE, 4); } deduct_cost (ch, cost); pet = create_mobile (pet->pIndexData); SET_BIT (pet->act, ACT_PET); STR_SET_BIT (pet->affected_by, AFF_CHARM); pet->comm = COMM_NOTELL | COMM_NOSHOUT | COMM_NOCHANNELS; argument = one_argument (argument, arg); if (arg[0] != '\0') { sprintf (buf, "%s %s", pet->name, arg); free_string (pet->name); pet->name = str_dup (buf); } sprintf (buf, "%sA neck tag says 'I belong to %s'.\n\r", pet->description, ch->name); free_string (pet->description); pet->description = str_dup (buf); char_to_room (pet, ch->in_room); add_follower (pet, ch); pet->leader = ch; ch->pet = pet; send_to_char ("Enjoy your pet.\n\r", ch); act ("$n bought $N as a pet.", ch, NULL, pet, TO_ROOM); return; } else { CHAR_DATA *keeper; OBJ_DATA *obj, *t_obj; //char arg[MAX_INPUT_LENGTH]; int number = 1, count = 1; int which = atoi(argument); if IS_NPC(ch) return; if ((keeper = find_keeper (ch)) == NULL) return; //number = mult_argument (argument, arg); //obj = get_obj_keeper (ch, keeper, arg); if (!(obj = get_obj_keeper_menu(ch, keeper, which))) { act ("$n tells you 'Hmm, sorry, I don't sell that. Check out my inventory 'list'. You can buy things by number.'", keeper, NULL, ch, TO_VICT); ch->pcdata->menu = NULL; //fixme: 5/18/2004 4:05PM potential memory leak return; } cost = get_cost (keeper, obj, TRUE); if (number < 1 || number > 99) { act ("$n tells you 'Get real!", keeper, NULL, ch, TO_VICT); return; } if (cost <= 0 || !can_see_obj (ch, obj)) { act ("$n tells you 'I don't sell that -- try 'list''.", keeper, NULL, ch, TO_VICT); ch->reply = keeper; return; } if (!IS_OBJ_STAT (obj, ITEM_INVENTORY)) { for (t_obj = obj->next_content; count < number && t_obj != NULL; t_obj = t_obj->next_content) { if (t_obj->pIndexData == obj->pIndexData && !str_cmp (t_obj->short_descr, obj->short_descr)) count++; else break; } if (count < number) { act ("$n tells you 'I don't have that many in stock.", keeper, NULL, ch, TO_VICT); ch->reply = keeper; return; } } if ((ch->silver + ch->gold * 10) < cost * number) { if (number > 1) act ("$n tells you 'You can't afford to buy that many.", keeper, obj, ch, TO_VICT); else act ("$n tells you 'You can't afford to buy $p'.", keeper, obj, ch, TO_VICT); ch->reply = keeper; return; } if (obj->level > ch->level) { act ("$n tells you 'You can't use $p yet'.", keeper, obj, ch, TO_VICT); ch->reply = keeper; return; } if (ch->carry_number + number * get_obj_number (obj) > can_carry_n (ch)) { send_to_char ("You can't carry that many items.\n\r", ch); return; } if (ch->carry_weight + number * get_obj_weight (obj) > can_carry_w (ch)) { send_to_char ("You can't carry that much weight.\n\r", ch); return; } /* haggle */ roll = number_percent (); if (!IS_OBJ_STAT (obj, ITEM_SELL_EXTRACT) && roll < get_skill (ch, gsn_dummy)) { cost -= obj->cost / 2 * roll / 100; act ("You haggle with $N.", ch, NULL, keeper, TO_CHAR); check_improve (ch, gsn_dummy, TRUE, 4); } if (number > 1) { sprintf (buf, "$n buys $p[%d].", number); act (buf, ch, obj, NULL, TO_ROOM); sprintf (buf, "You buy $p[%d] for %d silver.", number, cost * number); act (buf, ch, obj, NULL, TO_CHAR); } else { act ("$n buys $p.", ch, obj, NULL, TO_ROOM); sprintf (buf, "You buy $p for %d silver.", cost); act (buf, ch, obj, NULL, TO_CHAR); } deduct_cost (ch, cost * number); keeper->gold += cost * number / 100; keeper->silver += cost * number - (cost * number / 10) * 10; for (count = 0; count < number; count++) { if (IS_SET (obj->extra_flags, ITEM_INVENTORY)) t_obj = create_object (obj->pIndexData, obj->level); else { t_obj = obj; obj = obj->next_content; obj_from_char (t_obj); } if (t_obj->timer > 0 && !IS_OBJ_STAT (t_obj, ITEM_HAD_TIMER)) t_obj->timer = 0; REMOVE_BIT (t_obj->extra_flags, ITEM_HAD_TIMER); obj_to_char (t_obj, ch); if (cost < t_obj->cost) t_obj->cost = cost; } } } void do_list (CHAR_DATA * ch, char *argument) { char buf[MAX_STRING_LENGTH]; if (IS_SET (ch->in_room->room_flags, ROOM_PET_SHOP)) { ROOM_INDEX_DATA *pRoomIndexNext; CHAR_DATA *pet; bool found; pRoomIndexNext = get_room_index (ch->in_room->vnum + 1); if (pRoomIndexNext == NULL) { bug ("Do_list: bad pet shop at vnum %d.", ch->in_room->vnum); send_to_char ("You can't do that here.\n\r", ch); return; } found = FALSE; for (pet = pRoomIndexNext->people; pet; pet = pet->next_in_room) { if (IS_SET (pet->act, ACT_PET)) { if (!found) { found = TRUE; send_to_char ("Pets for sale:\n\r", ch); } sprintf (buf, "[%2d] %8d - %s\n\r", pet->level, 10 * pet->level * pet->level, pet->short_descr); send_to_char (buf, ch); } } if (!found) send_to_char ("Sorry, we're out of pets right now.\n\r", ch); return; } else { CHAR_DATA *keeper; OBJ_DATA *obj; int cost, count; int item_pos = 0; bool found; bool fMenu = FALSE; char arg[MAX_INPUT_LENGTH]; MENU_DATA *pMenu; //prototypes MENU_DATA *menu = NULL; OPTION_DATA *option; if ((keeper = find_keeper (ch)) == NULL) return; for (pMenu = menu_list; pMenu; pMenu = pMenu->next) { if (!str_cmp(pMenu->name, "HC00_do_list")) //Prototype do_list menu { fMenu = TRUE; break; } } if (!fMenu) { send_to_char("Uh-oh, no list menu prototype. No buying for you!\n\r", ch); return; } //act ("You can't bring $N into the city.", ch, NULL, fch, TO_CHAR); one_argument (argument, arg); found = FALSE; for (obj = keeper->carrying; obj; obj = obj->next_content) { if (obj->wear_loc == WEAR_NONE && can_see_obj (ch, obj) && (cost = get_cost (keeper, obj, TRUE)) > 0 && (arg[0] == '\0' || is_name (arg, obj->name))) { item_pos++; if (!found) { //if (!ch->pcdata->menu) menu = new_menu("do_list"); /*Ready up the menu*/ menu->fun_call = pMenu->fun_call; menu->fun_from = &do_list; sprintf(buf, "%s smiles and says, 'Here's what I'm selling:'\n\r", keeper->short_descr); menu->opening = str_dup (buf); sprintf(buf, "%s waves goodbye as you leave the store.'\n\r", keeper->short_descr); menu->closing = str_dup (buf); /*Give menu to player (later)*/ found = TRUE; act("$t", ch, menu->opening, keeper, TO_CHAR); } //Inventory list involving 'staff': //<20hp 100m 100mv> buy 10*1 //You buy a small sword for 30 silver. if (IS_OBJ_STAT (obj, ITEM_INVENTORY)) { sprintf(buf, "Lvl %d '{W%s{x', a %s, costs %d silver. (infinite)", obj->level, obj->short_descr, item_name (obj->item_type), cost); option = new_option(); //option->key = item_pos; option->args[0] = str_dup(buf); option->args[1] = str_dup(obj->name); option->fun_call = &do_buy; /*Add option to end of list*/ append_option(menu, option); //sprintf (buf, "[%2d %5d -- ] %s\n\r", // obj->level, cost, obj->short_descr); } else { count = 1; while (obj->next_content != NULL && obj->pIndexData == obj->next_content->pIndexData && !str_cmp (obj->short_descr, obj->next_content->short_descr)) { obj = obj->next_content; count++; } sprintf(buf, "Lvl %d '{W%s{x', a %s, costs %d silver. (%d left)", obj->level, obj->short_descr, item_name (obj->item_type), cost, count); option = new_option(); option->argi = item_pos; option->args[0] = str_dup(buf); option->args[1] = str_dup(obj->name); option->fun_call = &do_buy; /*Add option to end of list*/ append_option(menu, option); //option->next = menu->option; //menu->option = option; //sprintf (buf, "[%2d %5d %2d ] %s\n\r", // obj->level, cost, count, obj->short_descr); } //send_to_char (buf, ch); } } if (!found) send_to_char ("You can't buy anything here.\n\r", ch); else { /*Assign new menu (pop to top)*/ menu->next = ch->pcdata->menu; ch->pcdata->menu = menu; //wait_killmenu(ch, 10, menu); show_menu(ch, ch->pcdata->menu, FALSE); send_to_char("\n\rYou buy items by their item number (the first number listed)--e.g. 'buy 1'.\n\r", ch); } return; } } 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, roll; one_argument (argument, arg); if (arg[0] == '\0') { send_to_char ("Sell what?\n\r", ch); return; } if ((keeper = find_keeper (ch)) == NULL) return; if ((obj = get_obj_carry (ch, arg, ch)) == NULL) { act ("$n tells you 'You don't have that item'.", keeper, NULL, ch, TO_VICT); ch->reply = keeper; return; } if (!can_drop_obj (ch, obj)) { send_to_char ("You can't let go of it.\n\r", ch); return; } if (!can_see_obj (keeper, obj)) { act ("$n doesn't see what you are offering.", keeper, NULL, ch, TO_VICT); return; } if ((cost = get_cost (keeper, obj, FALSE)) <= 0) { act ("$n looks uninterested in $p.", keeper, obj, ch, TO_VICT); return; } if (cost > (keeper->silver + 10 * keeper->gold)) { act ("$n tells you 'I'm afraid I don't have enough wealth to buy $p.", keeper, obj, ch, TO_VICT); return; } act ("$n sells $p.", ch, obj, NULL, TO_ROOM); /* haggle */ roll = number_percent (); if (!IS_OBJ_STAT (obj, ITEM_SELL_EXTRACT) && roll < get_skill (ch, gsn_dummy)) { send_to_char ("You haggle with the shopkeeper.\n\r", ch); cost += obj->cost / 2 * roll / 10; cost = UMIN (cost, 95 * get_cost (keeper, obj, TRUE) / 10); cost = UMIN (cost, (keeper->silver + 10 * keeper->gold)); check_improve (ch, gsn_dummy, TRUE, 4); } sprintf (buf, "You sell $p for %d silver and %d gold piece%s.", cost - (cost / 10) * 10, cost / 10, cost == 1 ? "" : "s"); act (buf, ch, obj, NULL, TO_CHAR); ch->gold += cost / 10; ch->silver += cost - (cost / 10) * 10; deduct_cost (keeper, cost); if (keeper->gold < 0) keeper->gold = 0; if (keeper->silver < 0) keeper->silver = 0; if (obj->item_type == ITEM_TRASH || IS_OBJ_STAT (obj, ITEM_SELL_EXTRACT)) { extract_obj (obj); } else { obj_from_char (obj); if (obj->timer) SET_BIT (obj->extra_flags, ITEM_HAD_TIMER); else obj->timer = number_range (50, 100); obj_to_keeper (obj, keeper); } return; } void do_value (CHAR_DATA * ch, char *argument) { char buf[MAX_STRING_LENGTH]; char arg[MAX_INPUT_LENGTH]; CHAR_DATA *keeper; OBJ_DATA *obj; int cost; one_argument (argument, arg); if (arg[0] == '\0') { send_to_char ("Value what?\n\r", ch); return; } if ((keeper = find_keeper (ch)) == NULL) return; if ((obj = get_obj_carry (ch, arg, ch)) == NULL) { act ("$n tells you 'You don't have that item'.", keeper, NULL, ch, TO_VICT); ch->reply = keeper; return; } if (!can_see_obj (keeper, obj)) { act ("$n doesn't see what you are offering.", keeper, NULL, ch, TO_VICT); return; } if (!can_drop_obj (ch, obj)) { send_to_char ("You can't let go of it.\n\r", ch); return; } if ((cost = get_cost (keeper, obj, FALSE)) <= 0) { act ("$n looks uninterested in $p.", keeper, obj, ch, TO_VICT); return; } sprintf (buf, "$n tells you 'I'll give you %d silver and %d gold coins for $p'.", cost - (cost / 100) * 100, cost / 100); act (buf, keeper, obj, ch, TO_VICT); ch->reply = keeper; return; }