1 /* $OpenBSD: hack.shk.c,v 1.11 2009/10/27 23:59:25 deraadt 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 <stdio.h> 65 #include <stdlib.h> 66 #include "hack.h" 67 68 #ifdef QUEST 69 int shlevel = 0; 70 struct monst *shopkeeper = 0; 71 struct obj *billobjs = 0; 72 73 void 74 obfree(struct obj *obj, struct obj *merge) 75 { 76 free((char *) obj); 77 } 78 79 int 80 inshop() 81 { 82 return(0); 83 } 84 85 void 86 shopdig(int a) 87 {} 88 89 void 90 addtobill(struct obj *ign) 91 {} 92 93 void 94 subfrombill(struct obj *ign) 95 {} 96 97 void 98 splitbill(struct obj *ign, struct obj *ign2) 99 {} 100 101 int 102 dopay() 103 { 104 return(0); 105 } 106 107 void 108 paybill() 109 {} 110 111 int 112 doinvbill(int a) 113 { 114 return(0); 115 } 116 117 void 118 shkdead(struct monst *ign) 119 {} 120 121 int 122 shkcatch(struct obj *ign) 123 { 124 return(0); 125 } 126 127 int 128 shk_move(struct monst *ign) 129 { 130 return(0); 131 } 132 133 void 134 replshk(struct monst *mtmp, struct monst *mtmp2) 135 {} 136 137 char * 138 shkname(struct monst *ign) 139 { 140 return(""); 141 } 142 143 #else /* QUEST */ 144 #include "hack.mfndpos.h" 145 #include "def.eshk.h" 146 147 #define ESHK(mon) ((struct eshk *)(&(mon->mextra[0]))) 148 #define NOTANGRY(mon) mon->mpeaceful 149 #define ANGRY(mon) !NOTANGRY(mon) 150 151 extern char plname[]; 152 153 /* Descriptor of current shopkeeper. Note that the bill need not be 154 * per-shopkeeper, since it is valid only when in a shop. 155 */ 156 static struct monst *shopkeeper = 0; 157 static struct bill_x *bill; 158 static int shlevel = 0; /* level of this shopkeeper */ 159 struct obj *billobjs; /* objects on bill with bp->useup */ 160 /* only accessed here and by save & restore */ 161 static long int total; /* filled by addupbill() */ 162 static long int followmsg; /* last time of follow message */ 163 164 /* 165 * invariants: obj->unpaid iff onbill(obj) [unless bp->useup] 166 * obj->quan <= bp->bquan 167 */ 168 169 170 char shtypes[] = { /* 8 shoptypes: 7 specialized, 1 mixed */ 171 RING_SYM, WAND_SYM, WEAPON_SYM, FOOD_SYM, SCROLL_SYM, 172 POTION_SYM, ARMOR_SYM, 0 173 }; 174 175 static char *shopnam[] = { 176 "engagement ring", "walking cane", "antique weapon", 177 "delicatessen", "second hand book", "liquor", 178 "used armor", "assorted antiques" 179 }; 180 181 static void setpaid(void); 182 static void addupbill(void); 183 static void findshk(int); 184 static struct bill_x *onbill(struct obj *); 185 static void pay(long, struct monst *); 186 static int dopayobj(struct bill_x *); 187 static struct obj *bp_to_obj(struct bill_x *); 188 static int getprice(struct obj *); 189 static int realhunger(); 190 191 /* called in do_name.c */ 192 char * 193 shkname(struct monst *mtmp) 194 { 195 return(ESHK(mtmp)->shknam); 196 } 197 198 void 199 shkdead(struct monst *mtmp) /* called in mon.c */ 200 { 201 struct eshk *eshk = ESHK(mtmp); 202 203 if(eshk->shoplevel == dlevel) 204 rooms[eshk->shoproom].rtype = 0; 205 if(mtmp == shopkeeper) { 206 setpaid(); 207 shopkeeper = 0; 208 bill = (struct bill_x *) -1000; /* dump core when referenced */ 209 } 210 } 211 212 void 213 replshk(struct monst *mtmp, struct monst *mtmp2) 214 { 215 if(mtmp == shopkeeper) { 216 shopkeeper = mtmp2; 217 bill = &(ESHK(shopkeeper)->bill[0]); 218 } 219 } 220 221 /* caller has checked that shopkeeper exists */ 222 /* either we paid or left the shop or he just died */ 223 static void 224 setpaid() 225 { 226 struct obj *obj; 227 struct monst *mtmp; 228 229 for(obj = invent; obj; obj = obj->nobj) 230 obj->unpaid = 0; 231 for(obj = fobj; obj; obj = obj->nobj) 232 obj->unpaid = 0; 233 for(obj = fcobj; obj; obj = obj->nobj) 234 obj->unpaid = 0; 235 for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) 236 for(obj = mtmp->minvent; obj; obj = obj->nobj) 237 obj->unpaid = 0; 238 for(mtmp = fallen_down; mtmp; mtmp = mtmp->nmon) 239 for(obj = mtmp->minvent; obj; obj = obj->nobj) 240 obj->unpaid = 0; 241 while ((obj = billobjs)) { 242 billobjs = obj->nobj; 243 free((char *) obj); 244 } 245 ESHK(shopkeeper)->billct = 0; 246 } 247 248 /* delivers result in total */ 249 /* caller has checked that shopkeeper exists */ 250 static void 251 addupbill() 252 { 253 int ct = ESHK(shopkeeper)->billct; 254 struct bill_x *bp = bill; 255 256 total = 0; 257 while(ct--){ 258 total += bp->price * bp->bquan; 259 bp++; 260 } 261 } 262 263 int 264 inshop() 265 { 266 int roomno = inroom(u.ux,u.uy); 267 268 /* Did we just leave a shop? */ 269 if(u.uinshop && 270 (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) { 271 if(shopkeeper) { 272 if(ESHK(shopkeeper)->billct) { 273 if(inroom(shopkeeper->mx, shopkeeper->my) 274 == u.uinshop - 1) /* ab@unido */ 275 pline("Somehow you escaped the shop without paying!"); 276 addupbill(); 277 pline("You stole for a total worth of %ld zorkmids.", 278 total); 279 ESHK(shopkeeper)->robbed += total; 280 setpaid(); 281 if((rooms[ESHK(shopkeeper)->shoproom].rtype == GENERAL) 282 == (rn2(3) == 0)) 283 ESHK(shopkeeper)->following = 1; 284 } 285 shopkeeper = 0; 286 shlevel = 0; 287 } 288 u.uinshop = 0; 289 } 290 291 /* Did we just enter a zoo of some kind? */ 292 if(roomno >= 0) { 293 int rt = rooms[roomno].rtype; 294 struct monst *mtmp; 295 if(rt == ZOO) { 296 pline("Welcome to David's treasure zoo!"); 297 } else 298 if(rt == SWAMP) { 299 pline("It looks rather muddy down here."); 300 } else 301 if(rt == MORGUE) { 302 if(midnight()) 303 pline("Go away! Go away!"); 304 else 305 pline("You get an uncanny feeling ..."); 306 } else 307 rt = 0; 308 if(rt != 0) { 309 rooms[roomno].rtype = 0; 310 for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) 311 if(rt != ZOO || !rn2(3)) 312 mtmp->msleep = 0; 313 } 314 } 315 316 /* Did we just enter a shop? */ 317 if(roomno >= 0 && rooms[roomno].rtype >= 8) { 318 if(shlevel != dlevel || !shopkeeper 319 || ESHK(shopkeeper)->shoproom != roomno) 320 findshk(roomno); 321 if(!shopkeeper) { 322 rooms[roomno].rtype = 0; 323 u.uinshop = 0; 324 } else if(!u.uinshop){ 325 if(!ESHK(shopkeeper)->visitct || 326 strncmp(ESHK(shopkeeper)->customer, plname, PL_NSIZ)){ 327 328 /* He seems to be new here */ 329 ESHK(shopkeeper)->visitct = 0; 330 ESHK(shopkeeper)->following = 0; 331 (void) strlcpy(ESHK(shopkeeper)->customer,plname,PL_NSIZ); 332 NOTANGRY(shopkeeper) = 1; 333 } 334 if(!ESHK(shopkeeper)->following) { 335 boolean box, pick; 336 337 pline("Hello %s! Welcome%s to %s's %s shop!", 338 plname, 339 ESHK(shopkeeper)->visitct++ ? " again" : "", 340 shkname(shopkeeper), 341 shopnam[rooms[ESHK(shopkeeper)->shoproom].rtype - 8] ); 342 box = carrying(ICE_BOX); 343 pick = carrying(PICK_AXE); 344 if(box || pick) { 345 if(dochug(shopkeeper)) { 346 u.uinshop = 0; /* he died moving */ 347 return(0); 348 } 349 pline("Will you please leave your %s outside?", 350 (box && pick) ? "box and pick-axe" : 351 box ? "box" : "pick-axe"); 352 } 353 } 354 u.uinshop = roomno + 1; 355 } 356 } 357 return(u.uinshop); 358 } 359 360 static void 361 findshk(int roomno) 362 { 363 struct monst *mtmp; 364 365 for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) 366 if(mtmp->isshk && ESHK(mtmp)->shoproom == roomno 367 && ESHK(mtmp)->shoplevel == dlevel) { 368 shopkeeper = mtmp; 369 bill = &(ESHK(shopkeeper)->bill[0]); 370 shlevel = dlevel; 371 if(ANGRY(shopkeeper) && 372 strncmp(ESHK(shopkeeper)->customer,plname,PL_NSIZ)) 373 NOTANGRY(shopkeeper) = 1; 374 /* billobjs = 0; -- this is wrong if we save in a shop */ 375 /* (and it is harmless to have too many things in billobjs) */ 376 return; 377 } 378 shopkeeper = 0; 379 shlevel = 0; 380 bill = (struct bill_x *) -1000; /* dump core when referenced */ 381 } 382 383 static struct bill_x * 384 onbill(struct obj *obj) 385 { 386 struct bill_x *bp; 387 388 if(!shopkeeper) return(NULL); 389 for(bp = bill; bp < &bill[ESHK(shopkeeper)->billct]; bp++) 390 if(bp->bo_id == obj->o_id) { 391 if(!obj->unpaid) pline("onbill: paid obj on bill?"); 392 return(bp); 393 } 394 if(obj->unpaid) pline("onbill: unpaid obj not on bill?"); 395 return(NULL); 396 } 397 398 /* called with two args on merge */ 399 void 400 obfree(struct obj *obj, struct obj *merge) 401 { 402 struct bill_x *bp = onbill(obj); 403 struct bill_x *bpm; 404 405 if(bp) { 406 if(!merge){ 407 bp->useup = 1; 408 obj->unpaid = 0; /* only for doinvbill */ 409 obj->nobj = billobjs; 410 billobjs = obj; 411 return; 412 } 413 bpm = onbill(merge); 414 if(!bpm){ 415 /* this used to be a rename */ 416 impossible("obfree: not on bill??"); 417 return; 418 } else { 419 /* this was a merger */ 420 bpm->bquan += bp->bquan; 421 ESHK(shopkeeper)->billct--; 422 *bp = bill[ESHK(shopkeeper)->billct]; 423 } 424 } 425 free((char *) obj); 426 } 427 428 static void 429 pay(long tmp, struct monst *shkp) 430 { 431 long robbed = ESHK(shkp)->robbed; 432 433 u.ugold -= tmp; 434 shkp->mgold += tmp; 435 flags.botl = 1; 436 if(robbed) { 437 robbed -= tmp; 438 if(robbed < 0) robbed = 0; 439 ESHK(shkp)->robbed = robbed; 440 } 441 } 442 443 int 444 dopay() 445 { 446 long ltmp; 447 struct bill_x *bp; 448 struct monst *shkp; 449 int pass, tmp; 450 451 multi = 0; 452 (void) inshop(); 453 for(shkp = fmon; shkp; shkp = shkp->nmon) 454 if(shkp->isshk && dist(shkp->mx,shkp->my) < 3) 455 break; 456 if(!shkp && u.uinshop && 457 inroom(shopkeeper->mx,shopkeeper->my) == ESHK(shopkeeper)->shoproom) 458 shkp = shopkeeper; 459 460 if(!shkp) { 461 pline("There is nobody here to receive your payment."); 462 return(0); 463 } 464 ltmp = ESHK(shkp)->robbed; 465 if(shkp != shopkeeper && NOTANGRY(shkp)) { 466 if(!ltmp) { 467 pline("You do not owe %s anything.", monnam(shkp)); 468 } else 469 if(!u.ugold) { 470 pline("You have no money."); 471 } else { 472 long ugold = u.ugold; 473 474 if(u.ugold > ltmp) { 475 pline("You give %s the %ld gold pieces he asked for.", 476 monnam(shkp), ltmp); 477 pay(ltmp, shkp); 478 } else { 479 pline("You give %s all your gold.", monnam(shkp)); 480 pay(u.ugold, shkp); 481 } 482 if(ugold < ltmp/2) { 483 pline("Unfortunately, he doesn't look satisfied."); 484 } else { 485 ESHK(shkp)->robbed = 0; 486 ESHK(shkp)->following = 0; 487 if(ESHK(shkp)->shoplevel != dlevel) { 488 /* For convenience's sake, let him disappear */ 489 shkp->minvent = 0; /* %% */ 490 shkp->mgold = 0; 491 mondead(shkp); 492 } 493 } 494 } 495 return(1); 496 } 497 498 if(!ESHK(shkp)->billct){ 499 pline("You do not owe %s anything.", monnam(shkp)); 500 if(!u.ugold){ 501 pline("Moreover, you have no money."); 502 return(1); 503 } 504 if(ESHK(shkp)->robbed){ 505 #define min(a,b) ((a<b)?a:b) 506 pline("But since his shop has been robbed recently,"); 507 pline("you %srepay %s's expenses.", 508 (u.ugold < ESHK(shkp)->robbed) ? "partially " : "", 509 monnam(shkp)); 510 pay(min(u.ugold, ESHK(shkp)->robbed), shkp); 511 ESHK(shkp)->robbed = 0; 512 return(1); 513 } 514 if(ANGRY(shkp)){ 515 pline("But in order to appease %s,", 516 amonnam(shkp, "angry")); 517 if(u.ugold >= 1000){ 518 ltmp = 1000; 519 pline(" you give him 1000 gold pieces."); 520 } else { 521 ltmp = u.ugold; 522 pline(" you give him all your money."); 523 } 524 pay(ltmp, shkp); 525 if(strncmp(ESHK(shkp)->customer, plname, PL_NSIZ) 526 || rn2(3)){ 527 pline("%s calms down.", Monnam(shkp)); 528 NOTANGRY(shkp) = 1; 529 } else 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) 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)) return(1); 548 bill[tmp] = bill[--ESHK(shopkeeper)->billct]; 549 } 550 } 551 pline("Thank you for shopping in %s's %s store!", 552 shkname(shopkeeper), 553 shopnam[rooms[ESHK(shopkeeper)->shoproom].rtype - 8]); 554 NOTANGRY(shopkeeper) = 1; 555 return(1); 556 } 557 558 /* return 1 if paid successfully */ 559 /* 0 if not enough money */ 560 /* -1 if object could not be found (but was paid) */ 561 static int 562 dopayobj(struct bill_x *bp) 563 { 564 struct obj *obj; 565 long ltmp; 566 567 /* find the object on one of the lists */ 568 obj = bp_to_obj(bp); 569 570 if(!obj) { 571 impossible("Shopkeeper administration out of order."); 572 setpaid(); /* be nice to the player */ 573 return(0); 574 } 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)) ltmp += ltmp/3; 583 if(u.ugold < ltmp){ 584 pline("You don't have gold enough to pay %s.", 585 doname(obj)); 586 obj->unpaid = 1; 587 return(0); 588 } 589 pay(ltmp, shopkeeper); 590 pline("You bought %s for %ld gold piece%s.", 591 doname(obj), ltmp, plur(ltmp)); 592 if(bp->useup) { 593 struct obj *otmp = billobjs; 594 if(obj == billobjs) 595 billobjs = obj->nobj; 596 else { 597 while(otmp && otmp->nobj != obj) otmp = otmp->nobj; 598 if(otmp) otmp->nobj = obj->nobj; 599 else pline("Error in shopkeeper administration."); 600 } 601 free((char *) obj); 602 } 603 return(1); 604 } 605 606 /* routine called after dying (or quitting) with nonempty bill */ 607 void 608 paybill() 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))) 642 break; 643 for(mtmp = fallen_down; mtmp; mtmp = mtmp->nmon) 644 if ((obj = o_on(id, mtmp->minvent))) 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 656 if(!inshop() || 657 (u.ux == ESHK(shopkeeper)->shk.x && u.uy == ESHK(shopkeeper)->shk.y) || 658 (u.ux == ESHK(shopkeeper)->shd.x && u.uy == ESHK(shopkeeper)->shd.y) || 659 onbill(obj) /* perhaps we threw it away earlier */ 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) otmp->unpaid = 0; 695 else { 696 tmp = bp->price; 697 bp = &bill[ESHK(shopkeeper)->billct]; 698 bp->bo_id = otmp->o_id; 699 bp->bquan = otmp->quan; 700 bp->useup = 0; 701 bp->price = tmp; 702 ESHK(shopkeeper)->billct++; 703 } 704 } 705 706 void 707 subfrombill(struct obj *obj) 708 { 709 long ltmp; 710 int tmp; 711 struct obj *otmp; 712 struct bill_x *bp; 713 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 ltmp /= 2; 755 if(ESHK(shopkeeper)->robbed){ 756 if((ESHK(shopkeeper)->robbed -= ltmp) < 0) 757 ESHK(shopkeeper)->robbed = 0; 758 pline("Thank you for your contribution to restock this recently plundered shop."); 759 return; 760 } 761 if(ltmp > shopkeeper->mgold) 762 ltmp = shopkeeper->mgold; 763 pay(-ltmp, shopkeeper); 764 if(!ltmp) 765 pline("%s gladly accepts %s but cannot pay you at present.", 766 Monnam(shopkeeper), doname(obj)); 767 else 768 pline("You sold %s and got %ld gold piece%s.", doname(obj), ltmp, 769 plur(ltmp)); 770 } 771 772 int 773 doinvbill(int mode) 774 /* int mode; 0: deliver count 1: paged */ 775 { 776 struct bill_x *bp; 777 struct obj *obj; 778 long totused, thisused; 779 char buf[BUFSZ]; 780 781 if(mode == 0) { 782 int cnt = 0; 783 784 if(shopkeeper) 785 for(bp = bill; bp - bill < ESHK(shopkeeper)->billct; bp++) 786 if(bp->useup || 787 ((obj = bp_to_obj(bp)) && obj->quan < bp->bquan)) 788 cnt++; 789 return(cnt); 790 } 791 792 if(!shopkeeper) { 793 impossible("doinvbill: no shopkeeper?"); 794 return(0); 795 } 796 797 set_pager(0); 798 if(page_line("Unpaid articles already used up:") || page_line("")) 799 goto quit; 800 801 totused = 0; 802 for(bp = bill; bp - bill < ESHK(shopkeeper)->billct; bp++) { 803 obj = bp_to_obj(bp); 804 if(!obj) { 805 impossible("Bad shopkeeper administration."); 806 goto quit; 807 } 808 if(bp->useup || bp->bquan > obj->quan) { 809 int cnt, oquan, uquan; 810 811 oquan = obj->quan; 812 uquan = (bp->useup ? bp->bquan : bp->bquan - oquan); 813 thisused = bp->price * uquan; 814 totused += thisused; 815 obj->quan = uquan; /* cheat doname */ 816 (void) snprintf(buf, sizeof buf, "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 tmp = 10*rnd(75); 884 break; 885 case CHAIN_SYM: 886 pline("Strange ..., carrying a chain?"); 887 case BALL_SYM: 888 tmp = 10; 889 break; 890 default: 891 tmp = 10000; 892 } 893 return(tmp); 894 } 895 896 /* not completely foolproof */ 897 static int 898 realhunger() 899 { 900 int tmp = u.uhunger; 901 struct obj *otmp = invent; 902 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 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, satdoor, avoid, 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 976 shkroom = inroom(omx,omy); 977 appr = 1; 978 gx = ESHK(shkp)->shk.x; 979 gy = ESHK(shkp)->shk.y; 980 satdoor = (gx == omx && gy == omy); 981 if(ESHK(shkp)->following || ((z = holetime()) >= 0 && z*z <= udist)){ 982 gx = u.ux; 983 gy = u.uy; 984 if(shkroom < 0 || shkroom != inroom(u.ux,u.uy)) 985 if(udist > 4) 986 return(-1); /* leave it to m_move */ 987 } else if(ANGRY(shkp)) { 988 long saveBlind = Blind; 989 Blind = 0; 990 if(shkp->mcansee && !Invis && cansee(omx,omy)) { 991 gx = u.ux; 992 gy = u.uy; 993 } 994 Blind = saveBlind; 995 avoid = FALSE; 996 } else { 997 #define GDIST(x,y) ((x-gx)*(x-gx)+(y-gy)*(y-gy)) 998 if(Invis) 999 avoid = FALSE; 1000 else { 1001 uondoor = (u.ux == ESHK(shkp)->shd.x && 1002 u.uy == ESHK(shkp)->shd.y); 1003 if(uondoor) { 1004 if(ESHK(shkp)->billct) 1005 pline("Hello %s! Will you please pay before leaving?", 1006 plname); 1007 badinv = (carrying(PICK_AXE) || carrying(ICE_BOX)); 1008 if(satdoor && badinv) 1009 return(0); 1010 avoid = !badinv; 1011 } else { 1012 avoid = (u.uinshop && dist(gx,gy) > 8); 1013 badinv = FALSE; 1014 } 1015 1016 if(((!ESHK(shkp)->robbed && !ESHK(shkp)->billct) || avoid) 1017 && GDIST(omx,omy) < 3){ 1018 if(!badinv && !online(omx,omy)) 1019 return(0); 1020 if(satdoor) 1021 appr = gx = gy = 0; 1022 } 1023 } 1024 } 1025 if(omx == gx && omy == gy) 1026 return(0); 1027 if(shkp->mconf) { 1028 avoid = FALSE; 1029 appr = 0; 1030 } 1031 nix = omx; 1032 niy = omy; 1033 cnt = mfndpos(shkp,poss,info,ALLOW_SSM); 1034 if(avoid && uondoor) { /* perhaps we cannot avoid him */ 1035 for(i=0; i<cnt; i++) 1036 if(!(info[(int)i] & NOTONL)) 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[(int)i].x; 1045 ny = poss[(int)i].y; 1046 if(levl[(int)nx][(int)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; niy = ny; chi = i; break; 1055 } 1056 if(avoid && (info[(int)i] & NOTONL)) 1057 continue; 1058 if((!appr && !rn2(++chcnt)) || 1059 #ifdef STUPID 1060 (appr && (zz = GDIST(nix,niy)) && zz > GDIST(nx,ny)) 1061 #else 1062 (appr && GDIST(nx,ny) < GDIST(nix,niy)) 1063 #endif /* STUPID */ 1064 ) { 1065 nix = nx; 1066 niy = ny; 1067 chi = i; 1068 } 1069 } 1070 } 1071 if(nix != omx || niy != omy){ 1072 if(info[(int)chi] & ALLOW_M){ 1073 mtmp = m_at(nix,niy); 1074 if(hitmm(shkp,mtmp) == 1 && rn2(3) && 1075 hitmm(mtmp,shkp) == 2) return(2); 1076 return(0); 1077 } else if(info[(int)chi] & ALLOW_U){ 1078 (void) hitu(shkp, d(mdat->damn, mdat->damd)+1); 1079 return(0); 1080 } 1081 shkp->mx = nix; 1082 shkp->my = niy; 1083 pmon(shkp); 1084 if(ib) { 1085 freeobj(ib); 1086 mpickobj(shkp, ib); 1087 } 1088 return(1); 1089 } 1090 return(0); 1091 } 1092 1093 /* He is digging in the shop. */ 1094 void 1095 shopdig(int fall) 1096 { 1097 if(!fall) { 1098 if(u.utraptype == TT_PIT) 1099 pline("\"Be careful, sir, or you might fall through the floor.\""); 1100 else 1101 pline("\"Please, do not damage the floor here.\""); 1102 } else if(dist(shopkeeper->mx, shopkeeper->my) < 3) { 1103 struct obj *obj, *obj2; 1104 1105 pline("%s grabs your backpack!", shkname(shopkeeper)); 1106 for(obj = invent; obj; obj = obj2) { 1107 obj2 = obj->nobj; 1108 if(obj->owornmask) continue; 1109 freeinv(obj); 1110 obj->nobj = shopkeeper->minvent; 1111 shopkeeper->minvent = obj; 1112 if(obj->unpaid) 1113 subfrombill(obj); 1114 } 1115 } 1116 } 1117 #endif /* QUEST */ 1118 1119 int 1120 online(int x, int y) 1121 { 1122 return(x==u.ux || y==u.uy || 1123 (x-u.ux)*(x-u.ux) == (y-u.uy)*(y-u.uy)); 1124 } 1125 1126 /* Does this monster follow me downstairs? */ 1127 int 1128 follower(struct monst *mtmp) 1129 { 1130 return( mtmp->mtame || strchr("1TVWZi&, ", mtmp->data->mlet) 1131 #ifndef QUEST 1132 || (mtmp->isshk && ESHK(mtmp)->following) 1133 #endif /* QUEST */ 1134 ); 1135 } 1136