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