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