/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* hack.shk.c - version 1.0.3 */
struct monst
*shopkeeper
= 0;
struct obj
*billobjs
= 0;
obfree(obj
,merge
) register struct obj
*obj
, *merge
; {
doinvbill(){ return(0); }
replshk(mtmp
,mtmp2
) struct monst
*mtmp
, *mtmp2
; {}
char *shkname(){ return(""); }
#include "hack.mfndpos.h"
#define ESHK(mon) ((struct eshk *)(&(mon->mextra[0])))
#define NOTANGRY(mon) mon->mpeaceful
#define ANGRY(mon) !NOTANGRY(mon)
extern char plname
[], *xname();
extern struct obj
*o_on(), *bp_to_obj();
/* Descriptor of current shopkeeper. Note that the bill need not be
per-shopkeeper, since it is valid only when in a shop. */
static struct monst
*shopkeeper
= 0;
static struct bill_x
*bill
;
static int shlevel
= 0; /* level of this shopkeeper */
struct obj
*billobjs
; /* objects on bill with bp->useup */
/* only accessed here and by save & restore */
static long int total
; /* filled by addupbill() */
static long int followmsg
; /* last time of follow message */
invariants: obj->unpaid iff onbill(obj) [unless bp->useup]
char shtypes
[] = { /* 8 shoptypes: 7 specialized, 1 mixed */
RING_SYM
, WAND_SYM
, WEAPON_SYM
, FOOD_SYM
, SCROLL_SYM
,
static char *shopnam
[] = {
"engagement ring", "walking cane", "antique weapon",
"delicatessen", "second hand book", "liquor",
"used armor", "assorted antiques"
shkname(mtmp
) /* called in do_name.c */
register struct monst
*mtmp
;
return(ESHK(mtmp
)->shknam
);
shkdead(mtmp
) /* called in mon.c */
register struct monst
*mtmp
;
register struct eshk
*eshk
= ESHK(mtmp
);
if(eshk
->shoplevel
== dlevel
)
rooms
[eshk
->shoproom
].rtype
= 0;
bill
= (struct bill_x
*) -1000; /* dump core when referenced */
register struct monst
*mtmp
, *mtmp2
;
bill
= &(ESHK(shopkeeper
)->bill
[0]);
setpaid(){ /* caller has checked that shopkeeper exists */
/* either we paid or left the shop or he just died */
register struct obj
*obj
;
register struct monst
*mtmp
;
for(obj
= invent
; obj
; obj
= obj
->nobj
)
for(obj
= fobj
; obj
; obj
= obj
->nobj
)
for(obj
= fcobj
; obj
; obj
= obj
->nobj
)
for(mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
)
for(obj
= mtmp
->minvent
; obj
; obj
= obj
->nobj
)
for(mtmp
= fallen_down
; mtmp
; mtmp
= mtmp
->nmon
)
for(obj
= mtmp
->minvent
; obj
; obj
= obj
->nobj
)
ESHK(shopkeeper
)->billct
= 0;
addupbill(){ /* delivers result in total */
/* caller has checked that shopkeeper exists */
register ct
= ESHK(shopkeeper
)->billct
;
register struct bill_x
*bp
= bill
;
total
+= bp
->price
* bp
->bquan
;
register roomno
= inroom(u
.ux
,u
.uy
);
/* Did we just leave a shop? */
(u
.uinshop
!= roomno
+ 1 || shlevel
!= dlevel
|| !shopkeeper
)) {
if(ESHK(shopkeeper
)->billct
) {
if(inroom(shopkeeper
->mx
, shopkeeper
->my
)
== u
.uinshop
- 1) /* ab@unido */
pline("Somehow you escaped the shop without paying!");
pline("You stole for a total worth of %ld zorkmids.",
ESHK(shopkeeper
)->robbed
+= total
;
if((rooms
[ESHK(shopkeeper
)->shoproom
].rtype
== GENERAL
)
ESHK(shopkeeper
)->following
= 1;
/* Did we just enter a zoo of some kind? */
register int rt
= rooms
[roomno
].rtype
;
register struct monst
*mtmp
;
pline("Welcome to David's treasure zoo!");
pline("It looks rather muddy down here.");
pline("Go away! Go away!");
pline("You get an uncanny feeling ...");
for(mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
)
/* Did we just enter a shop? */
if(roomno
>= 0 && rooms
[roomno
].rtype
>= 8) {
if(shlevel
!= dlevel
|| !shopkeeper
|| ESHK(shopkeeper
)->shoproom
!= roomno
)
if(!ESHK(shopkeeper
)->visitct
||
strncmp(ESHK(shopkeeper
)->customer
, plname
, PL_NSIZ
)){
/* He seems to be new here */
ESHK(shopkeeper
)->visitct
= 0;
ESHK(shopkeeper
)->following
= 0;
(void) strncpy(ESHK(shopkeeper
)->customer
,plname
,PL_NSIZ
);
NOTANGRY(shopkeeper
) = 1;
if(!ESHK(shopkeeper
)->following
) {
pline("Hello %s! Welcome%s to %s's %s shop!",
ESHK(shopkeeper
)->visitct
++ ? " again" : "",
shopnam
[rooms
[ESHK(shopkeeper
)->shoproom
].rtype
- 8] );
pick
= carrying(PICK_AXE
);
u
.uinshop
= 0; /* he died moving */
pline("Will you please leave your %s outside?",
(box
&& pick
) ? "box and pick-axe" :
box
? "box" : "pick-axe");
register struct monst
*mtmp
;
for(mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
)
if(mtmp
->isshk
&& ESHK(mtmp
)->shoproom
== roomno
&& ESHK(mtmp
)->shoplevel
== dlevel
) {
bill
= &(ESHK(shopkeeper
)->bill
[0]);
strncmp(ESHK(shopkeeper
)->customer
,plname
,PL_NSIZ
))
NOTANGRY(shopkeeper
) = 1;
/* billobjs = 0; -- this is wrong if we save in a shop */
/* (and it is harmless to have too many things in billobjs) */
bill
= (struct bill_x
*) -1000; /* dump core when referenced */
onbill(obj
) register struct obj
*obj
; {
register struct bill_x
*bp
;
if(!shopkeeper
) return(0);
for(bp
= bill
; bp
< &bill
[ESHK(shopkeeper
)->billct
]; bp
++)
if(bp
->bo_id
== obj
->o_id
) {
if(!obj
->unpaid
) pline("onbill: paid obj on bill?");
if(obj
->unpaid
) pline("onbill: unpaid obj not on bill?");
/* called with two args on merge */
obfree(obj
,merge
) register struct obj
*obj
, *merge
; {
register struct bill_x
*bp
= onbill(obj
);
register struct bill_x
*bpm
;
obj
->unpaid
= 0; /* only for doinvbill */
/* this used to be a rename */
impossible("obfree: not on bill??");
ESHK(shopkeeper
)->billct
--;
*bp
= bill
[ESHK(shopkeeper
)->billct
];
register struct monst
*shkp
;
long robbed
= ESHK(shkp
)->robbed
;
if(robbed
< 0) robbed
= 0;
ESHK(shkp
)->robbed
= robbed
;
register struct bill_x
*bp
;
register struct monst
*shkp
;
for(shkp
= fmon
; shkp
; shkp
= shkp
->nmon
)
if(shkp
->isshk
&& dist(shkp
->mx
,shkp
->my
) < 3)
inroom(shopkeeper
->mx
,shopkeeper
->my
) == ESHK(shopkeeper
)->shoproom
)
pline("There is nobody here to receive your payment.");
ltmp
= ESHK(shkp
)->robbed
;
if(shkp
!= shopkeeper
&& NOTANGRY(shkp
)) {
pline("You do not owe %s anything.", monnam(shkp
));
pline("You have no money.");
pline("You give %s the %ld gold pieces he asked for.",
pline("You give %s all your gold.", monnam(shkp
));
pline("Unfortunately, he doesn't look satisfied.");
ESHK(shkp
)->following
= 0;
if(ESHK(shkp
)->shoplevel
!= dlevel
) {
/* For convenience's sake, let him disappear */
shkp
->minvent
= 0; /* %% */
pline("You do not owe %s anything.", monnam(shkp
));
pline("Moreover, you have no money.");
#define min(a,b) ((a<b)?a:b)
pline("But since his shop has been robbed recently,");
pline("you %srepay %s's expenses.",
(u
.ugold
< ESHK(shkp
)->robbed
) ? "partially " : "",
pay(min(u
.ugold
, ESHK(shkp
)->robbed
), shkp
);
pline("But in order to appease %s,",
pline(" you give him 1000 gold pieces.");
pline(" you give him all your money.");
if(strncmp(ESHK(shkp
)->customer
, plname
, PL_NSIZ
)
pline("%s calms down.", Monnam(shkp
));
} else pline("%s is as angry as ever.",
impossible("dopay: not to shopkeeper?");
if(shopkeeper
) setpaid();
for(pass
= 0; pass
<= 1; pass
++) {
while(tmp
< ESHK(shopkeeper
)->billct
) {
if(!pass
&& !bp
->useup
) {
if(!dopayobj(bp
)) return(1);
bill
[tmp
] = bill
[--ESHK(shopkeeper
)->billct
];
pline("Thank you for shopping in %s's %s store!",
shopnam
[rooms
[ESHK(shopkeeper
)->shoproom
].rtype
- 8]);
NOTANGRY(shopkeeper
) = 1;
/* return 1 if paid successfully */
/* 0 if not enough money */
/* -1 if object could not be found (but was paid) */
dopayobj(bp
) register struct bill_x
*bp
; {
register struct obj
*obj
;
/* find the object on one of the lists */
impossible("Shopkeeper administration out of order.");
setpaid(); /* be nice to the player */
if(!obj
->unpaid
&& !bp
->useup
){
impossible("Paid object on bill??");
ltmp
= bp
->price
* bp
->bquan
;
if(ANGRY(shopkeeper
)) ltmp
+= ltmp
/3;
pline("You don't have gold enough to pay %s.",
pline("You bought %s for %ld gold piece%s.",
doname(obj
), ltmp
, plur(ltmp
));
register struct obj
*otmp
= billobjs
;
while(otmp
&& otmp
->nobj
!= obj
) otmp
= otmp
->nobj
;
if(otmp
) otmp
->nobj
= obj
->nobj
;
else pline("Error in shopkeeper administration.");
/* routine called after dying (or quitting) with nonempty bill */
if(shlevel
== dlevel
&& shopkeeper
&& ESHK(shopkeeper
)->billct
){
shopkeeper
->mgold
+= u
.ugold
;
pline("%s comes and takes all your possessions.",
shopkeeper
->mgold
+= total
;
pline("%s comes and takes the %ld zorkmids you owed him.",
Monnam(shopkeeper
), total
);
setpaid(); /* in case we create bones */
/* find obj on one of the lists */
register struct bill_x
*bp
;
register struct obj
*obj
;
register struct monst
*mtmp
;
register unsigned id
= bp
->bo_id
;
obj
= o_on(id
, billobjs
);
else if(!(obj
= o_on(id
, invent
)) &&
!(obj
= o_on(id
, fobj
)) &&
!(obj
= o_on(id
, fcobj
))) {
for(mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
)
if(obj
= o_on(id
, mtmp
->minvent
))
for(mtmp
= fallen_down
; mtmp
; mtmp
= mtmp
->nmon
)
if(obj
= o_on(id
, mtmp
->minvent
))
/* called in hack.c when we pickup an object */
addtobill(obj
) register struct obj
*obj
; {
register struct bill_x
*bp
;
(u
.ux
== ESHK(shopkeeper
)->shk
.x
&& u
.uy
== ESHK(shopkeeper
)->shk
.y
) ||
(u
.ux
== ESHK(shopkeeper
)->shd
.x
&& u
.uy
== ESHK(shopkeeper
)->shd
.y
) ||
onbill(obj
) /* perhaps we threw it away earlier */
if(ESHK(shopkeeper
)->billct
== BILLSZ
){
pline("You got that for free!");
bp
= &bill
[ESHK(shopkeeper
)->billct
];
bp
->price
= getprice(obj
);
ESHK(shopkeeper
)->billct
++;
splitbill(obj
,otmp
) register struct obj
*obj
, *otmp
; {
/* otmp has been split off from obj */
register struct bill_x
*bp
;
impossible("splitbill: not on bill?");
if(bp
->bquan
< otmp
->quan
) {
impossible("Negative quantity on bill??");
if(bp
->bquan
== otmp
->quan
) {
impossible("Zero quantity on bill??");
if(ESHK(shopkeeper
)->billct
== BILLSZ
) otmp
->unpaid
= 0;
bp
= &bill
[ESHK(shopkeeper
)->billct
];
ESHK(shopkeeper
)->billct
++;
subfrombill(obj
) register struct obj
*obj
; {
register struct obj
*otmp
;
register struct bill_x
*bp
;
if(!inshop() || (u
.ux
== ESHK(shopkeeper
)->shk
.x
&& u
.uy
== ESHK(shopkeeper
)->shk
.y
) ||
(u
.ux
== ESHK(shopkeeper
)->shd
.x
&& u
.uy
== ESHK(shopkeeper
)->shd
.y
))
if((bp
= onbill(obj
)) != 0){
if(bp
->bquan
> obj
->quan
){
bp
->bo_id
= otmp
->o_id
= flags
.ident
++;
otmp
->quan
= (bp
->bquan
-= obj
->quan
);
otmp
->owt
= 0; /* superfluous */
ESHK(shopkeeper
)->billct
--;
*bp
= bill
[ESHK(shopkeeper
)->billct
];
pline("%s didn't notice.", Monnam(shopkeeper
));
/* he dropped something of his own - probably wants to sell it */
if(shopkeeper
->msleep
|| shopkeeper
->mfroz
||
inroom(shopkeeper
->mx
,shopkeeper
->my
) != ESHK(shopkeeper
)->shoproom
)
if(ESHK(shopkeeper
)->billct
== BILLSZ
||
((tmp
= shtypes
[rooms
[ESHK(shopkeeper
)->shoproom
].rtype
-8]) && tmp
!= obj
->olet
)
|| index("_0", obj
->olet
)) {
pline("%s seems not interested.", Monnam(shopkeeper
));
ltmp
= getprice(obj
) * obj
->quan
;
NOTANGRY(shopkeeper
) = 1;
if(ESHK(shopkeeper
)->robbed
){
if((ESHK(shopkeeper
)->robbed
-= ltmp
) < 0)
ESHK(shopkeeper
)->robbed
= 0;
pline("Thank you for your contribution to restock this recently plundered shop.");
if(ltmp
> shopkeeper
->mgold
)
ltmp
= shopkeeper
->mgold
;
pline("%s gladly accepts %s but cannot pay you at present.",
Monnam(shopkeeper
), doname(obj
));
pline("You sold %s and got %ld gold piece%s.", doname(obj
), ltmp
,
int mode
; /* 0: deliver count 1: paged */
register struct bill_x
*bp
;
register struct obj
*obj
;
for(bp
= bill
; bp
- bill
< ESHK(shopkeeper
)->billct
; bp
++)
((obj
= bp_to_obj(bp
)) && obj
->quan
< bp
->bquan
))
impossible("doinvbill: no shopkeeper?");
if(page_line("Unpaid articles already used up:") || page_line(""))
for(bp
= bill
; bp
- bill
< ESHK(shopkeeper
)->billct
; bp
++) {
impossible("Bad shopkeeper administration.");
if(bp
->useup
|| bp
->bquan
> obj
->quan
) {
register int cnt
, oquan
, uquan
;
uquan
= (bp
->useup
? bp
->bquan
: bp
->bquan
- oquan
);
thisused
= bp
->price
* uquan
;
obj
->quan
= uquan
; /* cheat doname */
(void) sprintf(buf
, "x - %s", doname(obj
));
obj
->quan
= oquan
; /* restore value */
for(cnt
= 0; buf
[cnt
]; cnt
++);
(void) sprintf(&buf
[cnt
], " %5ld zorkmids", thisused
);
(void) sprintf(buf
, "Total:%50ld zorkmids", totused
);
if(page_line("") || page_line(buf
))
getprice(obj
) register struct obj
*obj
; {
tmp
= 10*rnd((obj
->otyp
== EXPENSIVE_CAMERA
) ? 150 : 30);
if(obj
->otyp
== SCR_MAIL
)
tmp
= 10*rnd(5 + (2000/realhunger()));
if(ac
<= -10) /* probably impossible */
tmp
= 100 + ac
*ac
*rnd(10+ac
);
if(obj
->otyp
< BOOMERANG
)
else if(obj
->otyp
== LONG_SWORD
||
obj
->otyp
== TWO_HANDED_SWORD
)
pline("Strange ..., carrying a chain?");
realhunger(){ /* not completely foolproof */
register tmp
= u
.uhunger
;
register struct obj
*otmp
= invent
;
if(otmp
->olet
== FOOD_SYM
&& !otmp
->unpaid
)
tmp
+= objects
[otmp
->otyp
].nutrition
;
return((tmp
<= 0) ? 1 : tmp
);
register struct obj
*obj
;
register struct monst
*shkp
= shopkeeper
;
if(u
.uinshop
&& shkp
&& !shkp
->mfroz
&& !shkp
->msleep
&&
inroom(u
.ux
+u
.dx
, u
.uy
+u
.dy
) + 1 == u
.uinshop
&&
shkp
->mx
== ESHK(shkp
)->shk
.x
&& shkp
->my
== ESHK(shkp
)->shk
.y
&&
u
.ux
== ESHK(shkp
)->shd
.x
&& u
.uy
== ESHK(shkp
)->shd
.y
) {
pline("%s nimbly catches the %s.", Monnam(shkp
), xname(obj
));
obj
->nobj
= shkp
->minvent
;
* shk_move: return 1: he moved 0: he didnt -1: let m_move do it
register struct monst
*shkp
;
register struct monst
*mtmp
;
register struct permonst
*mdat
= shkp
->data
;
register xchar gx
,gy
,omx
,omy
,nx
,ny
,nix
,niy
;
schar shkroom
,chi
,chcnt
,cnt
;
boolean uondoor
, satdoor
, avoid
, badinv
;
if((udist
= dist(omx
,omy
)) < 3) {
(void) hitu(shkp
, d(mdat
->damn
, mdat
->damd
)+1);
if(ESHK(shkp
)->following
) {
if(strncmp(ESHK(shkp
)->customer
, plname
, PL_NSIZ
)){
pline("Hello %s! I was looking for %s.",
plname
, ESHK(shkp
)->customer
);
ESHK(shkp
)->following
= 0;
if(!ESHK(shkp
)->robbed
) { /* impossible? */
ESHK(shkp
)->following
= 0;
if(moves
> followmsg
+4) {
pline("Hello %s! Didn't you forget to pay?",
shkroom
= inroom(omx
,omy
);
satdoor
= (gx
== omx
&& gy
== omy
);
if(ESHK(shkp
)->following
|| ((z
= holetime()) >= 0 && z
*z
<= udist
)){
if(shkroom
< 0 || shkroom
!= inroom(u
.ux
,u
.uy
))
return(-1); /* leave it to m_move */
if(shkp
->mcansee
&& !Invis
&& cansee(omx
,omy
)) {
#define GDIST(x,y) ((x-gx)*(x-gx)+(y-gy)*(y-gy))
uondoor
= (u
.ux
== ESHK(shkp
)->shd
.x
&&
u
.uy
== ESHK(shkp
)->shd
.y
);
pline("Hello %s! Will you please pay before leaving?",
badinv
= (carrying(PICK_AXE
) || carrying(ICE_BOX
));
avoid
= (u
.uinshop
&& dist(gx
,gy
) > 8);
if(((!ESHK(shkp
)->robbed
&& !ESHK(shkp
)->billct
) || avoid
)
if(!badinv
&& !online(omx
,omy
))
if(omx
== gx
&& omy
== gy
)
cnt
= mfndpos(shkp
,poss
,info
,ALLOW_SSM
);
if(avoid
&& uondoor
) { /* perhaps we cannot avoid him */
if(!(info
[i
] & NOTONL
)) goto notonl_ok
;
if(levl
[nx
][ny
].typ
== ROOM
|| shkroom
!= ESHK(shkp
)->shoproom
|| ESHK(shkp
)->following
) {
/* cater for stupid compilers */
if(uondoor
&& (ib
= sobj_at(ICE_BOX
, nx
, ny
))) {
nix
= nx
; niy
= ny
; chi
= i
; break;
if(avoid
&& (info
[i
] & NOTONL
))
if((!appr
&& !rn2(++chcnt
)) ||
(appr
&& (zz
= GDIST(nix
,niy
)) && zz
> GDIST(nx
,ny
))
(appr
&& GDIST(nx
,ny
) < GDIST(nix
,niy
))
if(nix
!= omx
|| niy
!= omy
){
if(hitmm(shkp
,mtmp
) == 1 && rn2(3) &&
hitmm(mtmp
,shkp
) == 2) return(2);
} else if(info
[chi
] & ALLOW_U
){
(void) hitu(shkp
, d(mdat
->damn
, mdat
->damd
)+1);
/* He is digging in the shop. */
if(u
.utraptype
== TT_PIT
)
pline("\"Be careful, sir, or you might fall through the floor.\"");
pline("\"Please, do not damage the floor here.\"");
} else if(dist(shopkeeper
->mx
, shopkeeper
->my
) < 3) {
register struct obj
*obj
, *obj2
;
pline("%s grabs your backpack!", shkname(shopkeeper
));
for(obj
= invent
; obj
; obj
= obj2
) {
if(obj
->owornmask
) continue;
obj
->nobj
= shopkeeper
->minvent
;
shopkeeper
->minvent
= obj
;
return(x
==u
.ux
|| y
==u
.uy
||
(x
-u
.ux
)*(x
-u
.ux
) == (y
-u
.uy
)*(y
-u
.uy
));
/* Does this monster follow me downstairs? */
register struct monst
*mtmp
;
return( mtmp
->mtame
|| index("1TVWZi&, ", mtmp
->data
->mlet
)
|| (mtmp
->isshk
&& ESHK(mtmp
)->following
)