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