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