nexus/shops.c

1331 lines
35 KiB
C

#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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_IDCOST 50
#define DEF_SURCHARGE 15
#define DEF_RESIZECOST 80
#define DEF_REPAIRCOSTPERHP 2
extern enum GAMEMODE gamemode;
extern prompt_t prompt;
extern lifeform_t *player;
extern WINDOW *mainwin;
// "buf" is the name of an object
//
// this function will adjust the object name to avoid objects which shouldn't appear in shops.
// returns TRUE if the name was modified.
int apply_shopob_restrictions(char *buf) {
if (strstr(buf, "gold dollar")) {
strcpy(buf, "potion of water");
return B_TRUE;
}
if (strstr(buf, "credit card")) {
strcpy(buf, "paper clip");
return B_TRUE;
}
return B_FALSE;
}
// dir == 1 means "better skills increase the price"
// dir == -1 means "better skills decrease the price"
float applyshoppricemod(float origprice, lifeform_t *lf, object_t *shop, enum SHOPACTION action) {
float newprice;
float pricepct = 100;
int dir;
if (action == SA_BUY) {
dir = -1;
} else {
dir = 1;
}
if (lf) {
enum SKILLLEVEL slev;
// price goes up/down for charisma (+/- 25%)
// high bonus = lower price.
pricepct += ((getstatmod(lf, A_CHA)/2) * dir);
// reduce based on speech (up to -30%);
slev = getskill(lf, SK_SPEECH);
if (slev) {
int modamt;
modamt = ((int)(slev*5))*dir;
pricepct += modamt;
}
}
if (shop) {
flag_t *f;
f = hasflag(shop->flags, F_SHOPDONATED);
if (f) {
pricepct += ((((float)f->val[0]) / 100.0) * dir);
}
}
newprice = pctof(pricepct, origprice);
if (origprice > 0) {
limitf(&newprice, 1, NA);
}
return newprice;
}
int canafford(lifeform_t *lf, int amt) {
int goldamt = 0,gemamt = 0;
goldamt = countmoney(lf->pack);
if (getskill(lf, SK_SPEECH) >= PR_BEGINNER) {
gemamt = applyshoppricemod(counthighestobflagvalue(lf->pack, F_GEM), lf, NULL, SA_BUY); // adjust using charisma etc
}
if ((goldamt >= amt ) || (gemamt >= amt) || hasob(lf->pack, OT_CREDITCARD)) {
return B_TRUE;
}
return B_FALSE;
}
int getshopblessprice(object_t *o, object_t *shop) {
int cost = 0;
int remcursecost, blesscost,surcharge;
remcursecost = applyshoppricemod(DEF_REMCURSECOST, player, shop, SA_BUY);
blesscost = applyshoppricemod(DEF_BLESSCOST, player, shop, SA_BUY);
surcharge = applyshoppricemod(DEF_SURCHARGE, player, shop, SA_BUY);
if (iscursed(o)) {
cost = remcursecost;
if (isequipped(o)) cost += surcharge;
} else {
cost = blesscost;
}
cost *= o->amt;
return cost;
}
int obmatchessellflag(object_t *o, flag_t *f, enum SHOPACTION action) {
if ((f->val[0] == F_NONE) || hasflag(o->flags, f->val[0])) {
if ((f->val[2] == NA) || (o->type->obclass->id == f->val[2])) {
if ((action == SA_SELL) && (f->val[1] == NA)) {
} else {
return B_TRUE;
}
}
}
return B_FALSE;
}
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 ntaken = 0;
int ngiven = 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 (lfhasflag(lf, F_STENCH)) {
msg("\"Pheeew! You're not coming in smelling like that.\"");
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 = &ntaken;
} else if (curmenu == SM_DETECTCURSE) {
shopfunc = shopdetectcurse;
shoparg = NULL;
} else if (curmenu == SM_DONATE) {
shopfunc = shopdonate;
shoparg = &ngiven;
} else if (curmenu == SM_ID) {
shopfunc = shopid;
shoparg = &ngiven;
} else if (curmenu == SM_REST) {
shopfunc = shoprest;
shoparg = &ngiven;
} else if (curmenu == SM_RESIZE) {
shopfunc = shopresize;
shoparg = &ngiven;
} else if (curmenu == SM_REPAIR) {
shopfunc = shoprepair;
shoparg = &ngiven;
} else if (curmenu == SM_SELLITEMS) {
shopfunc = shopsell;
shoparg = &ngiven;
}
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);
more();
} else if (vm->type->id == OT_TEMPLE) {
strcpy(buf, "");
// message based on the god...
f = hasflag(vm->flags, F_LINKGOD);
if (f) {
switch (f->val[0]) {
case R_GODDEATH:
switch (rnd(1,2)) {
case 1: snprintf(buf, BUFLEN, "Go forth and kill!"); break;
case 2: snprintf(buf, BUFLEN, "Hecta's favour be with you!"); break;
}
break;
case R_GODMERCY:
switch (rnd(1,3)) {
case 1: snprintf(buf, BUFLEN, "Go in peace."); break;
case 2: snprintf(buf, BUFLEN, "Peace be with you."); break;
case 3: snprintf(buf, BUFLEN, "Remain merciful, traveller."); break;
}
break;
case R_GODPURITY:
switch (rnd(1,2)) {
case 1: snprintf(buf, BUFLEN, "Stay pure, friend."); break;
case 2: snprintf(buf, BUFLEN, "May the purity of Amberon ride with you."); break;
}
break;
case R_GODTHIEVES:
switch (rnd(1,3)) {
case 1: snprintf(buf, BUFLEN, "May your lockpicks never break!"); break;
case 2: snprintf(buf, BUFLEN, "Safe passage, friend."); break;
case 3: snprintf(buf, BUFLEN, "Felix's favour be with you!"); break;
}
break;
}
}
if (strlen(buf)) {
msg("\"%s\"", buf);
}
} else if (ntaken > ngiven) {
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 (ngiven > ntaken) {
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, vm, SA_BUY);
}
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, NULL);
more();
}
return SR_BACK;
}
enum SHOPRETURN shopdonate(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *ndonated) {
int count = 0;
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?", NULL, &count, B_TRUE, B_FALSE, B_FALSE, '\0', vm, SA_DONATE, MT_NOTHING, AO_NONE, F_NONE);
// validate it
if (o) {
long goldgiven = 0;
long multi = 0;
flag_t *f;
// 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;
}
if (o->type->id == OT_GOLD) {
givemoney(lf, NULL, count);
(*ndonated)++;
goldgiven += count;
} else {
object_t *newob;
char let;
int thisval,pct = 100;
if (vm->contents->first) {
let = vm->contents->last->letter + 1;
} else {
let = 'a';
}
thisval = getobvalue(o);
if (!isknown(o)) {
pct -= 50;
}
if (!isidentified(o)) {
pct -= 25;
}
thisval = pctof(pct, thisval);
newob = moveob(o, vm->contents, count);
newob->letter = let;
(*ndonated)++;
practice(player, SK_SPEECH, 1);
goldgiven += thisval;
}
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 {
sayphrase(NULL, SP_THANKS, SV_TALK, NA, NULL, player);
more();
}
f = hasflag(vm->flags, F_SHOPDONATED);
if (f) {
f->val[0] += goldgiven;
} else {
addflag(vm->flags, F_SHOPDONATED, goldgiven, NA, NA, NULL);
}
// remember amount donated, for addition to final score
f = lfhasflagval(lf, F_SCOREBONUS, NA, NA, NA, SCB_DONATIONS);
if (f) {
multi = f->val[1];
goldgiven += f->val[0];
} else {
f = addflag(lf->flags, F_SCOREBONUS, 0, NA, NA, SCB_DONATIONS);
multi = 0;
}
// cope with >= 65535
while (goldgiven > 65535) {
goldgiven -= 65535;
multi++;
}
f->val[0] = goldgiven;
f->val[1] = multi;
} else {
return SR_BACK;
}
angergodmaybe(R_GODTHIEVES, 10, GA_MONEY);
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, vm, SA_BUY);
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, vm, SA_BUY);
blesscost = applyshoppricemod(DEF_BLESSCOST, lf, vm, SA_BUY);
surcharge = applyshoppricemod(DEF_SURCHARGE, lf, vm, SA_BUY);
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) {
int ok = B_FALSE;
if (o->blessknown && (o->blessed != B_BLESSED) && !hasflag(o->flags, F_NOBLESS)) {
ok = B_TRUE;
} else if (!o->blessknown && !hasflag(o->flags, F_NOBLESS)) {
ok = B_TRUE;
}
if (ok) {
char costbuf[BUFLEN];
getobname(o, buf, o->amt);
sprintf(costbuf, "%-60s($%d)", buf, getshopblessprice(o, vm));
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, vm);
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);
// already blessed?
msg("The priest raise his hands in supplication."); more();
if (isblessed(o)) {
msg("\"Hey, this item is already blessed!\""); more();
o->blessknown = B_TRUE;
// chance to get your money back.
if (skillcheck(player, SC_SPEECH, 100, 0)) {
char goldbuf[BUFLEN];
msg("\"...so I will return your payment.\""); more();
sprintf(goldbuf, "%d gold dollars", cost);
addob(player->pack, goldbuf);
} else {
msg("\"Unfortunately, we do not offer refunds.\""); more();
}
} else {
blessob(o);
}
more();
}
}
return SR_CONTINUE;
}
enum SHOPRETURN shopid(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *nid) {
flag_t *sellflag[MAXCANDIDATES];
char buf[BUFLEN],obname[BUFLEN];
int nsellflags;
object_t *o;
int y,ch,i;
int cost = DEF_IDCOST;
y = starty;
cost = applyshoppricemod(DEF_IDCOST,lf, vm, SA_ID);
*nid = 0;
mvwprintw(mainwin, y, 0, "We can help inspect your items for a small fee."); y += 2;
mvwprintw(mainwin, y, 0, "For just $%d per item we can reveal their general purpose,", cost); y++;
mvwprintw(mainwin, y, 0, "however please note that we are unable to detect magical"); y++;
mvwprintw(mainwin, y, 0, "enchantments or blessings.", cost); y += 2;
mvwprintw(mainwin, y, 0, "Identify items [y/n]? ", countmoney(lf->pack));
ch = getch();
if (ch != 'y') {
return SR_BACK;
}
getflags(vm->flags, sellflag, &nsellflags, F_SHOPACCEPTSFLAG, F_NONE);
// ask what to sell
sprintf(buf, "What would you like to inspect (you have $%d)?", countmoney(lf->pack));
initprompt(&prompt, buf);
prompt.maycancel = B_TRUE;
for (o = player->pack->first ; o ; o = o->next) {
int ok = B_FALSE;
for (i = 0; i < nsellflags; i++) {
if (obmatchessellflag(o, sellflag[i], SA_ID)) {
if (!isknown(o)) {
ok = B_TRUE;
break;
}
}
}
if (ok) {
char costbuf[BUFLEN];
getobname(o, buf, o->amt);
sprintf(costbuf, "%-60s($%d)", buf, cost);
addchoice(&prompt, o->letter, costbuf, costbuf, o, NULL);
}
}
addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL);
ch = getchoice(&prompt);
o = (object_t *)prompt.result;
if (!o) {
return SR_BACK;
}
// confirm
getobname(o, obname, o->amt);
if (countmoney(player->pack) < cost) {
msg("You cannot afford the $%d inspection fee.", cost);
more();
} else {
// pay.
msg("You hand over $%d.", cost); more();
givemoney(player, NULL, cost);
// id it.
makeknown(o->type->id);
// get name again
getobname(o, obname, o->amt);
// tell the player
msg("\"%s %s.\"",(o->amt == 1) ? "That is" : "Those are", obname);
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, vm, SA_BUY);
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, vm);
snprintf(buf, BUFLEN, "%c - %s", o->letter, obname);
if (shopmode == BUY) {
if (canafford(player, 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=done, ?=toggle action)? ", (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, vm);
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') {
char ques[BUFLEN];
object_t *oo;
// do you have enough money (or a credit card)?
if (canafford(player, value)) {
object_t *money = NULL;
enum SKILLLEVEL slev;
slev = getskill(lf, SK_SPEECH);
// determine payment method
sprintf(ques, "How will you pay for %s ($%d)?", obname, value);
initprompt(&prompt, ques);
for (oo = player->pack->first ; oo ; oo = oo->next) {
if ((oo->type->id == OT_GOLD) || (oo->type->id == OT_CREDITCARD) || hasflag(oo->flags, F_GEM)) {
char moneyname[BUFLEN];
char fullname[BUFLEN];
int valid = B_FALSE;
getobname(oo, moneyname, oo->amt);
if (oo->type->id == OT_GOLD) {
// only list gold if you have enough
if (getobvalue(oo) >= value) valid = B_TRUE;
strcpy(fullname, moneyname);
} else if (hasflag(oo->flags, F_GEM)) {
// gems are only valid if our psychology skill is high
// enough to convince the shopkeeper to accept them.
if (slev >= PR_BEGINNER) {
int thisval;
thisval = applyshoppricemod(getobvalue(oo), player, vm, SA_SELL);
if (thisval >= value) {
valid = B_TRUE;
sprintf(fullname, "%s (worth $%d)", moneyname, thisval);
}
}
} else {
// always list anything else (credit cards)
valid = B_TRUE;
strcpy(fullname, moneyname);
}
if (valid) {
addchoice(&prompt, oo->letter, fullname, fullname, oo, NULL);
}
}
}
money = NULL;
if (prompt.nchoices == 1) {
// only one possibility.
// is it gold?
money = (object_t *)prompt.choice[0].data;
if (money->type->id != OT_GOLD) {
// if not gold, still confirm it.
money = NULL;
}
}
if (!money) {
addchoice(&prompt, '-', "(cancel)", "(cancel)", NULL, NULL);
prompt.maycancel = B_TRUE;
getchoice(&prompt);
money = (object_t *)prompt.result;
}
if (money) {
int buyit = B_FALSE;
char buytext[BUFLEN];
enum {
PM_GOLD,
PM_GEM,
PM_CARD,
} purchasemethod;
if (money->type->id == OT_CREDITCARD) {
purchasemethod = PM_CARD;
if (getcharges(money) >= value) {
// you got it!
buyit = B_TRUE;
usecharges(money, value);
strcpy(buytext, "Charged to card");
if (npurchased) (*npurchased)++;
} else {
// maxed!
usecharges(money, 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;
}
} else { // ie gold or gem;
if (getobvalue(money) >= value) {
buyit = B_TRUE;
// lose money (do this first to avoid potential weight issues)
if (hasflag(money->flags, F_GEM)) {
int gemsneeded;
char gemname[BUFLEN];
int valpergem;
valpergem = applyshoppricemod(real_getobvalue(money, 1), player, vm, SA_SELL);
gemsneeded = value / valpergem;
limit(&gemsneeded, 1, NA); // (in case gem is worth more than object)
getobname(money, gemname, gemsneeded);
// remove enough to pay...
removeob(money, gemsneeded);
purchasemethod = PM_GEM;
// announce that we're using a gem
msg("You hand over %s.", gemname); more();
} else {
givemoney(player, NULL, value);
purchasemethod = PM_GOLD;
}
strcpy(buytext, "Purchased");
if (npurchased) (*npurchased)++;
} else {
msg("\"I'm afraid that won't cover it...\""); more();
o = NULL;
}
}
if (buyit) {
int shopamt;
// 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, "%s: %c - %s", buytext, o->letter, obname);
if (npurchased) (*npurchased)++;
if (purchasemethod == PM_CARD) {
// god of thieves likes credit cards...
pleasegodmaybe(R_GODTHIEVES, (value/75));
more(); // in case there was an effect
} else {
practice(player, SK_SPEECH, 1);
}
}
} else {
msg("Cancelled."); more();
o = NULL;
} // end if money
} else {
msg("You can't afford that!"); more();
o = NULL;
}
} else if (answer == 's') { // steal
// skillcheck - difficulty based on total value of objects here
if (skillcheck(player, SC_STEAL, 75+(value/10), 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);
practice(player, SK_THIEVERY, 1);
pleasegodmaybe(R_GODTHIEVES, (value/50));
more(); // in case there was an effect
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 shoprepair(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *ndonated) {
object_t *o;
char ch,buf[BUFLEN];
int y,hpcost;
char obname[BUFLEN];
flag_t *f;
y = starty;
hpcost = DEF_REPAIRCOSTPERHP;
// get list of objects to repair
sprintf(buf, "Repair which object (you have $%d)?", countmoney(lf->pack));
initprompt(&prompt, buf);
for (o = player->pack->first ; o ; o = o->next) {
if (isarmour(o) && isdamaged(o)) {
char desc[BUFLEN];
int thiscost;
// determine cost...
f = hasflag(o->flags, F_OBHP);
thiscost = applyshoppricemod((f->val[1] - f->val[0]) * hpcost, lf, vm, SA_BUY);
// add to prompt
getobname(o, obname, o->amt);
sprintf(desc, "%s ($%d)", obname,thiscost);
addchoice(&prompt, o->letter, desc, desc, o, NULL);
}
}
addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL);
mvwprintw(mainwin, y, 0, "We offer repair services for most kinds armour."); y+=2;
if (prompt.nchoices == 1) {
mvwprintw(mainwin, y, 0, "You have no armour in need of repair."); y += 2;
mvwprintw(mainwin, y, 0, "[Press any key]");
} else {
mvwprintw(mainwin, y, 0, "Pay to repair your armour (you have $%d) [yn]? ", countmoney(lf->pack));
}
ch = getch();
if ((prompt.nchoices == 1) || (ch != 'y')) {
return SR_BACK;
}
// ask which object
ch = getchoice(&prompt);
o = (object_t *)prompt.result;
if (o) {
int thiscost;
// determine cost...
f = hasflag(o->flags, F_OBHP);
thiscost = applyshoppricemod((f->val[1] - f->val[0]) * hpcost, lf, vm, SA_BUY);
if (thiscost > countmoney(player->pack)) {
msg("You can't afford to repair that item!"); more();
return SR_CONTINUE;
}
msg("You hand over $%d.", thiscost); more();
givemoney(player, NULL, thiscost);
f->val[0] = f->val[1];
getobname(o, obname, o->amt);
msg("Your %s %s completely repaired.", obname, (o->amt == 1) ? "is" : "are"); more();
}
return SR_CONTINUE;
}
enum SHOPRETURN shopresize(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *ndonated) {
object_t *o;
char ch,buf[BUFLEN];
int y;
int resizecost;
y = starty;
resizecost = applyshoppricemod(pctof(75, DEF_RESIZECOST), lf, vm, SA_BUY);
mvwprintw(mainwin, y, 0, "For just $%d per item, we can resize weapons or armour to fit you.", resizecost); y ++;
mvwprintw(mainwin, y, 0, "(item quality will not be affected)"); y += 2;
if (countmoney(player->pack) < resizecost) {
mvwprintw(mainwin, y, 0, "Sadly, you cannot afford the resizing fee."); y += 2;
mvwprintw(mainwin, y, 0, "[Press a key to return]");
getch();
return SR_BACK;
} else {
mvwprintw(mainwin, y, 0, "Pay to resize an item (you have $%d) [yn]? ", countmoney(lf->pack));
}
ch = getch();
if (ch != 'y') {
return SR_BACK;
}
// ask which object
sprintf(buf, "Resize which object (you have $%d)?", countmoney(lf->pack));
initprompt(&prompt, buf);
for (o = player->pack->first ; o ; o = o->next) {
if (hasflag(o->flags, F_MULTISIZE) && !armourfits(player, o, NULL)) {
char costbuf[BUFLEN];
getobname(o, buf, o->amt);
sprintf(costbuf, "%-60s($%d)", buf, resizecost);
addchoice(&prompt, o->letter, costbuf, costbuf, o, NULL);
}
}
addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL);
ch = getchoice(&prompt);
o = (object_t *)prompt.result;
if (o) {
msg("You hand over $%d.", resizecost); more();
givemoney(player, NULL, resizecost);
resizeobject(o, getlfsize(player)); // always go to player's size
getobname(o, buf, 1);
msgnocap("%c - %s", o->letter, buf);
more();
}
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(10, lf, vm, SA_BUY);
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);
breakaitargets(lf, B_FALSE);
startresting(lf, B_FALSE);
more();
return SR_QUIT;
}
enum SHOPRETURN shopsell(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *nsold) {
int count = 0;
flag_t *sellflag[MAXCANDIDATES],*curflag = NULL ;
char buf[BUFLEN];
int nsellflags;
object_t *o;
int y,ch,i;
y = starty;
*nsold = 0;
mvwprintw(mainwin, y, 0, "We offer good prices for quality items."); y += 2;
mvwprintw(mainwin, y, 0, "Sell items [y/n]? ", countmoney(lf->pack));
ch = getch();
if (ch != 'y') {
return SR_BACK;
}
getflags(vm->flags, sellflag, &nsellflags, F_SHOPACCEPTSFLAG, F_NONE);
// ask what to sell
sprintf(buf, "What will you sell (you have $%d)?", countmoney(lf->pack));
o = doaskobject(player->pack, buf, NULL, &count, B_TRUE, B_FALSE, B_FALSE, '\0', vm, SA_SELL, MT_NOTHING, AO_NONE, F_NONE);
if (!o) {
return SR_BACK;
}
// validate it
if (o) {
object_t *newob;
char let, paymentbuf[BUFLEN],obname[BUFLEN],buf[BUFLEN];
int sellprice;
// get matching sell flag
curflag = NULL;
for (i = 0; i < nsellflags; i++) {
if (obmatchessellflag(o, sellflag[i], SA_SELL)) {
curflag = sellflag[i];
break;
}
}
if (!curflag) {
// you picked something impoassible
msg("We don't accept that kind of object.");
more();
return SR_CONTINUE;
}
// can we remove it?
if (isequipped(o)) {
if (takeoff(lf, o)) {
more();
return SR_CONTINUE;
}
}
// calculate sale price
sellprice = applyshoppricemod (
pctof(curflag->val[1], real_getobvalue(o, 1)),
lf, vm, SA_SELL );
sellprice *= count;
// confirm
getobname(o, obname, count);
sprintf(buf, "Sell %s for $%d?", obname, sellprice);
ch = askchar(buf, "yn","n", B_TRUE, B_FALSE);
if (ch == 'y') {
// move object to the shop
if (vm->contents->first) {
let = vm->contents->last->letter + 1;
} else {
let = 'a';
}
newob = moveob(o, vm->contents, count);
newob->letter = let;
(*nsold)++;
// give money to player
sprintf(paymentbuf, "%d gold dollars", sellprice);
addob(player->pack, paymentbuf);
practice(player, SK_SPEECH, 1);
msg("Pleasure doing business with you!!"); more();
}
} else {
return SR_BACK;
}
return SR_CONTINUE;
}