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 } 1143