installer - Several improvements
[dragonfly.git] / games / hack / hack.shk.c
1 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
2 /* hack.shk.c - version 1.0.3 */
3 /* $FreeBSD: src/games/hack/hack.shk.c,v 1.5 1999/11/16 10:26:37 marcel Exp $ */
4
5 #include "hack.h"
6 #ifdef QUEST
7 int shlevel = 0;
8 struct monst *shopkeeper = NULL;
9 struct obj *billobjs = NULL;
10
11 void
12 obfree(struct obj *obj, struct obj *merge)
13 {
14         free(obj);
15 }
16
17 int
18 inshop(void)
19 {
20         return (0);
21 }
22
23 void
24 shopdig(void)
25 {
26 }
27
28 void
29 addtobill(void)
30 {
31 }
32
33 void
34 subfrombill(void)
35 {
36 }
37
38 void
39 splitbill(void)
40 {
41 }
42
43 int
44 dopay(void)
45 {
46         return (0);
47 }
48
49 void
50 paybill(void)
51 {
52 }
53
54 int
55 doinvbill(void)
56 {
57         return (0);
58 }
59
60 void
61 shkdead(void)
62 {
63 }
64
65 int
66 shkcatch(void)
67 {
68         return (0);
69 }
70
71 int
72 shk_move(void)
73 {
74         return (0);
75 }
76
77 void
78 replshk(struct monst *mtmp, struct monst *mtmp2)
79 {
80 }
81
82 const char *
83 shkname(void)
84 {
85         return ("");
86 }
87
88 #else /* QUEST */
89 #include "hack.mfndpos.h"
90 #include "def.eshk.h"
91
92 #define ESHK(mon)       ((struct eshk *)(&(mon->mextra[0])))
93 #define NOTANGRY(mon)   mon->mpeaceful
94 #define ANGRY(mon)      !NOTANGRY(mon)
95
96 /* Descriptor of current shopkeeper. Note that the bill need not be
97  * per-shopkeeper, since it is valid only when in a shop. */
98 static struct monst *shopkeeper = NULL;
99 static struct bill_x *bill;
100 static int shlevel = 0;         /* level of this shopkeeper */
101 struct obj *billobjs;           /* objects on bill with bp->useup */
102                                 /* only accessed here and by save & restore */
103 static long int total;          /* filled by addupbill() */
104 static long int followmsg;      /* last time of follow message */
105
106 /*
107  *      invariants: obj->unpaid iff onbill(obj) [unless bp->useup]
108  *              obj->quan <= bp->bquan
109  */
110
111 char shtypes[] = {      /* 8 shoptypes: 7 specialized, 1 mixed */
112         RING_SYM, WAND_SYM, WEAPON_SYM, FOOD_SYM, SCROLL_SYM,
113         POTION_SYM, ARMOR_SYM, 0
114 };
115
116 static const char *shopnam[] = {
117         "engagement ring", "walking cane", "antique weapon",
118         "delicatessen", "second hand book", "liquor",
119         "used armor", "assorted antiques"
120 };
121
122 static void              setpaid(void);
123 static void              addupbill(void);
124 static void              findshk(int);
125 static struct bill_x    *onbill(struct obj *);
126 static void              pay(long, struct monst *);
127 static int               dopayobj(struct bill_x *);
128 static struct obj       *bp_to_obj(struct bill_x *);
129 static int               getprice(struct obj *);
130 static int               realhunger(void);
131
132 char *
133 shkname(struct monst *mtmp)             /* called in do_name.c */
134 {
135         return (ESHK(mtmp)->shknam);
136 }
137
138 void
139 shkdead(struct monst *mtmp)             /* called in mon.c */
140 {
141         struct eshk *eshk = ESHK(mtmp);
142
143         if (eshk->shoplevel == dlevel)
144                 rooms[eshk->shoproom].rtype = 0;
145         if (mtmp == shopkeeper) {
146                 setpaid();
147                 shopkeeper = NULL;
148                 bill = (struct bill_x *) - 1000; /* dump core when referenced */
149         }
150 }
151
152 void
153 replshk(struct monst *mtmp, struct monst *mtmp2)
154 {
155         if (mtmp == shopkeeper) {
156                 shopkeeper = mtmp2;
157                 bill = &(ESHK(shopkeeper)->bill[0]);
158         }
159 }
160
161 static void
162 setpaid(void)   /* caller has checked that shopkeeper exists */
163                 /* either we paid or left the shop or he just died */
164 {
165         struct obj *obj;
166         struct monst *mtmp;
167
168         for (obj = invent; obj; obj = obj->nobj)
169                 obj->unpaid = 0;
170         for (obj = fobj; obj; obj = obj->nobj)
171                 obj->unpaid = 0;
172         for (obj = fcobj; obj; obj = obj->nobj)
173                 obj->unpaid = 0;
174         for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
175                 for (obj = mtmp->minvent; obj; obj = obj->nobj)
176                         obj->unpaid = 0;
177         for (mtmp = fallen_down; mtmp; mtmp = mtmp->nmon)
178                 for (obj = mtmp->minvent; obj; obj = obj->nobj)
179                         obj->unpaid = 0;
180         while ((obj = billobjs) != NULL) {
181                 billobjs = obj->nobj;
182                 free(obj);
183         }
184         ESHK(shopkeeper)->billct = 0;
185 }
186
187 static void
188 addupbill(void)         /* delivers result in total */
189                         /* caller has checked that shopkeeper exists */
190 {
191         int ct = ESHK(shopkeeper)->billct;
192         struct bill_x *bp = bill;
193
194         total = 0;
195         while (ct--) {
196                 total += bp->price * bp->bquan;
197                 bp++;
198         }
199 }
200
201 int
202 inshop(void)
203 {
204         int roomno = inroom(u.ux, u.uy);
205
206         /* Did we just leave a shop? */
207         if (u.uinshop &&
208             (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) {
209                 if (shopkeeper) {
210                         if (ESHK(shopkeeper)->billct) {
211                                 if (inroom(shopkeeper->mx, shopkeeper->my)
212                                     == u.uinshop - 1)   /* ab@unido */
213                                         pline("Somehow you escaped the shop without paying!");
214                                 addupbill();
215                                 pline("You stole for a total worth of %ld zorkmids.",
216                                     total);
217                                 ESHK(shopkeeper)->robbed += total;
218                                 setpaid();
219                                 if ((rooms[ESHK(shopkeeper)->shoproom].rtype == GENERAL)
220                                     == (rn2(3) == 0))
221                                         ESHK(shopkeeper)->following = 1;
222                         }
223                         shopkeeper = NULL;
224                         shlevel = 0;
225                 }
226                 u.uinshop = 0;
227         }
228
229         /* Did we just enter a zoo of some kind? */
230         if (roomno >= 0) {
231                 int rt = rooms[roomno].rtype;
232                 struct monst *mtmp;
233                 if (rt == ZOO)
234                         pline("Welcome to David's treasure zoo!");
235                 else if (rt == SWAMP)
236                         pline("It looks rather muddy down here.");
237                 else if (rt == MORGUE) {
238                         if (midnight())
239                                 pline("Go away! Go away!");
240                         else
241                                 pline("You get an uncanny feeling ...");
242                 } else
243                         rt = 0;
244                 if (rt != 0) {
245                         rooms[roomno].rtype = 0;
246                         for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
247                                 if (rt != ZOO || !rn2(3))
248                                         mtmp->msleep = 0;
249                 }
250         }
251
252         /* Did we just enter a shop? */
253         if (roomno >= 0 && rooms[roomno].rtype >= 8) {
254                 if (shlevel != dlevel || !shopkeeper
255                     || ESHK(shopkeeper)->shoproom != roomno)
256                         findshk(roomno);
257                 if (!shopkeeper) {
258                         rooms[roomno].rtype = 0;
259                         u.uinshop = 0;
260                 } else if (!u.uinshop) {
261                         if (!ESHK(shopkeeper)->visitct ||
262                             strncmp(ESHK(shopkeeper)->customer, plname, PL_NSIZ)) {
263                                 /* He seems to be new here */
264                                 ESHK(shopkeeper)->visitct = 0;
265                                 ESHK(shopkeeper)->following = 0;
266                                 strncpy(ESHK(shopkeeper)->customer, plname, PL_NSIZ);
267                                 NOTANGRY(shopkeeper) = 1;
268                         }
269                         if (!ESHK(shopkeeper)->following) {
270                                 boolean box, pick;
271
272                                 pline("Hello %s! Welcome%s to %s's %s shop!",
273                                       plname,
274                                       ESHK(shopkeeper)->visitct++ ? " again" : "",
275                                       shkname(shopkeeper),
276                                       shopnam[rooms[ESHK(shopkeeper)->shoproom].rtype - 8]);
277                                 box = carrying(ICE_BOX);
278                                 pick = carrying(PICK_AXE);
279                                 if (box || pick) {
280                                         if (dochug(shopkeeper)) {
281                                                 u.uinshop = 0;  /* he died moving */
282                                                 return (0);
283                                         }
284                                         pline("Will you please leave your %s outside?",
285                                             (box && pick) ? "box and pick-axe" :
286                                             box ? "box" : "pick-axe");
287                                 }
288                         }
289                         u.uinshop = roomno + 1;
290                 }
291         }
292         return (u.uinshop);
293 }
294
295 static void
296 findshk(int roomno)
297 {
298         struct monst *mtmp;
299
300         for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
301                 if (mtmp->isshk && ESHK(mtmp)->shoproom == roomno
302                     && ESHK(mtmp)->shoplevel == dlevel) {
303                         shopkeeper = mtmp;
304                         bill = &(ESHK(shopkeeper)->bill[0]);
305                         shlevel = dlevel;
306                         if (ANGRY(shopkeeper) &&
307                             strncmp(ESHK(shopkeeper)->customer, plname, PL_NSIZ))
308                                 NOTANGRY(shopkeeper) = 1;
309                         /* billobjs = 0; -- this is wrong if we save in a shop */
310                         /* (and it is harmless to have too many things in billobjs) */
311                         return;
312                 }
313         shopkeeper = NULL;
314         shlevel = 0;
315         bill = (struct bill_x *) - 1000;        /* dump core when referenced */
316 }
317
318 static struct bill_x *
319 onbill(struct obj *obj)
320 {
321         struct bill_x *bp;
322
323         if (!shopkeeper)
324                 return (0);
325         for (bp = bill; bp < &bill[ESHK(shopkeeper)->billct]; bp++)
326                 if (bp->bo_id == obj->o_id) {
327                         if (!obj->unpaid)
328                                 pline("onbill: paid obj on bill?");
329                         return (bp);
330                 }
331         if (obj->unpaid)
332                 pline("onbill: unpaid obj not on bill?");
333         return (0);
334 }
335
336 /* called with two args on merge */
337 void
338 obfree(struct obj *obj, struct obj *merge)
339 {
340         struct bill_x *bp = onbill(obj);
341         struct bill_x *bpm;
342
343         if (bp) {
344                 if (!merge) {
345                         bp->useup = 1;
346                         obj->unpaid = 0;        /* only for doinvbill */
347                         obj->nobj = billobjs;
348                         billobjs = obj;
349                         return;
350                 }
351                 bpm = onbill(merge);
352                 if (!bpm) {
353                         /* this used to be a rename */
354                         impossible("obfree: not on bill??");
355                         return;
356                 } else {
357                         /* this was a merger */
358                         bpm->bquan += bp->bquan;
359                         ESHK(shopkeeper)->billct--;
360                         *bp = bill[ESHK(shopkeeper)->billct];
361                 }
362         }
363         free(obj);
364 }
365
366 static void
367 pay(long tmp, struct monst *shkp)
368 {
369         long robbed = ESHK(shkp)->robbed;
370
371         u.ugold -= tmp;
372         shkp->mgold += tmp;
373         flags.botl = 1;
374         if (robbed) {
375                 robbed -= tmp;
376                 if (robbed < 0)
377                         robbed = 0;
378                 ESHK(shkp)->robbed = robbed;
379         }
380 }
381
382 int
383 dopay(void)
384 {
385         long ltmp;
386         struct bill_x *bp;
387         struct monst *shkp;
388         int pass, tmp;
389
390         multi = 0;
391         inshop();
392         for (shkp = fmon; shkp; shkp = shkp->nmon)
393                 if (shkp->isshk && dist(shkp->mx, shkp->my) < 3)
394                         break;
395         if (!shkp && u.uinshop &&
396             inroom(shopkeeper->mx, shopkeeper->my) == ESHK(shopkeeper)->shoproom)
397                 shkp = shopkeeper;
398
399         if (!shkp) {
400                 pline("There is nobody here to receive your payment.");
401                 return (0);
402         }
403         ltmp = ESHK(shkp)->robbed;
404         if (shkp != shopkeeper && NOTANGRY(shkp)) {
405                 if (!ltmp)
406                         pline("You do not owe %s anything.", monnam(shkp));
407                 else if (!u.ugold)
408                         pline("You have no money.");
409                 else {
410                         long ugold = u.ugold;
411
412                         if (u.ugold > ltmp) {
413                                 pline("You give %s the %ld gold pieces he asked for.",
414                                     monnam(shkp), ltmp);
415                                 pay(ltmp, shkp);
416                         } else {
417                                 pline("You give %s all your gold.", monnam(shkp));
418                                 pay(u.ugold, shkp);
419                         }
420                         if (ugold < ltmp / 2)
421                                 pline("Unfortunately, he doesn't look satisfied.");
422                         else {
423                                 ESHK(shkp)->robbed = 0;
424                                 ESHK(shkp)->following = 0;
425                                 if (ESHK(shkp)->shoplevel != dlevel) {
426                                         /* For convenience's sake, let him disappear */
427                                         shkp->minvent = 0;      /* %% */
428                                         shkp->mgold = 0;
429                                         mondead(shkp);
430                                 }
431                         }
432                 }
433                 return (1);
434         }
435
436         if (!ESHK(shkp)->billct) {
437                 pline("You do not owe %s anything.", monnam(shkp));
438                 if (!u.ugold) {
439                         pline("Moreover, you have no money.");
440                         return (1);
441                 }
442                 if (ESHK(shkp)->robbed) {
443 #define min(a, b)       ((a < b) ? a : b)
444                         pline("But since his shop has been robbed recently,");
445                         pline("you %srepay %s's expenses.",
446                             (u.ugold < ESHK(shkp)->robbed) ? "partially " : "",
447                             monnam(shkp));
448                         pay(min(u.ugold, ESHK(shkp)->robbed), shkp);
449                         ESHK(shkp)->robbed = 0;
450                         return (1);
451                 }
452                 if (ANGRY(shkp)) {
453                         pline("But in order to appease %s,",
454                               amonnam(shkp, "angry"));
455                         if (u.ugold >= 1000) {
456                                 ltmp = 1000;
457                                 pline(" you give him 1000 gold pieces.");
458                         } else {
459                                 ltmp = u.ugold;
460                                 pline(" you give him all your money.");
461                         }
462                         pay(ltmp, shkp);
463                         if (strncmp(ESHK(shkp)->customer, plname, PL_NSIZ)
464                             || rn2(3)) {
465                                 pline("%s calms down.", Monnam(shkp));
466                                 NOTANGRY(shkp) = 1;
467                         } else
468                                 pline("%s is as angry as ever.", Monnam(shkp));
469                 }
470                 return (1);
471         }
472         if (shkp != shopkeeper) {
473                 impossible("dopay: not to shopkeeper?");
474                 if (shopkeeper)
475                         setpaid();
476                 return (0);
477         }
478         for (pass = 0; pass <= 1; pass++) {
479                 tmp = 0;
480                 while (tmp < ESHK(shopkeeper)->billct) {
481                         bp = &bill[tmp];
482                         if (!pass && !bp->useup) {
483                                 tmp++;
484                                 continue;
485                         }
486                         if (!dopayobj(bp))
487                                 return (1);
488                         bill[tmp] = bill[--ESHK(shopkeeper)->billct];
489                 }
490         }
491         pline("Thank you for shopping in %s's %s store!",
492               shkname(shopkeeper),
493               shopnam[rooms[ESHK(shopkeeper)->shoproom].rtype - 8]);
494         NOTANGRY(shopkeeper) = 1;
495         return (1);
496 }
497
498 /* return 1 if paid successfully */
499 /*        0 if not enough money */
500 /*       -1 if object could not be found (but was paid) */
501 static int
502 dopayobj(struct bill_x *bp)
503 {
504         struct obj *obj;
505         long ltmp;
506
507         /* find the object on one of the lists */
508         obj = bp_to_obj(bp);
509
510         if (!obj) {
511                 impossible("Shopkeeper administration out of order.");
512                 setpaid();      /* be nice to the player */
513                 return (0);
514         }
515
516         if (!obj->unpaid && !bp->useup) {
517                 impossible("Paid object on bill??");
518                 return (1);
519         }
520         obj->unpaid = 0;
521         ltmp = bp->price * bp->bquan;
522         if (ANGRY(shopkeeper))
523                 ltmp += ltmp / 3;
524         if (u.ugold < ltmp) {
525                 pline("You don't have gold enough to pay %s.",
526                       doname(obj));
527                 obj->unpaid = 1;
528                 return (0);
529         }
530         pay(ltmp, shopkeeper);
531         pline("You bought %s for %ld gold piece%s.",
532               doname(obj), ltmp, plur(ltmp));
533         if (bp->useup) {
534                 struct obj *otmp = billobjs;
535                 if (obj == billobjs)
536                         billobjs = obj->nobj;
537                 else {
538                         while (otmp && otmp->nobj != obj)
539                                 otmp = otmp->nobj;
540                         if (otmp)
541                                 otmp->nobj = obj->nobj;
542                         else
543                                 pline("Error in shopkeeper administration.");
544                 }
545                 free(obj);
546         }
547         return (1);
548 }
549
550 /* routine called after dying (or quitting) with nonempty bill */
551 void
552 paybill(void)
553 {
554         if (shlevel == dlevel && shopkeeper && ESHK(shopkeeper)->billct) {
555                 addupbill();
556                 if (total > u.ugold) {
557                         shopkeeper->mgold += u.ugold;
558                         u.ugold = 0;
559                         pline("%s comes and takes all your possessions.",
560                               Monnam(shopkeeper));
561                 } else {
562                         u.ugold -= total;
563                         shopkeeper->mgold += total;
564                         pline("%s comes and takes the %ld zorkmids you owed him.",
565                             Monnam(shopkeeper), total);
566                 }
567                 setpaid();      /* in case we create bones */
568         }
569 }
570
571 /* find obj on one of the lists */
572 static struct obj *
573 bp_to_obj(struct bill_x *bp)
574 {
575         struct obj *obj;
576         struct monst *mtmp;
577         unsigned id = bp->bo_id;
578
579         if (bp->useup)
580                 obj = o_on(id, billobjs);
581         else if (!(obj = o_on(id, invent)) &&
582                  !(obj = o_on(id, fobj)) &&
583                  !(obj = o_on(id, fcobj))) {
584                 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
585                         if ((obj = o_on(id, mtmp->minvent)) != NULL)
586                                 break;
587                 for (mtmp = fallen_down; mtmp; mtmp = mtmp->nmon)
588                         if ((obj = o_on(id, mtmp->minvent)) != NULL)
589                                 break;
590         }
591         return (obj);
592 }
593
594 /* called in hack.c when we pickup an object */
595 void
596 addtobill(struct obj *obj)
597 {
598         struct bill_x *bp;
599
600         if (!inshop() ||
601             (u.ux == ESHK(shopkeeper)->shk.x && u.uy == ESHK(shopkeeper)->shk.y) ||
602             (u.ux == ESHK(shopkeeper)->shd.x && u.uy == ESHK(shopkeeper)->shd.y) ||
603             onbill(obj) /* perhaps we threw it away earlier */
604            )
605                 return;
606         if (ESHK(shopkeeper)->billct == BILLSZ) {
607                 pline("You got that for free!");
608                 return;
609         }
610         bp = &bill[ESHK(shopkeeper)->billct];
611         bp->bo_id = obj->o_id;
612         bp->bquan = obj->quan;
613         bp->useup = 0;
614         bp->price = getprice(obj);
615         ESHK(shopkeeper)->billct++;
616         obj->unpaid = 1;
617 }
618
619 void
620 splitbill(struct obj *obj, struct obj *otmp)
621 {
622         /* otmp has been split off from obj */
623         struct bill_x *bp;
624         int tmp;
625
626         bp = onbill(obj);
627         if (!bp) {
628                 impossible("splitbill: not on bill?");
629                 return;
630         }
631         if (bp->bquan < otmp->quan)
632                 impossible("Negative quantity on bill??");
633         if (bp->bquan == otmp->quan)
634                 impossible("Zero quantity on bill??");
635         bp->bquan -= otmp->quan;
636
637         if (ESHK(shopkeeper)->billct == BILLSZ)
638                 otmp->unpaid = 0;
639         else {
640                 tmp = bp->price;
641                 bp = &bill[ESHK(shopkeeper)->billct];
642                 bp->bo_id = otmp->o_id;
643                 bp->bquan = otmp->quan;
644                 bp->useup = 0;
645                 bp->price = tmp;
646                 ESHK(shopkeeper)->billct++;
647         }
648 }
649
650 void
651 subfrombill(struct obj *obj)
652 {
653         long ltmp;
654         int tmp;
655         struct obj *otmp;
656         struct bill_x *bp;
657
658         if (!inshop() ||
659             (u.ux == ESHK(shopkeeper)->shk.x && u.uy == ESHK(shopkeeper)->shk.y) ||
660             (u.ux == ESHK(shopkeeper)->shd.x && u.uy == ESHK(shopkeeper)->shd.y))
661                 return;
662         if ((bp = onbill(obj)) != NULL) {
663                 obj->unpaid = 0;
664                 if (bp->bquan > obj->quan) {
665                         otmp = newobj(0);
666                         *otmp = *obj;
667                         bp->bo_id = otmp->o_id = flags.ident++;
668                         otmp->quan = (bp->bquan -= obj->quan);
669                         otmp->owt = 0;  /* superfluous */
670                         otmp->onamelth = 0;
671                         bp->useup = 1;
672                         otmp->nobj = billobjs;
673                         billobjs = otmp;
674                         return;
675                 }
676                 ESHK(shopkeeper)->billct--;
677                 *bp = bill[ESHK(shopkeeper)->billct];
678                 return;
679         }
680         if (obj->unpaid) {
681                 pline("%s didn't notice.", Monnam(shopkeeper));
682                 obj->unpaid = 0;
683                 return;         /* %% */
684         }
685         /* he dropped something of his own - probably wants to sell it */
686         if (shopkeeper->msleep || shopkeeper->mfroz ||
687             inroom(shopkeeper->mx, shopkeeper->my) != ESHK(shopkeeper)->shoproom)
688                 return;
689         if (ESHK(shopkeeper)->billct == BILLSZ ||
690             ((tmp = shtypes[rooms[ESHK(shopkeeper)->shoproom].rtype - 8]) &&
691              tmp != obj->olet) || strchr("_0", obj->olet)) {
692                 pline("%s seems not interested.", Monnam(shopkeeper));
693                 return;
694         }
695         ltmp = getprice(obj) * obj->quan;
696         if (ANGRY(shopkeeper)) {
697                 ltmp /= 3;
698                 NOTANGRY(shopkeeper) = 1;
699         } else
700                 ltmp /= 2;
701         if (ESHK(shopkeeper)->robbed) {
702                 if ((ESHK(shopkeeper)->robbed -= ltmp) < 0)
703                         ESHK(shopkeeper)->robbed = 0;
704                 pline("Thank you for your contribution to restock this recently plundered shop.");
705                 return;
706         }
707         if (ltmp > shopkeeper->mgold)
708                 ltmp = shopkeeper->mgold;
709         pay(-ltmp, shopkeeper);
710         if (!ltmp)
711                 pline("%s gladly accepts %s but cannot pay you at present.",
712                       Monnam(shopkeeper), doname(obj));
713         else
714                 pline("You sold %s and got %ld gold piece%s.", doname(obj), ltmp,
715                       plur(ltmp));
716 }
717
718 int
719 doinvbill(int mode)     /* 0: deliver count 1: paged */
720 {
721         struct bill_x *bp;
722         struct obj *obj;
723         long totused, thisused;
724         char buf[BUFSZ];
725
726         if (mode == 0) {
727                 int cnt = 0;
728
729                 if (shopkeeper)
730                         for (bp = bill; bp - bill < ESHK(shopkeeper)->billct; bp++)
731                                 if (bp->useup ||
732                                     ((obj = bp_to_obj(bp)) && obj->quan < bp->bquan))
733                                         cnt++;
734                 return (cnt);
735         }
736
737         if (!shopkeeper) {
738                 impossible("doinvbill: no shopkeeper?");
739                 return (0);
740         }
741
742         set_pager(0);
743         if (page_line("Unpaid articles already used up:") || page_line(""))
744                 goto quit;
745
746         totused = 0;
747         for (bp = bill; bp - bill < ESHK(shopkeeper)->billct; bp++) {
748                 obj = bp_to_obj(bp);
749                 if (!obj) {
750                         impossible("Bad shopkeeper administration.");
751                         goto quit;
752                 }
753                 if (bp->useup || bp->bquan > obj->quan) {
754                         int cnt, oquan, uquan;
755
756                         oquan = obj->quan;
757                         uquan = (bp->useup ? bp->bquan : bp->bquan - oquan);
758                         thisused = bp->price * uquan;
759                         totused += thisused;
760                         obj->quan = uquan;      /* cheat doname */
761                         sprintf(buf, "x -  %s", doname(obj));
762                         obj->quan = oquan;      /* restore value */
763                         for (cnt = 0; buf[cnt]; cnt++)
764                                 ; /* nothing */
765                         while (cnt < 50)
766                                 buf[cnt++] = ' ';
767                         sprintf(&buf[cnt], " %5ld zorkmids", thisused);
768                         if (page_line(buf))
769                                 goto quit;
770                 }
771         }
772         sprintf(buf, "Total:%50ld zorkmids", totused);
773         if (page_line("") || page_line(buf))
774                 goto quit;
775         set_pager(1);
776         return (0);
777 quit:
778         set_pager(2);
779         return (0);
780 }
781
782 static int
783 getprice(struct obj *obj)
784 {
785         int tmp, ac;
786
787         switch (obj->olet) {
788         case AMULET_SYM:
789                 tmp = 10 * rnd(500);
790                 break;
791         case TOOL_SYM:
792                 tmp = 10 * rnd((obj->otyp == EXPENSIVE_CAMERA) ? 150 : 30);
793                 break;
794         case RING_SYM:
795                 tmp = 10 * rnd(100);
796                 break;
797         case WAND_SYM:
798                 tmp = 10 * rnd(100);
799                 break;
800         case SCROLL_SYM:
801                 tmp = 10 * rnd(50);
802 #ifdef MAIL
803                 if (obj->otyp == SCR_MAIL)
804                         tmp = rnd(5);
805 #endif /* MAIL */
806                 break;
807         case POTION_SYM:
808                 tmp = 10 * rnd(50);
809                 break;
810         case FOOD_SYM:
811                 tmp = 10 * rnd(5 + (2000 / realhunger()));
812                 break;
813         case GEM_SYM:
814                 tmp = 10 * rnd(20);
815                 break;
816         case ARMOR_SYM:
817                 ac = ARM_BONUS(obj);
818                 if (ac <= -10)  /* probably impossible */
819                         ac = -9;
820                 tmp = 100 + ac * ac * rnd(10 + ac);
821                 break;
822         case WEAPON_SYM:
823                 if (obj->otyp < BOOMERANG)
824                         tmp = 5 * rnd(10);
825                 else if (obj->otyp == LONG_SWORD ||
826                          obj->otyp == TWO_HANDED_SWORD)
827                         tmp = 10 * rnd(150);
828                 else
829                         tmp = 10 * rnd(75);
830                 break;
831         case CHAIN_SYM:
832                 pline("Strange ..., carrying a chain?");
833         case BALL_SYM:
834                 tmp = 10;
835                 break;
836         default:
837                 tmp = 10000;
838         }
839         return (tmp);
840 }
841
842 static int
843 realhunger(void)        /* not completely foolproof */
844 {
845         int tmp = u.uhunger;
846         struct obj *otmp = invent;
847
848         while (otmp) {
849                 if (otmp->olet == FOOD_SYM && !otmp->unpaid)
850                         tmp += objects[otmp->otyp].nutrition;
851                 otmp = otmp->nobj;
852         }
853         return ((tmp <= 0) ? 1 : tmp);
854 }
855
856 bool
857 shkcatch(struct obj *obj)
858 {
859         struct monst *shkp = shopkeeper;
860
861         if (u.uinshop && shkp && !shkp->mfroz && !shkp->msleep &&
862             u.dx && u.dy &&
863             inroom(u.ux + u.dx, u.uy + u.dy) + 1 == u.uinshop &&
864             shkp->mx == ESHK(shkp)->shk.x && shkp->my == ESHK(shkp)->shk.y &&
865             u.ux == ESHK(shkp)->shd.x && u.uy == ESHK(shkp)->shd.y) {
866                 pline("%s nimbly catches the %s.", Monnam(shkp), xname(obj));
867                 obj->nobj = shkp->minvent;
868                 shkp->minvent = obj;
869                 return (1);
870         }
871         return (0);
872 }
873
874 /*
875  * shk_move: return 1: he moved  0: he didnt  -1: let m_move do it
876  */
877 int
878 shk_move(struct monst *shkp)
879 {
880         struct monst *mtmp;
881         struct permonst *mdat = shkp->data;
882         xchar gx, gy, omx, omy, nx, ny, nix, niy;
883         schar appr, i;
884         int udist;
885         int z;
886         schar shkroom, chi, chcnt, cnt;
887         boolean uondoor = 0, satdoor, avoid = 0, badinv;
888         coord poss[9];
889         int info[9];
890         struct obj *ib = NULL;
891
892         omx = shkp->mx;
893         omy = shkp->my;
894
895         if ((udist = dist(omx, omy)) < 3) {
896                 if (ANGRY(shkp)) {
897                         hitu(shkp, d(mdat->damn, mdat->damd) + 1);
898                         return (0);
899                 }
900                 if (ESHK(shkp)->following) {
901                         if (strncmp(ESHK(shkp)->customer, plname, PL_NSIZ)) {
902                                 pline("Hello %s! I was looking for %s.",
903                                       plname, ESHK(shkp)->customer);
904                                 ESHK(shkp)->following = 0;
905                                 return (0);
906                         }
907                         if (!ESHK(shkp)->robbed) {      /* impossible? */
908                                 ESHK(shkp)->following = 0;
909                                 return (0);
910                         }
911                         if (moves > followmsg + 4) {
912                                 pline("Hello %s! Didn't you forget to pay?",
913                                       plname);
914                                 followmsg = moves;
915                         }
916                         if (udist < 2)
917                                 return (0);
918                 }
919         }
920
921         shkroom = inroom(omx, omy);
922         appr = 1;
923         gx = ESHK(shkp)->shk.x;
924         gy = ESHK(shkp)->shk.y;
925         satdoor = (gx == omx && gy == omy);
926         if (ESHK(shkp)->following || ((z = holetime()) >= 0 && z * z <= udist)) {
927                 gx = u.ux;
928                 gy = u.uy;
929                 if (shkroom < 0 || shkroom != inroom(u.ux, u.uy))
930                         if (udist > 4)
931                                 return (-1);    /* leave it to m_move */
932         } else if (ANGRY(shkp)) {
933                 long saveBlind = Blind;
934                 Blind = 0;
935                 if (shkp->mcansee && !Invis && cansee(omx, omy)) {
936                         gx = u.ux;
937                         gy = u.uy;
938                 }
939                 Blind = saveBlind;
940                 avoid = FALSE;
941         } else {
942 #define GDIST(x, y)     ((x - gx) * (x - gx) + (y - gy) * (y - gy))
943                 if (Invis)
944                         avoid = FALSE;
945                 else {
946                         uondoor = (u.ux == ESHK(shkp)->shd.x &&
947                                    u.uy == ESHK(shkp)->shd.y);
948                         if (uondoor) {
949                                 if (ESHK(shkp)->billct)
950                                         pline("Hello %s! Will you please pay before leaving?",
951                                             plname);
952                                 badinv = (carrying(PICK_AXE) || carrying(ICE_BOX));
953                                 if (satdoor && badinv)
954                                         return (0);
955                                 avoid = !badinv;
956                         } else {
957                                 avoid = (u.uinshop && dist(gx, gy) > 8);
958                                 badinv = FALSE;
959                         }
960
961                         if (((!ESHK(shkp)->robbed && !ESHK(shkp)->billct) || avoid)
962                             && GDIST(omx, omy) < 3) {
963                                 if (!badinv && !online(omx, omy))
964                                         return (0);
965                                 if (satdoor)
966                                         appr = gx = gy = 0;
967                         }
968                 }
969         }
970         if (omx == gx && omy == gy)
971                 return (0);
972         if (shkp->mconf) {
973                 avoid = FALSE;
974                 appr = 0;
975         }
976         nix = omx;
977         niy = omy;
978         cnt = mfndpos(shkp, poss, info, ALLOW_SSM);
979         if (avoid && uondoor) { /* perhaps we cannot avoid him */
980                 for (i = 0; i < cnt; i++)
981                         if (!(info[i] & NOTONL))
982                                 goto notonl_ok;
983                 avoid = FALSE;
984 notonl_ok:
985                 ;
986         }
987         chi = -1;
988         chcnt = 0;
989         for (i = 0; i < cnt; i++) {
990                 nx = poss[i].x;
991                 ny = poss[i].y;
992                 if (levl[nx][ny].typ == ROOM
993                     || shkroom != ESHK(shkp)->shoproom
994                     || ESHK(shkp)->following) {
995 #ifdef STUPID
996                         /* cater for stupid compilers */
997                         int zz;
998 #endif /* STUPID */
999                         if (uondoor && (ib = sobj_at(ICE_BOX, nx, ny))) {
1000                                 nix = nx;
1001                                 niy = ny;
1002                                 chi = i; break;
1003                         }
1004                         if (avoid && (info[i] & NOTONL))
1005                                 continue;
1006                         if ((!appr && !rn2(++chcnt)) ||
1007 #ifdef STUPID
1008                             (appr && (zz = GDIST(nix, niy)) && zz > GDIST(nx, ny))
1009 #else
1010                             (appr && GDIST(nx, ny) < GDIST(nix, niy))
1011 #endif /* STUPID */
1012                             ) {
1013                                 nix = nx;
1014                                 niy = ny;
1015                                 chi = i;
1016                         }
1017                 }
1018         }
1019         if (nix != omx || niy != omy) {
1020                 if (info[chi] & ALLOW_M) {
1021                         mtmp = m_at(nix, niy);
1022                         if (hitmm(shkp, mtmp) == 1 && rn2(3) &&
1023                             hitmm(mtmp, shkp) == 2)
1024                                 return (2);
1025                         return (0);
1026                 } else if (info[chi] & ALLOW_U) {
1027                         hitu(shkp, d(mdat->damn, mdat->damd) + 1);
1028                         return (0);
1029                 }
1030                 shkp->mx = nix;
1031                 shkp->my = niy;
1032                 pmon(shkp);
1033                 if (ib) {
1034                         freeobj(ib);
1035                         mpickobj(shkp, ib);
1036                 }
1037                 return (1);
1038         }
1039         return (0);
1040 }
1041
1042 /* He is digging in the shop. */
1043 void
1044 shopdig(int fall)
1045 {
1046         if (!fall) {
1047                 if (u.utraptype == TT_PIT)
1048                         pline("\"Be careful, sir, or you might fall through the floor.\"");
1049                 else
1050                         pline("\"Please, do not damage the floor here.\"");
1051         } else if (dist(shopkeeper->mx, shopkeeper->my) < 3) {
1052                 struct obj *obj, *obj2;
1053
1054                 pline("%s grabs your backpack!", shkname(shopkeeper));
1055                 for (obj = invent; obj; obj = obj2) {
1056                         obj2 = obj->nobj;
1057                         if (obj->owornmask)
1058                                 continue;
1059                         freeinv(obj);
1060                         obj->nobj = shopkeeper->minvent;
1061                         shopkeeper->minvent = obj;
1062                         if (obj->unpaid)
1063                                 subfrombill(obj);
1064                 }
1065         }
1066 }
1067 #endif /* QUEST */
1068
1069 bool
1070 online(int x, int y)
1071 {
1072         return (x == u.ux || y == u.uy ||
1073                 (x - u.ux) * (x - u.ux) == (y - u.uy) * (y - u.uy));
1074 }
1075
1076 /* Does this monster follow me downstairs? */
1077 bool
1078 follower(struct monst *mtmp)
1079 {
1080         return (mtmp->mtame || strchr("1TVWZi&, ", mtmp->data->mlet)
1081 #ifndef QUEST
1082                 || (mtmp->isshk && ESHK(mtmp)->following)
1083 #endif /* QUEST */
1084                 );
1085 }