#include #include #include #include #include #include #include "attack.h" #include "defs.h" #include "flag.h" #include "god.h" #include "io.h" #include "lf.h" #include "map.h" #include "move.h" #include "nexus.h" #include "objects.h" #include "shops.h" #include "spell.h" #include "text.h" #define DEF_REMCURSECOST 60 #define DEF_BLESSCOST 50 #define DEF_SURCHARGE 15 extern enum GAMEMODE gamemode; extern prompt_t prompt; extern lifeform_t *player; extern WINDOW *mainwin; float applyshoppricemod(float origprice, lifeform_t *lf) { float newprice; if (lf) { float pricepctmod = 0; enum SKILLLEVEL slev; // price goes up/down for charisma (+/- 25%) pricepctmod -= getstatmod(lf, A_CHA)/2; // modify for speech (up to -30%); slev = getskill(lf, SK_SPEECH); if (slev) { pricepctmod -= (slev*5); } newprice = pctof(origprice, 100 + pricepctmod); } else { newprice = origprice; } if (origprice > 0) { limitf(&newprice, 1, NA); } return newprice; } int getshopblessprice(object_t *o) { int cost = 0; int remcursecost, blesscost,surcharge; remcursecost = applyshoppricemod(DEF_REMCURSECOST, player); blesscost = applyshoppricemod(DEF_BLESSCOST, player); surcharge = applyshoppricemod(DEF_SURCHARGE, player); if (iscursed(o)) { cost = remcursecost; if (isequipped(o)) cost += surcharge; } else { cost = blesscost; } cost *= o->amt; return cost; } void shop(lifeform_t *lf, object_t *vm) { int y,i,done; int curmenu = 0; char ch,buf[BUFLEN]; char toptext[BUFLEN],shopname[BUFLEN]; int npurchased = 0; int ndonated = 0; flag_t *f; object_t *o; strcpy(toptext, ""); taketime(lf, getactspeed(lf)); if (!isplayer(lf)) { if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); getobname(vm, shopname, 1); msg("%s browses through %s.", lfname, shopname); } return; } if (hasflagval(vm->flags, F_BANNEDLF, lf->id, NA, NA, NULL)) { if (!lfhasflag(lf, F_ANONYMOUS)) { msg("\"You are not welcome here, thief!\""); return; } } // temple to dead god? if (vm->type->id == OT_TEMPLE) { flag_t *f; lifeform_t *god = NULL; f = hasflag(vm->flags, F_LINKGOD); if (f) { god = findgod(f->val[0]); } if (!god) { msg("This temple seems to have been abandoned."); return; } } getobname(vm, shopname, 1); makeuppercase(shopname); // assign letters to objects ch = 'a'; for (o = vm->contents->first ; o ; o = o->next) { o->letter = ch; if (++ch > 'z') ch = 'A'; } // list objects for sale and ask for input done = B_FALSE; while (!done) { enum SHOPRETURN (*shopfunc)(lifeform_t *, object_t *, int, char *, int *) = NULL; int *shoparg = NULL; cls(); mvwprintw(mainwin, 0, 0, toptext); y = 2; centre(mainwin,C_WHITE, y, noprefix(shopname)); y += 2; // check for special sm_xxx menu ids first if (curmenu == SM_ABSOLVE) { shopfunc = shopabsolve; shoparg = NULL; } else if (curmenu == SM_BLESS) { shopfunc = shopbless; shoparg = NULL; } else if (curmenu == SM_MIRACLE) { shopfunc = shopmiracle; shoparg = NULL; } else if (curmenu == SM_PURCHASEITEMS) { shopfunc = shoppurchase; shoparg = &npurchased; } else if (curmenu == SM_DETECTCURSE) { shopfunc = shopdetectcurse; shoparg = NULL; } else if (curmenu == SM_DONATE) { shopfunc = shopdonate; shoparg = &ndonated; } else if (curmenu == SM_REST) { shopfunc = shoprest; shoparg = &ndonated; } if (shopfunc) { switch (shopfunc(lf, vm, y, toptext, shoparg)) { case SR_BACK: curmenu = 0; break; case SR_CONTINUE: break; case SR_QUIT: done = B_TRUE; break; } } else { flag_t *retflag[MAXCANDIDATES]; int nretflags,curidx = 0,found = B_TRUE; // show menu items getflags(vm->flags, retflag, &nretflags, F_SHOPMENU, F_NONE); while (found) { found = B_FALSE; for (i = 0; i < nretflags; i++) { f = retflag[i]; // check menu id if ((f->val[0] / 100) != curmenu) continue; // check menuitem index if ((f->val[0] % 100) == curidx) { snprintf(buf, BUFLEN, "%c - %s", f->text[0], f->text + 2); mvwprintw(mainwin, y, 0, "%s", buf); y++; found = B_TRUE; curidx++; } } } // ask for selection y += 2; // ask what to do mvwprintw(mainwin, y, 0, "What will you do (ESC to exit)? "); ch = getch(); // handle selection if (ch == 27) { done = B_TRUE; } else { flag_t *selection = NULL; // find matching menu item for (i = 0; i < nretflags; i++) { f = retflag[i]; // check menu id if ((f->val[0] / 100) != curmenu) continue; // check menuitem index if (f->text[0] == ch) { // handle the action. selection = f; } } if (selection) { switch (selection->val[1]) { case MA_GOTOMENU: curmenu = selection->val[2]; break; case MA_QUIT: done = B_TRUE; break; } } } } // end if curmenu == -1 or something else } // end while not done restoregamewindows(); if (hasflagval(vm->flags, F_BANNEDLF, lf->id, NA, NA, NULL)) { msg("\"...and stay out!\""); } else if (hasflag(lf->flags, F_RESTINGINMOTEL)) { switch (rnd(1,2)) { case 1: snprintf(buf, BUFLEN, "Sleep well!"); break; case 2: snprintf(buf, BUFLEN, "Enjoy your stay!"); break; case 3: snprintf(buf, BUFLEN, "Sweet dreams!"); break; } msg("\"%s\"", buf); } else if (npurchased > ndonated) { switch (rnd(1,2)) { case 1: snprintf(buf, BUFLEN, "Pleasure doing business with you!"); break; case 2: snprintf(buf, BUFLEN, "Thank you, come again!"); break; } msg("\"%s\"", buf); } else if (ndonated > npurchased) { switch (rnd(1,2)) { case 1: snprintf(buf, BUFLEN, "Thank you again for your generosity!"); break; case 2: snprintf(buf, BUFLEN, "I look forward to your next visit!"); break; } msg("\"%s\"", buf); } else { switch (rnd(1,2)) { case 1: snprintf(buf, BUFLEN, "See you next time."); break; case 2: snprintf(buf, BUFLEN, "Farewell!"); break; } msg("\"%s\"", buf); } } enum SHOPRETURN shopdetectcurse(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *ndonated) { object_t *o; char ch; int y; int cost = 0,possible = B_TRUE; y = starty; // calculate cost for (o = lf->pack->first ; o; o = o->next) { if (!o->blessknown && !hasflag(o->flags, F_NOBLESS)) { cost += 10; } } if (cost) { cost = applyshoppricemod(cost, lf); } if (!cost) { mvwprintw(mainwin, y, 0, "You do not seem to possess anything which merits our services."); y += 2; possible = B_FALSE; } else { // ask what to detect mvwprintw(mainwin, y, 0, "It will cost $%d to perform an divination on your items.", cost); y += 2; if (countmoney(lf->pack) < cost) { mvwprintw(mainwin, y, 0, "Unfortunately, you cannot afford this.", cost); y += 2; possible = B_FALSE; } } if (possible){ mvwprintw(mainwin, y, 0, "Pay to detect auras on your items (you have $%d) [yn]? ", countmoney(lf->pack)); } else { mvwprintw(mainwin, y, 0, "[Press a key to return]"); } ch = getch(); if (possible && (ch == 'y')) { givemoney(player, NULL, cost); dospelleffects(lf, OT_S_DETECTAURA, 10, lf, NULL, NULL, B_BLESSED, NULL, B_FALSE); more(); } return SR_BACK; } enum SHOPRETURN shopdonate(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *ndonated) { int count = 0; enum FLAG wantflag; enum OBCLASS wantoc; object_t *o; // ask what to donate switch (vm->type->id) { case OT_SHOPARMOUR: wantflag = F_ARMOURRATING; wantoc = OC_ARMOUR; break; case OT_SHOPWEAPON: wantflag = F_DAM; wantoc = OC_WEAPON; break; case OT_TEMPLE: wantflag = F_NONE; wantoc = OC_MONEY; break; default: wantflag = F_NONE; wantoc = OC_NONE; break; } o = doaskobject(lf->pack, "What will you donate?", &count, B_TRUE, B_FALSE, B_FALSE, '\0', AO_NONE, wantflag, F_NONE); // validate it if (o) { // can we remove it? if (isequipped(o)) { if (takeoff(lf, o)) { more(); return SR_CONTINUE; } } // confirm count for gold if (o->type->obclass->id == OC_MONEY) { char answer[BUFLEN],buf[BUFLEN]; sprintf(buf, "How much money will you donate (you have $%d)?", o->amt); askstring(buf, '?', answer, BUFLEN, NULL); count = atoi(answer); if (count <= 0) { return SR_CONTINUE; } if (count > o->amt) count = o->amt; } // okay it! if ((wantoc != OC_NONE) && (o->type->obclass->id != wantoc)) { msg("Sorry, we can't accept that!"); more(); } else { int goldgiven = 0; if (o->type->id == OT_GOLD) { givemoney(lf, NULL, count); (*ndonated)++; goldgiven += count; } else { object_t *newob; char let; if (vm->contents->first) { let = vm->contents->last->letter + 1; } else { let = 'a'; } newob = moveob(o, vm->contents, count); newob->letter = let; (*ndonated)++; } if ((vm->type->id == OT_TEMPLE) && goldgiven) { flag_t *f; f = hasflag(o->flags, F_LINKGOD); if (f) { lifeform_t *god = NULL; god = findgod(f->val[0]); msg("%s appreciates your kind donation.", god->race->name); modpiety(god->race->id, (goldgiven/2)); } else { msg("We appreciate your kind donation."); } } else { msg("Thanks!"); more(); } } } else { return SR_BACK; } return SR_CONTINUE; } enum SHOPRETURN shopabsolve(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *ndonated) { char ch; int y; int piety,cost = 0,possible = B_TRUE; flag_t *f; lifeform_t *god = NULL; y = starty; // get linked god f = hasflag(vm->flags, F_LINKGOD); god = findgod(f->val[0]); piety = getpiety(god->race->id); if (piety < 100) { cost = (100 - piety) * 3; cost = applyshoppricemod(cost, lf); mvwprintw(mainwin, y, 0, "It will cost $%d to absolve your sins against %s.", cost, god->race->name); y += 2; } else { mvwprintw(mainwin, y, 0, "You do not appear to have sinned against %s.", god->race->name); y += 2; possible = B_FALSE; } if (possible) { if (countmoney(lf->pack) >= cost) { mvwprintw(mainwin, y, 0, "Pay for absolution (you have $%d) [yn]? ", countmoney(lf->pack)); } else { mvwprintw(mainwin, y, 0, "You cannot afford to pay $%d for absolution.", cost); y+= 2; possible = B_FALSE; } } if (!possible) { mvwprintw(mainwin, y, 0, "[Press any key]"); } ch = getch(); if (!possible || (ch != 'y')) { return SR_BACK; } givemoney(lf, NULL, cost); setpiety(god->race->id, 100); msg("\"You sins are forgiven!\""); more(); return SR_BACK; } enum SHOPRETURN shopbless(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *ndonated) { object_t *o; char ch,buf[BUFLEN]; int y; int remcursecost, blesscost,surcharge; y = starty; remcursecost = applyshoppricemod(DEF_REMCURSECOST, lf); blesscost = applyshoppricemod(DEF_BLESSCOST, lf); surcharge = applyshoppricemod(DEF_SURCHARGE, lf); mvwprintw(mainwin, y, 0, "So long as their aura is known, we can bestow a blessing on most items."); y += 2; mvwprintw(mainwin, y, 0, "Curse removal - $%d each",remcursecost); y++; mvwprintw(mainwin, y, 0, "Blessings - $%d each",blesscost); y += 2; mvwprintw(mainwin, y, 0, "(there is a $%d surcharge for uncursing an equipped item)",surcharge); y += 2; mvwprintw(mainwin, y, 0, "Pay for a blessing (you have $%d) [yn]? ", countmoney(lf->pack)); ch = getch(); if (ch != 'y') { return SR_BACK; } // ask which object sprintf(buf, "Bless which object (you have $%d)?", countmoney(lf->pack)); initprompt(&prompt, buf); for (o = player->pack->first ; o ; o = o->next) { if (o->blessknown && (o->blessed != B_BLESSED) && !hasflag(o->flags, F_NOBLESS)) { char costbuf[BUFLEN]; getobname(o, buf, o->amt); sprintf(costbuf, "%-60s($%d)", buf, getshopblessprice(o)); addchoice(&prompt, o->letter, costbuf, costbuf, o, NULL); } } addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL); ch = getchoice(&prompt); o = (object_t *)prompt.result; if (o) { int cost; cost = getshopblessprice(o); if (countmoney(player->pack) < cost) { msg("You cannot afford the $%d blessing price.", cost); more(); } else { msg("You hand over $%d to the priest.", cost); more(); givemoney(player, NULL, cost); msg("The priest raise his hands in supplication."); more(); blessob(o); more(); } } return SR_CONTINUE; } enum SHOPRETURN shopmiracle(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *ndonated) { char ch; int y; int piety,cost = 0,possible = B_TRUE; flag_t *f; lifeform_t *god = NULL; y = starty; // get linked god f = hasflag(vm->flags, F_LINKGOD); god = findgod(f->val[0]); piety = getpiety(god->race->id); cost = 1000 - piety; cost = applyshoppricemod(cost,lf); if (godisangry(god->race->id)) { mvwprintw(mainwin, y, 0, "%s does not currently find you worthy of miracles.", god->race->name); y += 2; possible = B_FALSE; } else { mvwprintw(mainwin, y, 0, "For $%d, we will ask %s to intervene on your behalf.", cost, god->race->name); y += 2; } if (possible) { if (countmoney(lf->pack) >= cost) { mvwprintw(mainwin, y, 0, "Pay for a miracle (you have $%d) [yn]? ", countmoney(lf->pack)); } else { mvwprintw(mainwin, y, 0, "You cannot afford to pay $%d.", cost); y+= 2; possible = B_FALSE; } } if (!possible) { mvwprintw(mainwin, y, 0, "[Press any key]"); } ch = getch(); if (!possible || (ch != 'y')) { return SR_BACK; } givemoney(lf, NULL, cost); godgiftmaybe(god->race->id, B_TRUE); more(); return SR_BACK; } // returns B_TRUE if shop transaction should end enum SHOPRETURN shoppurchase(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *npurchased) { object_t *o; int y; char buf[BUFLEN], buf2[BUFLEN], choices[BUFLENSMALL]; char ch; static enum { BUY, EXAMINE } shopmode = BUY; y = starty; // list objects for sale for (o = vm->contents->first ; o ; o = o->next) { char obname[BUFLEN]; int thisprice; enum COLOUR col; getshopobname(o, obname, o->amt); thisprice = (int)getshopprice(o, player); snprintf(buf, BUFLEN, "%c - %s", o->letter, obname); if (shopmode == BUY) { if (countmoney(player->pack) >= thisprice) { col = C_GREY; } else { col = C_RED; } } else { col = C_GREY; } snprintf(buf2, BUFLEN, "^%d%-60s$%d", col, buf, thisprice); wmove(mainwin, y, 0); textwithcol(mainwin, buf2); y++; } strcpy(choices, ""); y++; wmove(mainwin, y, 0); textwithcol(mainwin, "^n"); mvwprintw(mainwin, y, 0, "You have $%d.", countmoney(player->pack)); y++; y++; // ask what to do mvwprintw(mainwin, y, 0, "What will you %s (ESC when done)? ", (shopmode == BUY) ? "buy" : "examine"); ch = getch(); if (ch == 27) { strcpy(toptext, ""); return SR_BACK; } else if (ch == '?') { if (shopmode == BUY) { shopmode = EXAMINE; } else { shopmode = BUY; } } else { // try to find that object... o = hasobletter(vm->contents, ch); if (o) { if (shopmode == EXAMINE) { describeob(o); } else { enum SKILLLEVEL slev; char obname[BUFLEN],validchars[BUFLENSMALL]; char answer; int value; value = (int)getshopprice(o, player); slev = getskill(player, SK_THIEVERY); // confirm getshopobname(o, obname, o->amt); snprintf(buf, BUFLEN, "Buy%s %s for $%d?", slev ? "/steal" : "", obname, value); strcpy(validchars, "yn"); if (slev) { strcat(validchars, "s"); } answer = askchar(buf, validchars,"n", B_TRUE, B_FALSE); if (answer == 'y') { // prompt to use a card if (hasob(player->pack, OT_CREDITCARD)) { char ch2; ch2 = askchar("Charge this purchase to your credit card?","yn","n", B_TRUE, B_FALSE); if (ch2 == 'y') { object_t *cc; // ask which one initprompt(&prompt, "Which credit card will you use?"); for (cc = player->pack->first ; cc ; cc = cc->next) { if (cc->type->id == OT_CREDITCARD) { char cardname[BUFLEN]; getobname(cc, cardname, 1); addchoice(&prompt, cc->letter, cardname, cardname, cc, NULL); } } if (prompt.nchoices == 1) { // only one card? cc = (object_t *)prompt.choice[0].data; } else { addchoice(&prompt, '-', "(cancel)", "(cancel)", NULL, NULL); prompt.maycancel = B_TRUE; getchoice(&prompt); cc = (object_t *)prompt.result; } if (cc) { if (getcharges(cc) >= getobvalue(o)) { int shopamt; // you got it! usecharges(cc, getobvalue(o)); o->letter = '\0'; shopamt = o->amt; // avoid "purchased: 2 apples" when you only bought 1 but were holding 1 o = moveob(o, player->pack, ALL); identify(o); getobname(o, obname, shopamt); snprintf(toptext, BUFLEN, "Charged to card: %c - %s", o->letter, obname); if (npurchased) (*npurchased)++; // god of thieves likes credit cards... pleasegodmaybe(R_GODTHIEVES, (value/75)); return SR_CONTINUE; } else { // maxed! usecharges(cc, getcharges(o)); // use up all remaining charges // get kicked out msg("^B\"Trying to use a maxed out card, eh? Get out of here, thief!\""); more(); // shop closes addflag(vm->flags, F_BANNEDLF, player->id, NA, NA, NULL); return SR_QUIT; } } } } // do you have enough money? if (countmoney(player->pack) >= getobvalue(o) ) { int shopamt; object_t *gold; gold = hasob(player->pack, OT_GOLD); if (gold) { // if so, buy it // lose money (do this first to avoid potential weight issues) givemoney(player, NULL, value); // clear o->letter o->letter = '\0'; // give object shopamt = o->amt; // avoid "purchased: 2 apples" when you only bought 1 but were holding 1 o = moveob(o, player->pack, ALL); identify(o); getobname(o, obname, shopamt); snprintf(toptext, BUFLEN, "Purchased: %c - %s", o->letter, obname); if (npurchased) (*npurchased)++; } else { msg("You don't seem to have any money..."); more(); o = NULL; } } else { msg("You can't afford that!"); more(); o = NULL; } } else if (answer == 's') { // steal // skillcheck - difficulty based on total value of objects here // 15 + value/50 means: // $50 is diff 16 // $100 is diff 17 // $200 is diff 19 // $500 is diff 25 // $1000 is diff 35 if (skillcheck(player, SC_STEAL, 15+(value/50), 0)) { int shopamt; // success o->letter = '\0'; shopamt = o->amt; // avoid "stolen: 2 apples" when you only bought 1 but were holding 1 o = moveob(o, player->pack, ALL); identify(o); getobname(o, obname, shopamt); snprintf(toptext, BUFLEN, "Stolen: %c - %s", o->letter, obname); pleasegodmaybe(R_GODTHIEVES, (value/50)); o = NULL; } else { msg("^B\"HEY! Get out of my shop, thief!\""); more(); // shop closes addflag(vm->flags, F_BANNEDLF, player->id, NA, NA, NULL); return SR_QUIT; } } else { // cancelled strcpy(toptext, ""); } } // end if shopmode == ... } else { snprintf(toptext, BUFLEN, "No such item."); } // end if o } // end if ch return SR_CONTINUE; } enum SHOPRETURN shoprest(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *ndonated) { char ch,obid[BUFLENSMALL],buf[BUFLEN]; int y,i; int cost = 0,possible = B_TRUE, amt; y = starty; cost = applyshoppricemod(100, lf); mvwprintw(mainwin, y, 0, "We offer good quality rooms for only $%d per hour.", cost); y += 2; if (countmoney(lf->pack) >= cost) { mvwprintw(mainwin, y, 0, "Rent a room (you have $%d) [yn]? ", countmoney(lf->pack)); } else { mvwprintw(mainwin, y, 0, "You cannot afford to pay $%d.", cost); y+= 2; possible = B_FALSE; } if (!possible) { mvwprintw(mainwin, y, 0, "[Press any key]"); } ch = getch(); if (!possible || (ch != 'y')) { return SR_BACK; } // how many hours? sprintf(buf, "How many hours will you pay for (you have $%d)?", countmoney(lf->pack)); initprompt(&prompt, buf); for (i = 1; i <= 9; i++) { int thiscost; thiscost = cost * i; sprintf(buf, "%d hour%s ($%d)", i, (i == 1) ? "" : "s", thiscost); addchoice(&prompt, '0' + i, buf, buf, NULL, NULL); } addchoice(&prompt, '-', "Cancel", NULL, NULL, NULL); ch = getchoice(&prompt); if (ch == '-') { return SR_BACK; } amt = ch - '0'; // ie. 1 - 12 cost *= amt; givemoney(lf, NULL, cost); // mark that we are resting in a hotel sprintf(obid, "%ld", vm->id); addflag(lf->flags, F_RESTINGINMOTEL, amt*60, 0, NA, obid); addflag(lf->flags, F_RESTUNTILBETTER, B_TRUE, NA, NA, NULL); startresting(lf, B_FALSE); more(); breakaitargets(lf, B_FALSE); return SR_QUIT; }