/************************************************************************ Realms of Aurealis James Rhone aka Vall of RoA shop.c Shopkeeps and such. Player comms to buy/sell/value/list and repair items interacting with specially flagged mobs. ******** 100% Completely Original Code ******** *** BE AWARE OF ALL RIGHTS AND RESERVATIONS *** ******** Heavily modified and expanded ******** All rights reserved henceforth. Please note that no guarantees are associated with any code from Realms of Aurealis. All code which has been released to the general public has been done so with an 'as is' pretense. RoA is based on both Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well as the RoA license. *** Read, Learn, Understand, Improve *** *************************************************************************/ #include "conf.h" #include "sysdep.h" #include "structures.h" #include "comm.h" #include "handler.h" #include "db.h" #include "interpreter.h" #include "acmd.h" #include "utils.h" #include "lists.h" #include "plshop.h" #include "magic.h" #include "global.h" // external vars extern struct str_app_type str_app[]; // external funcs BOOL wandering_shopkeep(chdata *ch); /*****************************************************/ /* New shop code, OLCable, generic shopkeeper stuff */ /* RoA, James Rhone */ /*****************************************************/ int same_type(chdata *keeper, obdata *ob); int check_reaction(chdata *ch, char *str); void perform_reaction(chdata *mob, char *str, chdata *ch, obdata *ob); void do_mob_random(chdata *mob); // minor bug: original sets not recursive thru containers.. will fix someday // updated to not originalize plshopkeep items... those are 1 time only sells // rather than repleneshing supplies of items 1/22/98 -jtrhone void set_original_objects(int zone) { chdata *mob; obdata *obj; if (zone == -1) /* reset all of them */ { for(mob = character_list; mob; mob = mob->next) if (SPC_FLAGGED(mob, SPC_SHOPKEEP) && !MOB_FLAGGED(mob, MOB_PLSHOPKEEP)) for (obj = mob->carrying; obj; obj=obj->next_content) { obj->original = TRUE; obj->touched = FALSE; } return; } /* must be a reset_roazone or unlock -save methinks */ if (REAL_ZONE(zone)) { for(mob = character_list; mob; mob = mob->next) if (SPC_FLAGGED(mob, SPC_SHOPKEEP) && !MOB_FLAGGED(mob, MOB_PLSHOPKEEP)) if (zone == GET_MOB_VNUM(mob) / 100) for (obj = mob->carrying; obj; obj=obj->next_content) { obj->original = TRUE; obj->touched = FALSE; } } } ACMD(do_list) { obdata *obj; chdata *keeper; struct shop_data_type *shp; plshop *s = NULL; int cost; char *argu = argument; if (INVALID_ROOM(ch->in_room)) return; for(keeper = world[ch->in_room].people; keeper ; keeper = keeper->next_in_room) if (SPC_FLAGGED(keeper, SPC_SHOPKEEP)) break; if (!keeper) /* no shopkeeper present */ { send_to_char("Nobody is selling anything here!\n\r",ch); return; } if (MOB_FLAGGED(keeper, MOB_PLSHOPKEEP)) if (!(s = get_shopkeeps_plshop(GET_MOB_VNUM(keeper)))) { sprintf(buf, "SYSERR: Unable to locate plshop for %s (#%d).", GET_NAME(keeper), GET_MOB_VNUM(keeper)); mudlog(buf, BRF, LEV_IMM, TRUE); send_to_char("Unable to locate plshopkeep's shop, notify immortal.\n\r",ch); return; } shp = keeper->npc_specials.shop_data; if (!shp) { sprintf(buf, "SYSERR: Undefined shopkeep: %s (#%d).", GET_NAME(keeper), GET_MOB_VNUM(keeper)); mudlog(buf, BRF, LEV_IMM, TRUE); send_to_char("This shopkeep is yet undefined, notify an immortal.\n\r",ch); return; } if (!IS_IMMORTAL(ch) && s && !PLS_FLAGGED(s, PLS_OPEN)) { send_to_char("This shop doesn't seem to be open...\n\r",ch); return; } /* only sell and what not if shopkeep is open and in shop */ if (!wandering_shopkeep(keeper)) if (!SHOPKEEP_OPEN(keeper) || keeper->in_room != real_room(shp->vshop)) { if (shp->vshop < 0) sprintf(buf, "I have retired!! Seek an immortal :)"); else sprintf(buf, "Find me at my shop around %d%s.", (!shp->open) ? 12 : ((shp->open > 12) ? shp->open - 12 : shp->open), shp->open > 12 ? "pm" : "am"); do_say(keeper, buf, 0, SPEAKING(keeper)); return; } if (!IS_IMMORTAL(ch) && s && !check_plshop_rcflags(ch, s)) { act("$N refuses to do business with your type.", TRUE, ch, 0, keeper, TO_CHAR); return; } skip_spaces(&argu); if (keeper->carrying) { if (MSTR1(keeper)) perform_reaction(keeper, MSTR1(keeper), ch, NULL); else send_to_char("These are the items for sale:\n\r",ch); for(*buf = '\0', obj = keeper->carrying; obj; obj = obj->next_content) { // in case they list a particular type... 2/5/98 -jtrhone if (*argu && !isname(argu, obj->name)) continue; cost = obj->cost; if (s) { if (PLS_FLAGGED(s, PLS_ITEMTAX) && s->item_tax) cost += (int) (obj->cost * (s->item_tax / 100.0)); if (s->keeper_cut) cost += (int) (obj->cost * (s->keeper_cut / 100.0)); if (s->markup) cost += (int) (obj->cost * (s->markup / 100.0)); } if (cost > 1) sprintf(buf2,"%s for %d %s.\n\r",obj->shdesc, cost, currency_name_plural); else sprintf(buf2,"%s for %d %s.\n\r",obj->shdesc, cost, currency_name); CCAP(buf2); if (strlen(buf) + strlen(buf2) < MAX_STRING_LENGTH - 10) strcat(buf, buf2); } if (*buf) page_string(ch->desc, buf, 1); } else act("$N has nothing to sell.",TRUE,ch,0,keeper,TO_CHAR); } ACMD(do_buy) { obdata *obj = NULL, *tmp = NULL; chdata *keeper = NULL; struct shop_data_type *shp = NULL; int i, quantity = 0; char *argu = argument; plshop *s = NULL; int pc_cost = 0, cost = 0, itax = 0, scut = 0, markup = 0; if (IS_NPC(ch) || !ch->desc) return; if (INVALID_ROOM(ch->in_room)) return; for(keeper = world[ch->in_room].people; keeper ; keeper = keeper->next_in_room) if (SPC_FLAGGED(keeper, SPC_SHOPKEEP)) break; if (!keeper || !keeper->carrying) /* no shopkeeper present */ { send_to_char("Nobody is selling anything here!\n\r",ch); return; } skip_spaces(&argu); if (!*argu) { send_to_char("What do you want to buy?\n\r",ch); return; } if (MOB_FLAGGED(keeper, MOB_PLSHOPKEEP)) if (!(s = get_shopkeeps_plshop(GET_MOB_VNUM(keeper)))) { sprintf(buf, "SYSERR: Unable to locate plshop for %s (#%d).", GET_NAME(keeper), GET_MOB_VNUM(keeper)); mudlog(buf, BRF, LEV_IMM, TRUE); send_to_char("Unable to locate plshopkeep's shop, notify immortal.\n\r",ch); return; } shp = keeper->npc_specials.shop_data; if (!shp) { send_to_char("This shopkeep is yet undefined, notify an immortal.\n\r",ch); return; } if (!IS_IMMORTAL(ch) && s && !PLS_FLAGGED(s, PLS_OPEN)) { send_to_char("This shop doesn't seem to be open...\n\r",ch); return; } /* only sell and what not if shopkeep is open and in shop */ if (!wandering_shopkeep(keeper)) if (!SHOPKEEP_OPEN(keeper) || keeper->in_room != real_room(shp->vshop)) { if (shp->vshop < 0) sprintf(buf, "I have retired!! Seek an immortal :)"); else sprintf(buf, "Find me at my shop around %d%s.", (!shp->open) ? 12 : ((shp->open > 12) ? shp->open - 12 : shp->open), shp->open > 12 ? "pm" : "am"); do_say(keeper, buf, 0, SPEAKING(keeper)); return; } if (!IS_IMMORTAL(ch) && s && !check_plshop_rcflags(ch, s)) { act("$N refuses to do business with your type.", TRUE, ch, 0, keeper, TO_CHAR); return; } half_chop(argu, arg, buf2); // check for quantity arg first if (is_number(arg)) { if (!(quantity = atoi(arg)) || !*buf2) { send_to_char("Buy what?\n\r",ch); return; } obj = get_obj_in_list_vis(keeper, buf2, keeper->carrying); } else { obj = get_obj_in_list_vis(keeper, arg, keeper->carrying); quantity = 1; } if (!obj) { if (MSTR3(keeper)) perform_reaction(keeper, MSTR3(keeper), ch, NULL); else act("$N does not have that item.",TRUE,ch,0,keeper,TO_CHAR); return; } // cant buy multiple unoriginal shopkeep items if (!obj->original) quantity = 1; // ok, now calculate cost.... // updated for plshops 1/23/98 -jtrhone pc_cost = cost = obj->cost*quantity; if (s) { if (PLS_FLAGGED(s, PLS_ITEMTAX) && s->item_tax) { itax = (int) (cost * (s->item_tax / 100.0)); pc_cost += itax; } if (s->keeper_cut) { scut = (int) (cost * (s->keeper_cut / 100.0)); pc_cost += scut; } if (s->markup) { markup = (int) (cost * (s->markup / 100.0)); pc_cost += markup; } } if (GET_GOLD(ch) < pc_cost) { sprintf(buf, "You don't have enough %s!\n\r", currency_name_plural); S2C(); return; } if ((IS_CARRYING_W(ch) + (obj->weight * quantity)) > CAN_CARRY_W(ch)) { sprintf(buf, "%s : You can't carry that much weight.\n\r", fname(obj->name)); S2C(); return; } if (obj->min_level > GET_LEVEL(ch)) { if (MSTR5(keeper)) perform_reaction(keeper, MSTR5(keeper), ch, obj); else act("$p is too powerful for you.",FALSE,ch,obj,keeper,TO_CHAR); return; } /* GENERIC 50% markoff on items that keepers will buy */ if (quantity > 1) { if (pc_cost > 1) sprintf(buf,"$N sells you [%%B%%6%d%%0] $p for %d %s.", quantity, pc_cost, currency_name_plural); else sprintf(buf,"$N sells you [%%B%%6%d%%0] $p for %d %s.", quantity, pc_cost, currency_name); } else { if (pc_cost > 1) sprintf(buf,"$N sells you $p for %d %s.", pc_cost, currency_name_plural); else sprintf(buf,"$N sells you $p for %d %s.", pc_cost, currency_name); } act(buf, FALSE, ch, obj, keeper, TO_CHAR); GET_GOLD(ch) -= pc_cost; // if there is a plshop, calc stats.. if (s) { s->gross_income += pc_cost; s->net_income += cost + markup; // if they marked down, (-markup) lose net gain s->gold_today += cost + markup; // even though, really, they don't 3/15/98 s->gold += cost + markup; s->item_tax_paid += itax; s->keeper_paid += scut; } if (MSTR4(keeper)) perform_reaction(keeper, MSTR4(keeper), ch, obj); if (!obj->original) /* if not original item give that item to ch */ { obj_from_char(obj); obj_to_char(obj,ch); } else /* if original shopkeeper object simply clone and give it to ch*/ if (obj->item_number > 0) { for (i = 0; i < quantity; i++) { tmp = read_object(obj->item_number, REAL); obj_to_char(tmp,ch); } } else { sprintf(buf, "SYSERR: Shopkeep #%d trying to sell non-virtual object.", GET_MOB_VNUM(keeper)); mudlog(buf, BRF, LEV_IMM, FALSE); } } // TODO: shopkeep should offer based on item condition... ACMD(do_sell) { int room; char *argu = argument; obdata *obj; chdata *keeper; struct shop_data_type *shp; if (IS_NPC(ch) || !ch->desc) return; room = ch->in_room; if (!room) return; for(keeper = world[room].people; keeper ; keeper = keeper->next_in_room) if (SPC_FLAGGED(keeper, SPC_SHOPKEEP)) break; if (!keeper) /* no shopkeeper present */ { send_to_char("Nobody is selling anything here!\n\r",ch); return; } skip_spaces(&argu); if (!*argu) { send_to_char("What do you want to sell?\n\r",ch); return; } shp = keeper->npc_specials.shop_data; if (!shp) { send_to_char("This shopkeep is yet undefined, notify an immortal.\n\r",ch); return; } /* only sell and what not if shopkeep is open and in shop */ if (!wandering_shopkeep(keeper)) if (!SHOPKEEP_OPEN(keeper) || keeper->in_room != real_room(shp->vshop)) { if (shp->vshop < 0) sprintf(buf, "I have retired!! Seek an immortal :)"); else sprintf(buf, "Find me at my shop around %d%s.", (!shp->open) ? 12 : ((shp->open > 12) ? shp->open - 12 : shp->open), shp->open > 12 ? "pm" : "am"); do_say(keeper, buf, 0, SPEAKING(keeper)); return; } one_argument(argu, arg); if (!( obj = get_obj_in_list_vis(ch, arg, ch->carrying))) { sprintf(buf, "You don't have %s %s!\n\r", AN(arg), arg); S2C(); return; } if (!obj->cost || !same_type(keeper, obj) || SPC_FLAGGED(keeper, SPC_NOBUY) || !OBJ_HITS(obj) || MOB_FLAGGED(keeper, MOB_PLSHOPKEEP)) { if (MSTR2(keeper)) perform_reaction(keeper, MSTR2(keeper), ch, obj); else act("$N does not want $p.",TRUE,ch, obj, keeper, TO_CHAR); return; } /* GENERIC 50% markoff on items that keepers will buy */ if (obj->cost/2 > 1) sprintf(buf,"$N pays you %d %s for $p.", obj->cost / 2, currency_name_plural); else sprintf(buf,"$N pays you %d %s for $p.", obj->cost / 2, currency_name); act(buf, FALSE, ch, obj, keeper, TO_CHAR); if (MSTR4(keeper)) perform_reaction(keeper, MSTR4(keeper), ch, obj); GET_GOLD(ch) += (obj->cost / 2); obj_from_char(obj); obj->touched = FALSE; obj->original = FALSE; /* this item wasn't on shopkeeper originally */ obj_to_char(obj,keeper); } // TODO: shopkeep should offer based on item condition... ACMD(do_value) { int room; char *argu = argument; obdata *obj; chdata *keeper; if (IS_NPC(ch) || !ch->desc) return; room = ch->in_room; if (!room) return; for(keeper = world[room].people; keeper ; keeper = keeper->next_in_room) if (SPC_FLAGGED(keeper, SPC_SHOPKEEP)) break; if (!keeper) /* no shopkeeper present */ { send_to_char("There is no shopkeeper here!\n\r",ch); return; } skip_spaces(&argu); if (!*argu) { send_to_char("What do you want to have appraised?\n\r",ch); return; } one_argument(argu, arg); if (!( obj = get_obj_in_list_vis(ch, arg, ch->carrying))) { send_to_char("You don't have that item!\n\r",ch); return; } if (!obj->cost || !same_type(keeper, obj) || SPC_FLAGGED(keeper, SPC_NOBUY) || !OBJ_HITS(obj) || MOB_FLAGGED(keeper, MOB_PLSHOPKEEP)) { if (MSTR2(keeper)) perform_reaction(keeper,MSTR2(keeper), ch, obj); else act("$N doesn't want $p.", FALSE, ch, obj, keeper, TO_CHAR); return; } /* GENERIC 50% markoff on items that keepers will buy */ if (obj->cost/2 > 1) sprintf(buf,"$N will pay you %d %s for $p.", obj->cost / 2, currency_name_plural); else sprintf(buf,"$N will pay you %d %s for $p.", obj->cost / 2, currency_name); act(buf, FALSE, ch, obj, keeper, TO_CHAR); } // let players identify an object before they buy for 10% of its value // 3/18/98 -jtrhone ACMD(do_listid) { obdata *obj = NULL; chdata *keeper = NULL; struct shop_data_type *shp = NULL; plshop *s = NULL; char *argu = argument; int pc_cost = 0, cost = 0, itax = 0, scut = 0, markup = 0; if (IS_NPC(ch) || !ch->desc) return; if (INVALID_ROOM(ch->in_room)) return; for(keeper = world[ch->in_room].people; keeper ; keeper = keeper->next_in_room) if (SPC_FLAGGED(keeper, SPC_SHOPKEEP)) break; if (!keeper || !keeper->carrying) /* no shopkeeper present */ { send_to_char("Nobody is selling anything here!\n\r",ch); return; } skip_spaces(&argu); if (!*argu) { send_to_char("What do you want to identify?\n\r",ch); return; } if (MOB_FLAGGED(keeper, MOB_PLSHOPKEEP)) if (!(s = get_shopkeeps_plshop(GET_MOB_VNUM(keeper)))) { sprintf(buf, "SYSERR: Unable to locate plshop for %s (#%d).", GET_NAME(keeper), GET_MOB_VNUM(keeper)); mudlog(buf, BRF, LEV_IMM, TRUE); send_to_char("Unable to locate plshopkeep's shop, notify immortal.\n\r",ch); return; } shp = keeper->npc_specials.shop_data; if (!shp) { send_to_char("This shopkeep is yet undefined, notify an immortal.\n\r",ch); return; } if (!IS_IMMORTAL(ch) && s && !PLS_FLAGGED(s, PLS_OPEN)) { send_to_char("This shop doesn't seem to be open...\n\r",ch); return; } /* only sell and what not if shopkeep is open and in shop */ if (!wandering_shopkeep(keeper)) if (!SHOPKEEP_OPEN(keeper) || keeper->in_room != real_room(shp->vshop)) { if (shp->vshop < 0) sprintf(buf, "I have retired!! Seek an immortal :)"); else sprintf(buf, "Find me at my shop around %d%s.", (!shp->open) ? 12 : ((shp->open > 12) ? shp->open - 12 : shp->open), shp->open > 12 ? "pm" : "am"); do_say(keeper, buf, 0, SPEAKING(keeper)); return; } if (!IS_IMMORTAL(ch) && s && !check_plshop_rcflags(ch, s)) { act("$N refuses to do business with your type.", TRUE, ch, 0, keeper, TO_CHAR); return; } obj = get_obj_in_list_vis(keeper, argu, keeper->carrying); if (!obj) { if (MSTR3(keeper)) perform_reaction(keeper, MSTR3(keeper), ch, NULL); else act("$N does not have that item.",TRUE,ch,0,keeper,TO_CHAR); return; } // ok, now calculate cost.... // updated for plshops 1/23/98 -jtrhone // only charge 10% to id the item // knock everything down by 10%, not just pc_cost, moneybugfix 8/3/98 -jtrhone pc_cost = cost = MAX(1, (obj->cost / 10)); if (s) { if (PLS_FLAGGED(s, PLS_ITEMTAX) && s->item_tax) { itax = (int) (cost * (s->item_tax / 100.0)); pc_cost += itax; } if (s->keeper_cut) { scut = (int) (cost * (s->keeper_cut / 100.0)); pc_cost += scut; } if (s->markup) { markup = (int) (cost * (s->markup / 100.0)); pc_cost += markup; } } if (GET_GOLD(ch) < pc_cost) { sprintf(buf, "You don't have enough %s!\n\r", currency_name_plural); S2C(); return; } if (pc_cost > 1) sprintf(buf,"$N describes $p to you for %d %s.", pc_cost, currency_name_plural); else sprintf(buf,"$N describes $p to you for %d %s.", pc_cost, currency_name); act(buf, FALSE, ch, obj, keeper, TO_CHAR); GET_GOLD(ch) -= pc_cost; // if there is a plshop, calc stats.. if (s) { s->gross_income += pc_cost; s->net_income += cost + markup; // if they marked down, (-markup) lose net gain s->gold_today += cost + markup; // even though, really, they don't 3/15/98 s->gold += cost + markup; s->item_tax_paid += itax; s->keeper_paid += scut; } if (MSTR4(keeper)) perform_reaction(keeper, MSTR4(keeper), ch, obj); // have them cast the spell (*spell_info[SPELL_IDENTIFY].spell_pointer) (ch, "", NULL, obj); } int same_type(chdata *keeper, obdata *ob) { int type = ob->type_flag; obdata *obj; for(obj = keeper->carrying; obj; obj = obj->next_content) if (obj->type_flag == type && !obj->touched) return TRUE; return FALSE; } /* thats it folks, eliminated a ton of code for slightly less features */ /* however, we can now edit shopkeepers online, something IMPOSSIBLE */ /* or at least a giant pain in the ass to do with the old code */ /* James Rhone aka Vall @ RoA */ /* shopkeep like code for mobs who can REPAIR objects */ // cost gets an estimate from the fixer on how much itll cost to fix... ACMD(do_cost) { int room, charge; char *argu = argument; obdata *obj; chdata *keeper; if (IS_NPC(ch) || !ch->desc) return; room = ch->in_room; if (!room) return; for(keeper = world[room].people; keeper ; keeper = keeper->next_in_room) if (SPC_FLAGGED(keeper, SPC_FIXER)) break; if (!keeper) /* no shopkeeper present */ { send_to_char("Nobody here can repair items.\n\r",ch); return; } skip_spaces(&argu); if (!*argu) { send_to_char("What do you want to have appraised?\n\r",ch); return; } one_argument(argu, arg); if (!( obj = get_obj_in_list_vis(ch, arg, ch->carrying))) { do_say(keeper,"You don't have that item!\n\r",0, SPEAKING(keeper)); return; } if (!same_type(keeper, obj)) { do_say(keeper, "I don't know how to fix that!\n\r",0, SPEAKING(keeper)); return; } if (MAX_OBJ_HITS(obj) == -1 || OBJ_HITS(obj) >= MAX_OBJ_HITS(obj)) { do_say(keeper,"That doesn't need repair!",0, SPEAKING(keeper)); return; } if (OBJ_HITS(obj) <= 0) { do_say(keeper,"That's beyond repair!",0, SPEAKING(keeper)); return; } charge = (MAX_OBJ_HITS(obj) - OBJ_HITS(obj)) * 10; /* why not? :) */ if (charge > 1) sprintf(buf,"That'll cost you %d %s to repair.", charge, currency_name_plural); else sprintf(buf,"That'll cost you %d %s to repair.", charge, currency_name); do_say(keeper,buf,0, SPEAKING(keeper)); } // let the shopkeep fix the item... ACMD(do_repair) { int room, charge; char *argu = argument; obdata *obj; chdata *keeper; if (IS_NPC(ch) || !ch->desc) return; room = ch->in_room; if (!room) return; for(keeper = world[room].people; keeper ; keeper = keeper->next_in_room) if (SPC_FLAGGED(keeper, SPC_FIXER)) break; if (!keeper) /* no shopkeeper present */ { send_to_char("Nobody here can repair items.\n\r",ch); return; } skip_spaces(&argu); if (!*argu) { send_to_char("What do you want to have repaired?\n\r",ch); return; } one_argument(argu, arg); if (!( obj = get_obj_in_list_vis(ch, arg, ch->carrying))) { do_say(keeper,"You don't have that item!\n\r",0, SPEAKING(keeper)); return; } if (!same_type(keeper, obj)) { do_say(keeper,"I don't know how to fix that!\n\r",0, SPEAKING(keeper)); return; } if (MAX_OBJ_HITS(obj) == -1 || OBJ_HITS(obj) >= MAX_OBJ_HITS(obj)) { do_say(keeper,"That doesn't need repair!",0, SPEAKING(keeper)); return; } if (OBJ_HITS(obj) <= 0) { do_say(keeper,"That's beyond repair!",0, SPEAKING(keeper)); return; } charge = (MAX_OBJ_HITS(obj) - OBJ_HITS(obj)) * 10; if (charge > 1) sprintf(buf, "That'll cost you %d %s to repair.",charge, currency_name_plural); else sprintf(buf, "That'll cost you %d %s to repair.",charge, currency_name); do_say(keeper, buf, 0, SPEAKING(keeper)); if (GET_GOLD(ch) < charge) { sprintf(buf, "You don't have enough %s!\n\r", currency_name_plural); S2C(); return; } GET_GOLD(ch) -= charge; act("$N quickly repairs $p for $n.", FALSE, ch, obj, keeper, TO_ROOM); do_say(keeper, "There you go!", 0, SPEAKING(keeper)); OBJ_HITS(obj) = MAX_OBJ_HITS(obj); }