/************************************************************************ Realms of Aurealis James Rhone aka Vall of RoA plshop.c Code related to player owned shops as well as all the RoAOLC interfaces related to Online Player Shop Creation (OLPSC). ******** 100% Completely Original Code ******** *** BE AWARE OF ALL RIGHTS AND RESERVATIONS *** ******** 100% Completely Original Code ******** 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 "utils.h" #include "comm.h" #include "handler.h" #include "acmd.h" #include "db.h" #include "mudlimits.h" #include "interpreter.h" #include "roaolc.h" #include "lists.h" #include "plshop.h" #include "global.h" #include "objsave.h" // external functions... extern char *get_name_by_id(int id); extern void perform_give(chdata *ch, chdata *vict, obdata *obj, BOOL calc_weight); // external variables extern dsdata *descriptor_list; extern char *rcbits[]; // internal functions ROA_MENU(plsedit_top_menu); ROA_MENU(plsedit_confirm_quit); ROA_MENU(plsedit_confirm_save); ROA_MENU(plsedit_confirm_delete); // internal variables // for the types of plshops... not many yet char *plshop_types[] = { "NORMAL", "PUBLIC", "\n" }; char *plshop_bits[] = { "PLS_OPEN", "PLS_LANDTAX", "PLS_ITEMTAX", "PLS_LANDTAX_OWED", "\n" }; // global variables shlist topshops[TOP_PLSHOPS]; // return a ptr to the plshop with this ID plshop *get_plshop_by_id(int id) { plshop *ptr; for (ptr = pls_global; ptr; ptr=ptr->global_next) if (ptr->plshop_id == id) return ptr; return NULL; } BOOL owns_plshop(plshop *ptr, chdata *ch) { return (ptr->owner_idnum == GET_IDNUM(ch)); } // given a plshop ptr, insert it on global plshop list void insert_plshop_to_world(plshop *ptr) { ptr->global_next = pls_global; pls_global = ptr; } // given a plshop ptr, insert it on global plshop list // note: this does not FREE the ptr void remove_plshop_from_world(plshop *ptr) { plshop *temp; REMOVE_FROM_LIST(ptr, pls_global, global_next); } // given a plshop ptr, insert it on chars plshop list void insert_plshop_to_char(plshop *ptr, chdata *ch) { ptr->next = PLSHOPS(ch); PLSHOPS(ch) = ptr; ptr->owner = ch; } // given a plshop ptr, insert it on chars plshop list void remove_plshop_from_char(plshop *ptr, chdata *ch) { plshop *temp; REMOVE_FROM_LIST(ptr, PLSHOPS(ch), next); ptr->owner = NULL; } // remove every plshop from char's LL of plshops void remove_char_plshops(chdata *ch) { if (IS_NPC(ch)) return; while (PLSHOPS(ch)) remove_plshop_from_char(PLSHOPS(ch), ch); } // when a player logs in, scan global LL of plshops and link them // into players LL of plshops... void load_plshops(chdata *ch) { plshop *ptr; int rroom; if (IS_NPC(ch)) return; // just in case they have a list of em already... remove_char_plshops(ch); for (ptr = pls_global; ptr; ptr=ptr->global_next) if (owns_plshop(ptr, ch)) { // first time send them header if (!PLSHOPS(ch)) { send_to_char("\n\r%B%6Current Shop Accounts:%0\n\r",ch); send_to_char("%B%6-=-=-=-=-==-=-=-=-=-=-%0\n\r",ch); } insert_plshop_to_char(ptr, ch); if ((rroom = real_room(ptr->shop_rvnum)) > 0 && world[rroom].name) strcpy(buf2, world[rroom].name); else strcpy(buf2, "<Unknown>"); sprintf(buf, "%%6Plshop #%%0%d (%s): %%B%%6%d%%0 %s.\n\r", ptr->plshop_id, buf2, ptr->gold, currency_name_plural); S2C(); } } // when a new plshop is created, we must be able to relink // the players list of shops so they can access it... ACMD(do_relink_plshops) { char *argu = argument; chdata *vict; if (IS_NPC(ch)) return; skip_spaces(&argu); if (!*argu) { send_to_char("Usage: pllink <PC name>.\n\r",ch); return; } if (!(vict = get_char_vis(ch, argu))) { send_to_char("Victim not found.\n\r",ch); return; } if (IS_NPC(vict)) { send_to_char("Usage: pllink <PC name>.\n\r",ch); return; } // the load function will unlink current list and relink load_plshops(vict); send_to_char("Ok.\n\r",ch); send_to_char("%BPlshops relinked...%0\n\r",vict); } // given a file ptr and a ptr to allocated plrshop stucture, load from disk void load_single_shop(FILE *fp, plshop *ptr) { fscanf(fp, "%d %d %d %d\n", &ptr->owner_idnum, &ptr->shopkeep_vnum, &ptr->shop_rvnum, &ptr->gold); fscanf(fp, "%d %d %d\n", &ptr->type, &ptr->bitv, &ptr->rc_bitv); fscanf(fp, "%d %d %d\n", &ptr->minlevel, &ptr->maxlevel, &ptr->markup); fscanf(fp, "%d %d %d %d\n", &ptr->max_sell, &ptr->item_tax, &ptr->land_tax, &ptr->keeper_cut); fscanf(fp, "%d %d %d %d %d\n", &ptr->days_open, &ptr->averages[0], &ptr->averages[1], &ptr->averages[2], &ptr->averages[3]); fscanf(fp, "%d %d %d %d %d\n", &ptr->item_tax_paid, &ptr->land_tax_paid, &ptr->keeper_paid, &ptr->gross_income, &ptr->net_income); ptr->gold_today = 0; ptr->next = NULL; // set character next to NULL } // read in each shop, inserting it into global linked list void boot_plshops(void) { FILE *fp; struct stat s; plshop *ptr; int i, cnt; char fname[MAX_INPUT_LENGTH]; for (i = 0, cnt = 0; i < MAX_PLSHOPS; i++) { sprintf(fname, "%s/%d.plshop", PLSHOP_DIR, i); if (stat(fname, &s) < 0) continue; if (!(fp = fopen(fname, "rt"))) continue; CREATE(ptr, plshop, 1); ptr->plshop_id = i; load_single_shop(fp, ptr); fclose(fp); // insert into global list ptr->global_next = pls_global; pls_global = ptr; cnt++; } sprintf(buf, " %d Player Shops loaded", cnt); log(buf); } // given a file pointer and ptr to the shop to save BOOL save_single_shop(FILE *fp, plshop *ptr) { fprintf(fp, "%d %d %d %d\n", ptr->owner_idnum, ptr->shopkeep_vnum, ptr->shop_rvnum, ptr->gold); fprintf(fp, "%d %d %d\n", ptr->type, ptr->bitv, ptr->rc_bitv); fprintf(fp, "%d %d %d\n", ptr->minlevel, ptr->maxlevel, ptr->markup); fprintf(fp, "%d %d %d %d\n", ptr->max_sell, ptr->item_tax, ptr->land_tax, ptr->keeper_cut); fprintf(fp, "%d %d %d %d %d\n", ptr->days_open, ptr->averages[0], ptr->averages[1], ptr->averages[2], ptr->averages[3]); fprintf(fp, "%d %d %d %d %d\n", ptr->item_tax_paid, ptr->land_tax_paid, ptr->keeper_paid, ptr->gross_income, ptr->net_income); return TRUE; } void save_plshops(void) { FILE *fp; plshop *ptr; int cnt = 0; char fname[MAX_INPUT_LENGTH]; chdata *mob; for (ptr = pls_global; ptr; ptr=ptr->global_next) { sprintf(fname, "%s/%d.plshop", PLSHOP_DIR, ptr->plshop_id); if (!(fp = fopen(fname, "wt"))) continue; save_single_shop(fp, ptr); cnt++; fclose(fp); } sprintf(buf, "SYSUPD: %d plshops saved.", cnt); mudlog(buf, BUG, LEV_IMM, TRUE); // now save all the plshopkeeps... for (mob = character_list; mob; mob=mob->next) if (MOB_FLAGGED(mob, MOB_PLSHOPKEEP)) save_plshopkeep_objs(mob); } // get the next available plshop_id based on what's in the // current global list... return -1 if none available int get_available_plshop_id(void) { int i; for (i = 0; i < MAX_PLSHOPS; i++) if (!get_plshop_by_id(i)) return i; return -1; } BOOL plshop_being_editted(plshop *ptr) { dsdata *d = descriptor_list; for ( ; d; d=d->next) if (D_CHECK(d) && (PLS_EDITTED(d->character) == ptr)) return TRUE; return FALSE; } //================================================================== // User interface and misc dealings with Immortals and players... //================================================================== BOOL check_plshop_rcflags(chdata *ch, plshop *qptr) { if (PLS_RCFLAGGED(qptr, PLS_NO_WARRIOR) && IS_NAT_WARRIOR(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_CLERIC) && IS_NAT_CLERIC(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_MAGE) && IS_NAT_MAGE(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_THIEF) && IS_NAT_THIEF(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_SHAMAN) && IS_NAT_SHAMAN(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_RANGER) && IS_NAT_RANGER(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_WARLOCK) && IS_NAT_WARLOCK(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_BARD) && IS_NAT_BARD(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_MONK) && IS_NAT_MONK(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_MADEPT) && IS_NAT_MADEPT(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_DRUID) && IS_NAT_DRUID(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_HUMAN) && IS_HUMAN(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_ELF) && IS_ELF(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_HALF_ELF) && IS_HALF_ELF(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_ORC) && IS_ORC(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_OGRE) && IS_OGRE(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_DROW) && IS_DROW(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_DWARF) && IS_DWARF(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_PIXIE) && IS_PIXIE(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_NIXIE) && IS_NIXIE(ch)) return FALSE; if (PLS_RCFLAGGED(qptr, PLS_NO_DRAGON) && IS_DRAGON(ch)) return FALSE; return TRUE; } // show an immortal the global plshop list.. currently with very limited // detail other than the plshop's ID and the owners ID and name ACMD(do_plshoplist) { char *p; plshop *ptr; int i; if (IS_NPC(ch)) return; strcpy(buf, "%B%6Current Player Owned Shops:%0\n\r" "%B%6-=-=-=-=-=-=-=-=-=-=-=-=-=-%0\n\r" "%B%5ID# Owner (Name)%0\n\r" "%B%6-=-=-=-=-=-=-=-=-=-=-=-=-=-%0\n\r"); for (i = 0; i < MAX_PLSHOPS; i++) if ((ptr = get_plshop_by_id(i))) { p = get_name_by_id(ptr->owner_idnum); sprintf(buf2, "%s", p ? p : "<UNKNOWN>"); sprintf(buf+strlen(buf),"%-3d - %5d (%s)\n\r",i,ptr->owner_idnum,buf2); } page_string(ch->desc, buf, 1); } // show stats of a particular shop ACMD(do_plshopstat) { char *argu = argument, *p; int id; plshop *s; if (IS_NPC(ch)) return; skip_spaces(&argu); if (!*argu || !is_number(argu)) { send_to_char("Usage: plshopstat <plshop ID>.\n\r",ch); return; } id = atoi(argu); if (!(s = get_plshop_by_id(id))) { send_to_char("Unable to locate a plshop with that ID.\n\r",ch); return; } // ok, we have ptr to the shop...send stats sprintf(buf, "%%B%%6Player Shop Stats for ShopID#%%0: %d\n\r", s->plshop_id); S2C(); p = get_name_by_id(s->owner_idnum); sprintf(buf2, "%s", p ? p : "<UNKNOWN>"); sprintf(buf, "%%6OwnerID#%%0: %d (%s)\n\r", s->owner_idnum, buf2); S2C(); sprintf(buf, "%%6Shopkeep Vnum%%0: %d\n\r", s->shopkeep_vnum); S2C(); sprintf(buf, "%%6Shop Room Vnum%%0: %d\n\r", s->shop_rvnum); S2C(); sprinttype(PLS_TYPE(s), plshop_types, buf2); sprintf(buf, "%%6Shop Type%%0: %%5%s%%0\n\r", buf2); S2C(); sprintbit(PLS_FLAGS(s), plshop_bits, buf2); sprintf(buf, "%%6Shop Flags%%0: %%5%s%%0\n\r", buf2); S2C(); sprintbit(PLS_RCFLAGS(s), rcbits, buf2); sprintf(buf, "%%6Shop RC Flags%%0: %%5%s%%0\n\r", buf2); S2C(); sprintf(buf, "%%6Shop Minlevel | MaxLevel%%0: %d | %d\n\r", s->minlevel, s->maxlevel); S2C(); sprintf(buf, "%%6Max Simultaneous Items to Sell%%0: %d\n\r", s->max_sell); S2C(); if (PLS_FLAGGED(s, PLS_ITEMTAX)) { sprintf(buf, "%%6Item Tax (percentage of item gross)%%0: %d\n\r", s->item_tax); S2C(); } if (PLS_FLAGGED(s, PLS_LANDTAX)) { sprintf(buf, "%%6Property Tax (monthly charge)%%0: %d\n\r", s->land_tax); S2C(); } sprintf(buf, "%%6Shopkeep's Cut (percentage of item gross)%%0: %d\n\r", s->keeper_cut); S2C(); sprintf(buf, "%%6Owner's Markup (percentage of item gross)%%0: %d\n\r", s->markup); S2C(); sprintf(buf, "%%6Days Open%%0: %d\n\r", s->days_open); S2C(); sprintf(buf, "%%6Account%%0: %d\n\r", s->gold); S2C(); sprintf(buf, "%%6Amount Sold Today%%0: %d\n\r", s->gold_today); S2C(); sprintf(buf, "%%6Total Gross Income | Total Net Income%%0: %d | %d\n\r", s->gross_income, s->net_income); S2C(); sprintf(buf, "%%6Total Keeper Cut%%0: %d\n\r", s->keeper_paid); S2C(); sprintf(buf, "%%6Total Item Taxes Paid%%0: %d\n\r", s->item_tax_paid); S2C(); sprintf(buf, "%%6Total Property Taxes Paid%%0: %d\n\r", s->land_tax_paid); S2C(); sprintf(buf, "%%6Sale Averages Per Day | Week | Month | Year%%0: %d | %d | %d | %d\n\r", PLS_PERDAY(s),PLS_PERWEEK(s),PLS_PERMONTH(s),PLS_PERYEAR(s)); S2C(); } // for qsort int sort_shops(const void *first, const void *second) { shlist *one, *two; one = (shlist *) first; two = (shlist *) second; if (one->gross_income < two->gross_income) return 1; else if (one->gross_income > two->gross_income) return -1; else return 0; } // wipe the topshop array void clear_topshops(void) { memset( (char *)&topshops, 0, sizeof(topshops)); } // return the index of the lowest value in the topshop array int get_lowest_in_topshops(void) { int lowindex = 0; int i; for (i=0; i < TOP_PLSHOPS; i++) if (topshops[i].gross_income <= topshops[lowindex].gross_income) lowindex = i; return lowindex; } // go thru linked list, fill in topshop array void fill_topshops(void) { plshop *ptr; int low; for (ptr = pls_global; ptr; ptr=ptr->global_next) { low = get_lowest_in_topshops(); if (ptr->gross_income > topshops[low].gross_income) { topshops[low].shop_id = ptr->plshop_id; topshops[low].gross_income = ptr->gross_income; } } } // show immortal the top 20 shops in order based on most gross_income ACMD(do_topshops) { int i; plshop *ptr; char *p; BOOL found = FALSE; clear_topshops(); fill_topshops(); qsort((void *) topshops, TOP_PLSHOPS, sizeof(shlist), sort_shops); strcpy(buf, "%B%6Top Player Shops by Gross Income%0:\n\r"); strcat(buf, "%B%6-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-%0\n\r"); for(i = 0; i < TOP_PLSHOPS && topshops[i].gross_income > 0; i++) { ptr = get_plshop_by_id(topshops[i].shop_id); if (!ptr) break; found = TRUE; p = get_name_by_id(ptr->owner_idnum); sprintf(buf2, "%s", p ? p : "<UNKNOWN>"); sprintf(buf+strlen(buf),"%%5Shop ID#%%0:%-3d - %d %%5%s%%0 (%s)\n\r", ptr->plshop_id, ptr->gross_income, currency_name_plural, buf2); } if (!found) strcat(buf, "%B%1No qualifying shops...%0\n\r"); page_string(ch->desc, buf, 1); } // immortal ability to allocate new plshop, insert into list // and get thrown into plshop menu ACMD(do_plshopcreate) { plshop *ptr; int id; if (IS_NPC(ch)) return; id = get_available_plshop_id(); if (id < 0) { mudlog("SYSERR: Unable to get available plshop ID.", BRF, LEV_IMM, TRUE); return; } // ok allocate, assign, insert... CREATE(ptr, plshop, 1); ptr->plshop_id = id; insert_plshop_to_world(ptr); // now send them into the menu... PLS_EDITTED(ch) = ptr; SET_BIT(PLR_FLAGS(ch), PLR_BUILDING); MENU_DEPTH(ch) = 0; ch->pc_specials->field_changed = FALSE; menu_jump(ch, plsedit_top_menu); } ACMD(do_plshopedit) { char *argu = argument; int id; plshop *ptr; if (IS_NPC(ch)) return; skip_spaces(&argu); if (!*argu || !is_number(argu)) { send_to_char("Usage: plshopedit <plshop ID>.\n\r",ch); return; } id = atoi(argu); if (!(ptr = get_plshop_by_id(id))) { send_to_char("Unable to locate a plshop with that ID.\n\r",ch); return; } SET_BIT(PLR_FLAGS(ch), PLR_BUILDING); MENU_DEPTH(ch) = 0; ch->pc_specials->field_changed = FALSE; PLS_EDITTED(ch) = ptr; menu_jump(ch, plsedit_top_menu); } ACMD(do_plshopsave) { if (IS_NPC(ch)) return; save_plshops(); send_to_char("Ok.\n\r",ch); } ACMD(do_plshopdelete) { char *argu = argument; int id; plshop *ptr; if (IS_NPC(ch)) return; skip_spaces(&argu); if (!*argu || !is_number(argu)) { send_to_char("Usage: plshopdelete <plshop ID>.\n\r",ch); return; } id = atoi(argu); if (!(ptr = get_plshop_by_id(id))) { send_to_char("Unable to locate a plshop with that ID.\n\r",ch); return; } PLS_EDITTED(ch) = ptr; menu_jump(ch, plsedit_confirm_delete); } //================================================================== // Mortal interface to player shops... // Started 1/18/98... //================================================================== plshop *get_shopkeeps_plshop(int mvnum) { plshop *ptr; if (real_mobile(mvnum) < 0) return NULL; for (ptr = pls_global; ptr; ptr = ptr->global_next) if (ptr->shopkeep_vnum == mvnum) return ptr; return NULL; } // return ptr to shop if char standing in their own shop? plshop *in_own_shop(chdata *ch) { plshop *ptr; if (INVALID_ROOM(ch->in_room)) return NULL; for (ptr = PLSHOPS(ch); ptr; ptr = ptr->next) if (world[ch->in_room].number == ptr->shop_rvnum) return ptr; return NULL; } // deposit gumbas into this shop's account... ACMD(do_shopdeposit) { plshop *ptr; int amount; if (IS_NPC(ch)) return; if (!(ptr = in_own_shop(ch))) { send_to_char("You must be located in your shop to access its account.\n\r",ch); return; } one_argument(argument, arg); if (!arg || !*arg || !is_number(arg)) { send_to_char("Usage: shopdeposit < amount >.\n\r",ch); return; } amount = atoi(arg); if (amount <= 0) { send_to_char("Usage: shopdeposit < amount >.\n\r",ch); return; } if (amount > GET_GOLD(ch)) { sprintf(buf, "You don't have that many %s on hand.\n\r", currency_name_plural); S2C(); return; } ptr->gold += amount; GET_GOLD(ch) -= amount; sprintf(buf, "%%6Amount deposited%%0: %d\n\r%%6New Shop Balance%%0: %d\n\r", amount, ptr->gold); S2C(); } // withdraw gumbas into this shop's account... ACMD(do_shopwithdraw) { plshop *ptr; int amount; if (IS_NPC(ch)) return; if (!(ptr = in_own_shop(ch))) { send_to_char("You must be located in your shop to access its account.\n\r",ch); return; } one_argument(argument, arg); if (!arg || !*arg || !is_number(arg)) { send_to_char("Usage: shopwithdraw < amount >.\n\r",ch); return; } amount = atoi(arg); if (amount <= 0) { send_to_char("Usage: shopwithdraw < amount >.\n\r",ch); return; } if (amount > ptr->gold) { sprintf(buf, "You don't have that many %s in this shop's account.\n\r", currency_name_plural); S2C(); return; } ptr->gold -= amount; GET_GOLD(ch) += amount; sprintf(buf, "%%6Amount withdrawn%%0: %d\n\r%%6New Shop Balance%%0: %d\n\r", amount, ptr->gold); S2C(); } // let player open a shop ACMD(do_shopopen) { plshop *ptr; if (IS_NPC(ch)) return; if (!(ptr = in_own_shop(ch))) { send_to_char("You must be located in your shop to open it.\n\r",ch); return; } if (PLS_FLAGGED(ptr, PLS_OPEN)) { send_to_char("This shop is already open.\n\r",ch); return; } SET_BIT(PLS_FLAGS(ptr), PLS_OPEN); send_to_char("Ok. Shop now flagged OPEN.\n\r",ch); } // let player close a shop ACMD(do_shopclose) { plshop *ptr; if (IS_NPC(ch)) return; if (!(ptr = in_own_shop(ch))) { send_to_char("You must be located in your shop to close it.\n\r",ch); return; } if (!PLS_FLAGGED(ptr, PLS_OPEN)) { send_to_char("This shop is already closed.\n\r",ch); return; } REMOVE_BIT(PLS_FLAGS(ptr), PLS_OPEN); send_to_char("Ok. Shop now flagged CLOSED.\n\r",ch); } // account of all shop accounts for player ACMD(do_plshopbank) { plshop *ptr; int rroom; if (IS_NPC(ch)) return; for (ptr = PLSHOPS(ch); ptr; ptr=ptr->next) { // first time send them header if (ptr == PLSHOPS(ch)) { send_to_char("\n\r%B%6Current Shop Accounts:%0\n\r",ch); send_to_char("%B%6-=-=-=-=-==-=-=-=-=-=-%0\n\r",ch); } if ((rroom = real_room(ptr->shop_rvnum)) > 0) strcpy(buf2, world[rroom].name); else strcpy(buf2, "<Unknown>"); sprintf(buf, "%%6Plshop #%%0%d (%s): %%B%%6%d%%0 %s.\n\r", ptr->plshop_id, buf2, ptr->gold, currency_name_plural); S2C(); } } // let player edit some of the stuff on their shops... ACMD(do_mort_plshopedit) { plshop *s; if (IS_NPC(ch)) return; if (!(s = in_own_shop(ch))) { send_to_char("You must be located in your shop to edit it.\n\r",ch); return; } SET_BIT(PLR_FLAGS(ch), PLR_BUILDING); MENU_DEPTH(ch) = 0; ch->pc_specials->field_changed = FALSE; PLS_EDITTED(ch) = s; menu_jump(ch, plsedit_top_menu); } // let player get good stats on each plshop they own ACMD(do_shopstats) { plshop *s; char *p; if (IS_NPC(ch)) return; if (!(s = in_own_shop(ch))) { send_to_char("You must be located in your shop to get its stats.\n\r",ch); return; } // ok, we have ptr to the shop...send stats sprintf(buf, "%%B%%6Player Shop Stats for ShopID#%%0: %d\n\r", s->plshop_id); S2C(); p = get_name_by_id(s->owner_idnum); sprintf(buf2, "%s", p ? p : "<UNKNOWN>"); sprintf(buf, "%%6OwnerID#%%0: %d (%s)\n\r", s->owner_idnum, buf2); S2C(); sprinttype(PLS_TYPE(s), plshop_types, buf2); sprintf(buf, "%%6Shop Type%%0: %%5%s%%0\n\r", buf2); S2C(); sprintbit(PLS_FLAGS(s), plshop_bits, buf2); sprintf(buf, "%%6Shop Flags%%0: %%5%s%%0\n\r", buf2); S2C(); sprintbit(PLS_RCFLAGS(s), rcbits, buf2); sprintf(buf, "%%6Shop RC Flags%%0: %%5%s%%0\n\r", buf2); S2C(); sprintf(buf, "%%6Shop Minlevel | MaxLevel%%0: %d | %d\n\r", s->minlevel, s->maxlevel); S2C(); sprintf(buf, "%%6Max Simultaneous Items to Sell%%0: %d\n\r", s->max_sell); S2C(); if (PLS_FLAGGED(s, PLS_ITEMTAX)) { sprintf(buf, "%%6Item Tax (percentage of item gross)%%0: %d\n\r", s->item_tax); S2C(); } if (PLS_FLAGGED(s, PLS_LANDTAX)) { sprintf(buf, "%%6Property Tax (monthly charge)%%0: %d\n\r", s->land_tax); S2C(); } sprintf(buf, "%%6Shopkeep's Cut (percentage of item gross)%%0: %d\n\r", s->keeper_cut); S2C(); sprintf(buf, "%%6Owner's Markup (percentage of item gross)%%0: %d\n\r", s->markup); S2C(); sprintf(buf, "%%6Days Open%%0: %d\n\r", s->days_open); S2C(); sprintf(buf, "%%6Account%%0: %d\n\r", s->gold); S2C(); sprintf(buf, "%%6Amount Sold Today%%0: %d\n\r", s->gold_today); S2C(); sprintf(buf, "%%6Total Gross Income | Total Net Income%%0: %d | %d\n\r", s->gross_income, s->net_income); S2C(); sprintf(buf, "%%6Total Keeper Cut%%0: %d\n\r", s->keeper_paid); S2C(); sprintf(buf, "%%6Total Item Taxes Paid%%0: %d\n\r", s->item_tax_paid); S2C(); sprintf(buf, "%%6Total Property Taxes Paid%%0: %d\n\r", s->land_tax_paid); S2C(); sprintf(buf, "%%6Sale Averages Per Day | Week | Month | Year%%0: %d | %d | %d | %d\n\r", PLS_PERDAY(s),PLS_PERWEEK(s),PLS_PERMONTH(s),PLS_PERYEAR(s)); S2C(); } // if in own shop and shopkeep mob is present, give item to mob to sell ACMD(do_shopgive) { plshop *s; chdata *mob; obdata *obj, *next_obj; int dotmode, i; if (IS_NPC(ch)) return; if (!(s = in_own_shop(ch))) { send_to_char("You must be located in your shop.\n\r",ch); return; } // ok, we have ptr to the shop...now find shopkeep... for (mob = world[ch->in_room].people; mob; mob = mob->next_in_room) if (MOB_FLAGGED(mob, MOB_PLSHOPKEEP) && GET_MOB_VNUM(mob) == s->shopkeep_vnum) break; if (!mob) { send_to_char("Your shopkeep must be present.\n\r",ch); return; } one_argument(argument, arg); if (!arg || !*arg) { send_to_char("Shopgive what?\n\r",ch); return; } i = inv_count(mob->carrying); if (i >= s->max_sell) { act("$N is sellng as much as $E can.", TRUE, ch, 0, mob, TO_CHAR); return; } dotmode = find_all_dots(arg); if (dotmode == FIND_ALL) { if (!ch->carrying) send_to_char("You don't seem to be holding anything.\n\r", ch); else for (obj = ch->carrying; obj; obj = next_obj) { next_obj = obj->next_content; // yes.. there is a limit if (++i <= s->max_sell) perform_give(ch, mob, obj, FALSE); else break; } } else if (dotmode == FIND_ALLDOT) { if (!*arg) { send_to_char("All of what?\n\r", ch); return; } if (!(obj = get_obj_in_list_vis(ch, arg, ch->carrying))) { sprintf(buf, "You don't seem to have any %ss.\n\r", arg); S2C(); } else while (obj) { next_obj = get_obj_in_list_vis(ch, arg, obj->next_content); // yes.. there is a limit if (++i <= s->max_sell) perform_give(ch, mob, obj, FALSE); else break; obj = next_obj; } } else { if (!(obj = get_obj_in_list_vis(ch, arg, ch->carrying))) { sprintf(buf, "You don't seem to have %s %s.\n\r", AN(arg), arg); S2C(); } else if (++i <= s->max_sell) perform_give(ch, mob, obj, FALSE); } } // if in own shop and shopkeep mob is present, take item from mob ACMD(do_shoptake) { plshop *s; chdata *mob; obdata *obj, *next_obj; int dotmode; if (IS_NPC(ch)) return; if (!(s = in_own_shop(ch))) { send_to_char("You must be located in your shop.\n\r",ch); return; } // ok, we have ptr to the shop...now find shopkeep... for (mob = world[ch->in_room].people; mob; mob = mob->next_in_room) if (MOB_FLAGGED(mob, MOB_PLSHOPKEEP) && GET_MOB_VNUM(mob) == s->shopkeep_vnum) break; if (!mob) { send_to_char("Your shopkeep must be present.\n\r",ch); return; } one_argument(argument, arg); if (!arg || !*arg) { send_to_char("Shoptake what?\n\r",ch); return; } dotmode = find_all_dots(arg); if (dotmode == FIND_ALL) { if (!mob->carrying) act("$N doesn't seem to be carrying anything.", FALSE, ch, 0, mob, TO_CHAR); else for (obj = mob->carrying; obj; obj = next_obj) { next_obj = obj->next_content; perform_give(mob, ch, obj, FALSE); } } else if (dotmode == FIND_ALLDOT) { if (!*arg) { send_to_char("All of what?\n\r", ch); return; } if (!(obj = get_obj_in_list_vis(mob, arg, mob->carrying))) { sprintf(buf, "$N doesn't seem to have any %ss.", arg); act(buf, FALSE, ch, 0, mob, TO_CHAR); } else while (obj) { next_obj = get_obj_in_list_vis(mob, arg, obj->next_content); perform_give(mob, ch, obj, FALSE); obj = next_obj; } } else { if (!(obj = get_obj_in_list_vis(mob, arg, mob->carrying))) { sprintf(buf, "$N doesn't seem to have %s %s.\n\r", AN(arg), arg); act(buf, FALSE, ch, 0, mob, TO_CHAR); } else perform_give(mob, ch, obj, FALSE); } } // save a mob's inventory list to disk void save_plshopkeep_objs(chdata *mob) { FILE *fp; char fname[MAX_INPUT_LENGTH]; if (!MOB_FLAGGED(mob, MOB_PLSHOPKEEP)) return; sprintf(fname, "plshops/%d.plshopkeep", GET_MOB_VNUM(mob)); if (!mob->carrying) { unlink(fname); return; } if (!(fp = fopen(fname, "w"))) { sprintf(buf, "SYSERR: Error opening plshopkeep file %d.plshopkeep",GET_MOB_VNUM(mob)); mudlog(buf, BRF, LEV_IMM, TRUE); return; } if (inv_count(mob->carrying) > MAX_SIMUL) { sprintf(buf, "%%1WARNING%%0: Max simultaneous items (%d) exceeded.\n\r" "Plshopkeep inventory not saved.\n\r", MAX_SIMUL); send_to_room(buf, mob->in_room); fclose(fp); return; } // cannot save non rentables in file... extract_norents(mob->carrying); // check one more time just so we're not calling a function for no reason // if the room only had norents, now it has nothing, remove file and return if (!mob->carrying) { fclose(fp); unlink(fname); return; } // save_object is recursive, just send the first object in the list // re: objsave.c if (!save_object(mob->carrying, fp)) { fclose(fp); send_to_room("Error saving plshopkeep inventory, notify an immortal...\n\r",mob->in_room); return; } fclose(fp); send_to_room("Saving plshopkeep...\n\r", mob->in_room); sprintf(buf, "SYSUPD: Plshopkeep #%d saved.", GET_MOB_VNUM(mob)); mudlog(buf, BUG, LEV_IMM, FALSE); } // give a mob, load it's objects BOOL load_plshopkeep_objs(chdata *mob) { FILE *fp; char fname[MAX_STRING_LENGTH]; obdata *obj = NULL; int tmp; if (!MOB_FLAGGED(mob, MOB_PLSHOPKEEP)) { sprintf(buf, "SYSERR: Invalid plshopkeep mob (%s).", GET_NAME(mob)); mudlog(buf, NRM, LEV_IMM, TRUE); return FALSE; } sprintf(fname, "plshops/%d.plshopkeep", GET_MOB_VNUM(mob)); if (!(fp = fopen(fname, "rt"))) { if (errno != ENOENT) { sprintf(buf, "SYSERR: Plshopkeep (#%d) object loading error.", GET_MOB_VNUM(mob)); mudlog(buf, NRM, LEV_IMM, TRUE); } return FALSE; } // wipe anything that it currently has... extract_objects(mob->carrying); /* read objects here, restore saved stats, give em to mob */ while (!feof(fp)) { fscanf(fp, "%d", &tmp); if (real_object(tmp) < 0) /* skip it, shouldnt be here */ { fgets(buf, 80, fp); /* skip this line */ continue; } else /* lets read this puppy */ { obj = read_object(tmp, VIRTUAL); fscanf(fp, "%d %d %d %d %d %d %d %d %d %d %d %d\n", &obj->value[0], &obj->value[1], &obj->value[2], &obj->value[3], &obj->objsave_parent, &obj->tmp1, &obj->objsave_self, &obj->tmp2, &obj->objsave_where, &obj->obj_hits, &obj->owner_of, &obj->obj_id); obj_to_char(obj, mob); } } fclose(fp); return TRUE; } //================================================================== // Plshop updates over time... //================================================================== void daily_plshop_update(void) { plshop *s; int days; for (s = pls_global; s; s=s->global_next) { s->gross_income += s->gold_today; s->gold_today = 0; days = ++s->days_open; if (days) s->averages[PERDAY] = s->gross_income/days; } } // every month, cals normal numbers, but also take land tax // if it's flagged as such void monthly_plshop_update(void) { plshop *s; int days, weeks, months; for (s = pls_global; s; s=s->global_next) { days = s->days_open; weeks = days/7; months = weeks/4; if (weeks) s->averages[PERWEEK] = s->gross_income/weeks; if (months) s->averages[PERMONTH] = s->gross_income/months; if (PLS_FLAGGED(s, PLS_LANDTAX)) { // everything is coooolll if (s->gold >= s->land_tax) { s->land_tax_paid += s->land_tax; s->gold -= s->land_tax; } else // uh oh, if it is flagged... syslog it { if (PLS_FLAGGED(s, PLS_LANDTAX_OWED)) { sprintf(buf, "SYSUPD: Plshop #%d unable to pay land tax (%d).", s->plshop_id, s->land_tax); mudlog(buf, BRF, LEV_IMM, TRUE); } SET_BIT(PLS_FLAGS(s), PLS_LANDTAX_OWED); if (s->gold > 0) s->land_tax_paid += s->land_tax - s->gold; // YES will go into the hole... s->gold -= s->land_tax; } } } } void yearly_plshop_update(void) { plshop *s; int days, weeks, months, years; for (s = pls_global; s; s=s->global_next) { days = s->days_open; weeks = days/7; months = weeks/4; years = months/16; if (years) s->averages[PERYEAR] = s->gross_income/years; } } //================================================================== // // RoA OLPSC (Online Player Shop Creation) // // Started 12/23/97 (and all through the house...not a creature was // stirring, except that damn trackball and that // annoying extra-click keyboard... -jtrhone) //================================================================== ROA_MENU(plsedit_top_menu) { char *p; int field; plshop *s = PLS_EDITTED(ch); if (!input_str) { menu_title_send("PlayerShopEdit Main Menu", ch); sprintf(buf, " X.) %%6Shop ID#%%0: %d\n\r", s->plshop_id); S2C(); if (IS_AIMP(ch)) { p = get_name_by_id(s->owner_idnum); sprintf(buf2, "%s", p ? p : "<UNKNOWN>"); sprintf(buf, " 1.) %%6OwnerID#%%0: %d (%s)\n\r", s->owner_idnum, buf2); S2C(); sprintf(buf, " 2.) %%6Shopkeep Vnum%%0: %d\n\r", s->shopkeep_vnum); S2C(); sprintf(buf, " 3.) %%6Shop Room Vnum%%0: %d\n\r", s->shop_rvnum); S2C(); sprinttype(PLS_TYPE(s), plshop_types, buf2); sprintf(buf, " 4.) %%6Shop Type%%0: %%5%s%%0\n\r", buf2); S2C(); sprintbit(PLS_FLAGS(s), plshop_bits, buf2); sprintf(buf, " 5.) %%6Shop Flags%%0: %%5%s%%0\n\r", buf2); S2C(); } sprintbit(PLS_RCFLAGS(s), rcbits, buf2); sprintf(buf, " 6.) %%6Shop RC Flags%%0: %%5%s%%0\n\r", buf2); S2C(); sprintf(buf, " 7.) %%6Shop Minlevel%%0: %d\n\r", s->minlevel); S2C(); sprintf(buf, " 8.) %%6Shop Maxlevel%%0: %d\n\r", s->maxlevel); S2C(); if (IS_AIMP(ch)) { sprintf(buf, " 9.) %%6Max Simultaneous Items to Sell%%0: %d\n\r", s->max_sell); S2C(); if (PLS_FLAGGED(s, PLS_ITEMTAX)) { sprintf(buf, "10.) %%6Item Tax (percentage of item gross)%%0: %d\n\r", s->item_tax); S2C(); } if (PLS_FLAGGED(s, PLS_LANDTAX)) { sprintf(buf, "11.) %%6Property Tax (monthly charge)%%0: %d\n\r", s->land_tax); S2C(); } sprintf(buf, "12.) %%6Shopkeep's Cut (percentage of item gross)%%0: %d\n\r", s->keeper_cut); S2C(); } sprintf(buf, "13.) %%6Owner's Markup (percentage of item gross)%%0: %d\n\r", s->markup); S2C(); if (IS_AIMP(ch)) { sprintf(buf, "14.) %%6Days Open (do not edit)%%0: %d\n\r", s->days_open); S2C(); sprintf(buf, "15.) %%6Account (do not edit)%%0: %d\n\r", s->gold); S2C(); } send_to_char("\n\r", ch); MENU_PROMPT(ch) = "Enter field number to change or 0 to end: "; return; } strcpy(buf, input_str); p = strtok(buf, " \n\r"); if (p) field = atoi(p); else field = 0; // don't let morts do certain options... if (!IS_AIMP(ch) && field != 0 && field != 6 && field != 7 && field != 8 && field != 13) field = 99; switch (field) { case 0: menu_jump(ch, plsedit_confirm_quit); break; case 1: GET_INTEGER_ARG(ch, "Enter Owner ID#: ", s->owner_idnum, 0, 99999); break; case 2: GET_INTEGER_ARG(ch, "Enter Shopkeep Vnum: ", s->shopkeep_vnum, 0, 99999); break; case 3: GET_INTEGER_ARG(ch, "Enter Plshop Room Vnum: ", s->shop_rvnum, 0, 99999); break; case 4: get_integer_list(ch, "Enter number of the desired type: ", &s->type, sizeof(s->type), plshop_types); break; case 5: toggle_menu(ch, "Enter plshop bit to toggle: ", &s->bitv, plshop_bits); break; case 6: toggle_menu(ch, "Enter plshop rcbit to toggle: ", &s->rc_bitv, rcbits); break; case 7: GET_INTEGER_ARG(ch, "Enter Plshop Minlevel: ", s->minlevel, 0, LEV_IMM); break; case 8: GET_INTEGER_ARG(ch, "Enter Plshop Maxlevel: ", s->maxlevel, 0, LEV_IMM); break; case 9: GET_INTEGER_ARG(ch, "Enter Max Simultaneous Items to Sell: ", s->max_sell, 0, 50); break; case 10: if (PLS_FLAGGED(s, PLS_ITEMTAX)) { GET_INTEGER_ARG(ch, "Enter Per Item Tax: ", s->item_tax, 0, 99); } break; case 11: if (PLS_FLAGGED(s, PLS_LANDTAX)) { GET_INTEGER_ARG(ch, "Enter Monthly Property Tax: ", s->land_tax, 0, 32767); } break; case 12: GET_INTEGER_ARG(ch, "Enter Shopkeep's Cut: ", s->keeper_cut, 0, 99); break; case 13: // changed max markup rate for greedy shopkeeps 3/15/98 GET_INTEGER_ARG(ch, "Enter Owner's Markup: ", s->markup, -99, 999); break; case 14: GET_INTEGER_ARG(ch, "Enter # of Days Open (LOGGED): ", s->days_open, 0, 9999); break; case 15: GET_INTEGER_ARG(ch, "Enter Account Amount (LOGGED): ", s->gold, 0, 32767); break; default: send_to_char("That field cannot be changed.\n\r", ch); break; } ch->pc_specials->field_changed = TRUE; } ROA_MENU(plsedit_confirm_quit) { char buf[MAX_STRING_LENGTH]; char *p; if (!input_str) { MENU_PROMPT(ch) = "Quit editting plshop (yes/NO)? "; return; } strcpy(buf, input_str); p = strtok(buf, " \n\r"); if (p && strncasecmp("yes", p, strlen(p)) == 0) menu_jump(ch, plsedit_confirm_save); else menu_jump(ch, plsedit_top_menu); } ROA_MENU(plsedit_confirm_save) { char buf[MAX_STRING_LENGTH]; char *p; plshop *s = PLS_EDITTED(ch); if (!input_str) { MENU_PROMPT(ch) = "Save editted plshop to disk (YES/no)? "; return; } strcpy(buf, input_str); p = strtok(buf, " \n\r"); if (!p || strncasecmp("no", p, strlen(p)) != 0) { save_plshops(); sprintf(buf, "PLRUPD: %s has editted plshop #%d", GET_NAME(ch), s->plshop_id); mudlog(buf, NRM, GET_LEVEL(ch), TRUE); } else if (IS_AIMP(ch)) { menu_jump(ch, plsedit_confirm_delete); return; } PLS_EDITTED(ch) = NULL; MENU_PROMPT(ch) = NULL; MENU_HANDLER(ch) = NULL; MENU_DEPTH(ch) = 0; REMOVE_BIT(PLR_FLAGS(ch), PLR_BUILDING); } ROA_MENU(plsedit_confirm_delete) { char fname[MAX_STRING_LENGTH]; char *p; plshop *ptr = PLS_EDITTED(ch); if (!input_str) { MENU_PROMPT(ch) = "Delete plshop (yes/NO)? "; return; } strcpy(buf, input_str); p = strtok(buf, " \n\r"); if (p && strncasecmp("yes", p, strlen(p)) == 0) { send_to_char("Delete confirmed...\n\r",ch); // send log first, before we wax it sprintf(buf, "PLRUPD: %s has deleted plshop #%d", GET_NAME(ch), ptr->plshop_id); mudlog(buf, NRM, GET_LEVEL(ch), TRUE); // remove from disk sprintf(fname, "%s/%d.plshop", PLSHOP_DIR, ptr->plshop_id); unlink(fname); // remove from memory remove_plshop_from_world(ptr); FREENULL(ptr); } else send_to_char("Delete not confirmed, plshop remains in global list.\n\r",ch); PLS_EDITTED(ch) = NULL; MENU_PROMPT(ch) = NULL; MENU_HANDLER(ch) = NULL; MENU_DEPTH(ch) = 0; REMOVE_BIT(PLR_FLAGS(ch), PLR_BUILDING); }