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