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