1/* $NetBSD: hack.invent.c,v 1.17 2011/08/06 20:42:43 dholland Exp $ */ 2 3/* 4 * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica, 5 * Amsterdam 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are 10 * met: 11 * 12 * - Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * - Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * - Neither the name of the Stichting Centrum voor Wiskunde en 20 * Informatica, nor the names of its contributors may be used to endorse or 21 * promote products derived from this software without specific prior 22 * written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 25 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 27 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 28 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37/* 38 * Copyright (c) 1982 Jay Fenlason <hack@gnu.org> 39 * All rights reserved. 40 * 41 * Redistribution and use in source and binary forms, with or without 42 * modification, are permitted provided that the following conditions 43 * are met: 44 * 1. Redistributions of source code must retain the above copyright 45 * notice, this list of conditions and the following disclaimer. 46 * 2. Redistributions in binary form must reproduce the above copyright 47 * notice, this list of conditions and the following disclaimer in the 48 * documentation and/or other materials provided with the distribution. 49 * 3. The name of the author may not be used to endorse or promote products 50 * derived from this software without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 53 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 54 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 55 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 56 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 57 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 58 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 59 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 60 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 61 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 62 */ 63 64#include <sys/cdefs.h> 65#ifndef lint 66__RCSID("$NetBSD: hack.invent.c,v 1.17 2011/08/06 20:42:43 dholland Exp $"); 67#endif /* not lint */ 68 69#include <assert.h> 70#include <stdlib.h> 71#include "hack.h" 72#include "extern.h" 73 74#ifndef NOWORM 75#include "def.wseg.h" 76#endif /* NOWORM */ 77 78#define NOINVSYM '#' 79 80static int lastinvnr = 51; /* 0 ... 51 */ 81 82static char *xprname(struct obj *, char); 83static void doinv(const char *); 84static int merged(struct obj *, struct obj *, int); 85 86static void 87assigninvlet(struct obj *otmp) 88{ 89 boolean inuse[52]; 90 int i; 91 struct obj *obj; 92 93 for (i = 0; i < 52; i++) 94 inuse[i] = FALSE; 95 for (obj = invent; obj; obj = obj->nobj) 96 if (obj != otmp) { 97 i = obj->invlet; 98 if ('a' <= i && i <= 'z') 99 inuse[i - 'a'] = TRUE; 100 else if ('A' <= i && i <= 'Z') 101 inuse[i - 'A' + 26] = TRUE; 102 if (i == otmp->invlet) 103 otmp->invlet = 0; 104 } 105 if ((i = otmp->invlet) && 106 (('a' <= i && i <= 'z') || ('A' <= i && i <= 'Z'))) 107 return; 108 for (i = lastinvnr + 1; i != lastinvnr; i++) { 109 if (i == 52) { 110 i = -1; 111 continue; 112 } 113 if (!inuse[i]) 114 break; 115 } 116 otmp->invlet = (inuse[i] ? NOINVSYM : 117 (i < 26) ? ('a' + i) : ('A' + i - 26)); 118 lastinvnr = i; 119} 120 121struct obj * 122addinv(struct obj *obj) 123{ 124 struct obj *otmp; 125 126 /* merge or attach to end of chain */ 127 if (!invent) { 128 invent = obj; 129 otmp = 0; 130 } else 131 for (otmp = invent; /* otmp */ ; otmp = otmp->nobj) { 132 if (merged(otmp, obj, 0)) 133 return (otmp); 134 if (!otmp->nobj) { 135 otmp->nobj = obj; 136 break; 137 } 138 } 139 obj->nobj = 0; 140 141 if (flags.invlet_constant) { 142 assigninvlet(obj); 143 /* 144 * The ordering of the chain is nowhere significant 145 * so in case you prefer some other order than the 146 * historical one, change the code below. 147 */ 148 if (otmp) { /* find proper place in chain */ 149 otmp->nobj = 0; 150 if ((invent->invlet ^ 040) > (obj->invlet ^ 040)) { 151 obj->nobj = invent; 152 invent = obj; 153 } else 154 for (otmp = invent;; otmp = otmp->nobj) { 155 if (!otmp->nobj || 156 (otmp->nobj->invlet ^ 040) > (obj->invlet ^ 040)) { 157 obj->nobj = otmp->nobj; 158 otmp->nobj = obj; 159 break; 160 } 161 } 162 } 163 } 164 return (obj); 165} 166 167void 168useup(struct obj *obj) 169{ 170 if (obj->quan > 1) { 171 obj->quan--; 172 obj->owt = weight(obj); 173 } else { 174 setnotworn(obj); 175 freeinv(obj); 176 obfree(obj, (struct obj *) 0); 177 } 178} 179 180void 181freeinv(struct obj *obj) 182{ 183 struct obj *otmp; 184 185 if (obj == invent) 186 invent = invent->nobj; 187 else { 188 for (otmp = invent; otmp->nobj != obj; otmp = otmp->nobj) 189 if (!otmp->nobj) 190 panic("freeinv"); 191 otmp->nobj = obj->nobj; 192 } 193} 194 195/* destroy object in fobj chain (if unpaid, it remains on the bill) */ 196void 197delobj(struct obj *obj) 198{ 199 freeobj(obj); 200 unpobj(obj); 201 obfree(obj, (struct obj *) 0); 202} 203 204/* unlink obj from chain starting with fobj */ 205void 206freeobj(struct obj *obj) 207{ 208 struct obj *otmp; 209 210 if (obj == fobj) 211 fobj = fobj->nobj; 212 else { 213 otmp = fobj; 214 while (otmp->nobj != obj) { 215 if (otmp->nobj == NULL) 216 panic("error in freeobj"); 217 otmp = otmp->nobj; 218 } 219 otmp->nobj = obj->nobj; 220 } 221} 222 223/* Note: freegold throws away its argument! */ 224void 225freegold(struct gold *gold) 226{ 227 struct gold *gtmp; 228 229 if (gold == fgold) 230 fgold = gold->ngold; 231 else { 232 gtmp = fgold; 233 while (gtmp->ngold != gold) { 234 if (gtmp->ngold == NULL) 235 panic("error in freegold"); 236 gtmp = gtmp->ngold; 237 } 238 gtmp->ngold = gold->ngold; 239 } 240 free(gold); 241} 242 243void 244deltrap(struct trap *trap) 245{ 246 struct trap *ttmp; 247 248 if (trap == ftrap) 249 ftrap = ftrap->ntrap; 250 else { 251 for (ttmp = ftrap; ttmp->ntrap != trap; ttmp = ttmp->ntrap); 252 ttmp->ntrap = trap->ntrap; 253 } 254 free(trap); 255} 256 257struct wseg *m_atseg; 258 259struct monst * 260m_at(int x, int y) 261{ 262 struct monst *mtmp; 263#ifndef NOWORM 264 struct wseg *wtmp; 265#endif /* NOWORM */ 266 267 m_atseg = 0; 268 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { 269 if (mtmp->mx == x && mtmp->my == y) 270 return (mtmp); 271#ifndef NOWORM 272 if (mtmp->wormno) { 273 for (wtmp = wsegs[mtmp->wormno]; wtmp; wtmp = wtmp->nseg) 274 if (wtmp->wx == x && wtmp->wy == y) { 275 m_atseg = wtmp; 276 return (mtmp); 277 } 278 } 279#endif /* NOWORM */ 280 } 281 return (0); 282} 283 284struct obj * 285o_at(int x, int y) 286{ 287 struct obj *otmp; 288 289 for (otmp = fobj; otmp; otmp = otmp->nobj) 290 if (otmp->ox == x && otmp->oy == y) 291 return (otmp); 292 return (0); 293} 294 295struct obj * 296sobj_at(int n, int x, int y) 297{ 298 struct obj *otmp; 299 300 for (otmp = fobj; otmp; otmp = otmp->nobj) 301 if (otmp->ox == x && otmp->oy == y && otmp->otyp == n) 302 return (otmp); 303 return (0); 304} 305 306int 307carried(struct obj *obj) 308{ 309 struct obj *otmp; 310 for (otmp = invent; otmp; otmp = otmp->nobj) 311 if (otmp == obj) 312 return (1); 313 return (0); 314} 315 316int 317carrying(int type) 318{ 319 struct obj *otmp; 320 321 for (otmp = invent; otmp; otmp = otmp->nobj) 322 if (otmp->otyp == type) 323 return (TRUE); 324 return (FALSE); 325} 326 327struct obj * 328o_on(unsigned int id, struct obj *objchn) 329{ 330 while (objchn) { 331 if (objchn->o_id == id) 332 return (objchn); 333 objchn = objchn->nobj; 334 } 335 return ((struct obj *) 0); 336} 337 338struct trap * 339t_at(int x, int y) 340{ 341 struct trap *trap = ftrap; 342 while (trap) { 343 if (trap->tx == x && trap->ty == y) 344 return (trap); 345 trap = trap->ntrap; 346 } 347 return (0); 348} 349 350struct gold * 351g_at(int x, int y) 352{ 353 struct gold *gold = fgold; 354 while (gold) { 355 if (gold->gx == x && gold->gy == y) 356 return (gold); 357 gold = gold->ngold; 358 } 359 return (0); 360} 361 362/* make dummy object structure containing gold - for temporary use only */ 363static struct obj * 364mkgoldobj(long q) 365{ 366 struct obj *otmp; 367 368 otmp = newobj(0); 369 /* should set o_id etc. but otmp will be freed soon */ 370 otmp->olet = '$'; 371 u.ugold -= q; 372 OGOLD(otmp) = q; 373 flags.botl = 1; 374 return (otmp); 375} 376 377/* 378 * getobj returns: 379 * struct obj *xxx: object to do something with. 380 * (struct obj *) 0 error return: no object. 381 * &zeroobj explicitly no object (as in w-). 382 */ 383struct obj * 384getobj(const char *let, const char *word) 385{ 386 struct obj *otmp; 387 char ilet, ilet1, ilet2; 388 char buf[BUFSZ]; 389 char lets[BUFSZ]; 390 int foo = 0, foo2; 391 char *bp = buf; 392 xchar allowcnt = 0; /* 0, 1 or 2 */ 393 boolean allowgold = FALSE; 394 boolean allowall = FALSE; 395 boolean allownone = FALSE; 396 xchar foox = 0; 397 long cnt; 398 399 if (*let == '0') 400 let++, allowcnt = 1; 401 if (*let == '$') 402 let++, allowgold = TRUE; 403 if (*let == '#') 404 let++, allowall = TRUE; 405 if (*let == '-') 406 let++, allownone = TRUE; 407 if (allownone) 408 *bp++ = '-'; 409 if (allowgold) 410 *bp++ = '$'; 411 if (bp > buf && bp[-1] == '-') 412 *bp++ = ' '; 413 414 ilet = 'a'; 415 for (otmp = invent; otmp; otmp = otmp->nobj) { 416 if (!*let || strchr(let, otmp->olet)) { 417 bp[foo++] = flags.invlet_constant ? otmp->invlet : ilet; 418 419 /* ugly check: remove inappropriate things */ 420 if ((!strcmp(word, "take off") && 421 !(otmp->owornmask & (W_ARMOR - W_ARM2))) 422 || (!strcmp(word, "wear") && 423 (otmp->owornmask & (W_ARMOR | W_RING))) 424 || (!strcmp(word, "wield") && 425 (otmp->owornmask & W_WEP))) { 426 foo--; 427 foox++; 428 } 429 } 430 if (ilet == 'z') 431 ilet = 'A'; 432 else 433 ilet++; 434 } 435 bp[foo] = 0; 436 if (foo == 0 && bp > buf && bp[-1] == ' ') 437 *--bp = 0; 438 (void) strcpy(lets, bp);/* necessary since we destroy buf */ 439 if (foo > 5) { /* compactify string */ 440 foo = foo2 = 1; 441 ilet2 = bp[0]; 442 ilet1 = bp[1]; 443 while ((ilet = bp[++foo2] = bp[++foo]) != '\0') { 444 if (ilet == ilet1 + 1) { 445 if (ilet1 == ilet2 + 1) 446 bp[foo2 - 1] = ilet1 = '-'; 447 else if (ilet2 == '-') { 448 bp[--foo2] = ++ilet1; 449 continue; 450 } 451 } 452 ilet2 = ilet1; 453 ilet1 = ilet; 454 } 455 } 456 if (!foo && !allowall && !allowgold && !allownone) { 457 pline("You don't have anything %sto %s.", 458 foox ? "else " : "", word); 459 return (0); 460 } 461 for (;;) { 462 if (!buf[0]) 463 pline("What do you want to %s [*]? ", word); 464 else 465 pline("What do you want to %s [%s or ?*]? ", 466 word, buf); 467 468 cnt = 0; 469 ilet = readchar(); 470 while (digit(ilet) && allowcnt) { 471 if (cnt < 100000000) 472 cnt = 10 * cnt + (ilet - '0'); 473 else 474 cnt = 999999999; 475 allowcnt = 2; /* signal presence of cnt */ 476 ilet = readchar(); 477 } 478 if (digit(ilet)) { 479 pline("No count allowed with this command."); 480 continue; 481 } 482 if (strchr(quitchars, ilet)) 483 return ((struct obj *) 0); 484 if (ilet == '-') { 485 return (allownone ? &zeroobj : (struct obj *) 0); 486 } 487 if (ilet == '$') { 488 if (!allowgold) { 489 pline("You cannot %s gold.", word); 490 continue; 491 } 492 if (!(allowcnt == 2 && cnt < u.ugold)) 493 cnt = u.ugold; 494 return (mkgoldobj(cnt)); 495 } 496 if (ilet == '?') { 497 doinv(lets); 498 if (!(ilet = morc)) 499 continue; 500 /* he typed a letter (not a space) to more() */ 501 } else if (ilet == '*') { 502 doinv(NULL); 503 if (!(ilet = morc)) 504 continue; 505 /* ... */ 506 } 507 if (flags.invlet_constant) { 508 for (otmp = invent; otmp; otmp = otmp->nobj) 509 if (otmp->invlet == ilet) 510 break; 511 } else { 512 if (ilet >= 'A' && ilet <= 'Z') 513 ilet += 'z' - 'A' + 1; 514 ilet -= 'a'; 515 for (otmp = invent; otmp && ilet; 516 ilet--, otmp = otmp->nobj); 517 } 518 if (!otmp) { 519 pline("You don't have that object."); 520 continue; 521 } 522 if (cnt < 0 || otmp->quan < cnt) { 523 pline("You don't have that many! [You have %u]" 524 ,otmp->quan); 525 continue; 526 } 527 break; 528 } 529 if (!allowall && let && !strchr(let, otmp->olet)) { 530 pline("That is a silly thing to %s.", word); 531 return (0); 532 } 533 if (allowcnt == 2) { /* cnt given */ 534 if (cnt == 0) 535 return (0); 536 if (cnt != otmp->quan) { 537 struct obj *obj; 538 obj = splitobj(otmp, (int) cnt); 539 if (otmp == uwep) 540 setuwep(obj); 541 } 542 } 543 return (otmp); 544} 545 546static int 547ckunpaid(struct obj *otmp) 548{ 549 return (otmp->unpaid); 550} 551 552/* interactive version of getobj - used for Drop and Identify */ 553/* return the number of times fn was called successfully */ 554int 555ggetobj(const char *word, int (*fn)(struct obj *), int max) 556{ 557 char buf[BUFSZ]; 558 char *ip; 559 char sym; 560 unsigned oletct = 0, iletct = 0; 561 boolean allflag = FALSE; 562 char olets[20], ilets[20]; 563 int (*ckfn)(struct obj *) = 564 (int (*)(struct obj *)) 0; 565 xchar allowgold = (u.ugold && !strcmp(word, "drop")) ? 1 : 0; /* BAH */ 566 if (!invent && !allowgold) { 567 pline("You have nothing to %s.", word); 568 return (0); 569 } else { 570 struct obj *otmp = invent; 571 int uflg = 0; 572 573 if (allowgold) 574 ilets[iletct++] = '$'; 575 ilets[iletct] = 0; 576 while (otmp) { 577 if (!strchr(ilets, otmp->olet)) { 578 ilets[iletct++] = otmp->olet; 579 ilets[iletct] = 0; 580 } 581 if (otmp->unpaid) 582 uflg = 1; 583 otmp = otmp->nobj; 584 } 585 ilets[iletct++] = ' '; 586 if (uflg) 587 ilets[iletct++] = 'u'; 588 if (invent) 589 ilets[iletct++] = 'a'; 590 ilets[iletct] = 0; 591 assert(iletct < sizeof(ilets)); 592 } 593 pline("What kinds of thing do you want to %s? [%s] ", 594 word, ilets); 595 getlin(buf); 596 if (buf[0] == '\033') { 597 clrlin(); 598 return (0); 599 } 600 ip = buf; 601 olets[0] = 0; 602 while ((sym = *ip++) != '\0') { 603 if (sym == ' ') 604 continue; 605 if (sym == '$') { 606 if (allowgold == 1) 607 (*fn) (mkgoldobj(u.ugold)); 608 else if (!u.ugold) 609 pline("You have no gold."); 610 allowgold = 2; 611 } else if (sym == 'a' || sym == 'A') 612 allflag = TRUE; 613 else if (sym == 'u' || sym == 'U') 614 ckfn = ckunpaid; 615 else if (strchr("!%?[()=*/\"0", sym)) { 616 if (!strchr(olets, sym)) { 617 olets[oletct++] = sym; 618 olets[oletct] = 0; 619 } 620 assert(oletct < sizeof(olets)); 621 } else 622 pline("You don't have any %c's.", sym); 623 } 624 if (allowgold == 2 && !oletct) 625 return (1); /* he dropped gold (or at least tried to) */ 626 else 627 return (askchain(invent, olets, allflag, fn, ckfn, max)); 628} 629 630/* 631 * Walk through the chain starting at objchn and ask for all objects 632 * with olet in olets (if nonNULL) and satisfying ckfn (if nonNULL) 633 * whether the action in question (i.e., fn) has to be performed. 634 * If allflag then no questions are asked. Max gives the max nr of 635 * objects to be treated. Return the number of objects treated. 636 */ 637int 638askchain(struct obj *objchn, char *olets, int allflag, 639 int (*fn)(struct obj *), 640 int (*ckfn)(struct obj *), 641 int max) 642{ 643 struct obj *otmp, *otmp2; 644 char sym, ilet; 645 int cnt = 0; 646 ilet = 'a' - 1; 647 for (otmp = objchn; otmp; otmp = otmp2) { 648 if (ilet == 'z') 649 ilet = 'A'; 650 else 651 ilet++; 652 otmp2 = otmp->nobj; 653 if (olets && *olets && !strchr(olets, otmp->olet)) 654 continue; 655 if (ckfn && !(*ckfn) (otmp)) 656 continue; 657 if (!allflag) { 658 pline("%s", xprname(otmp, ilet)); 659 addtopl(" [nyaq]? "); 660 sym = readchar(); 661 } else 662 sym = 'y'; 663 664 switch (sym) { 665 case 'a': 666 allflag = 1; 667 /* FALLTHROUGH */ 668 case 'y': 669 cnt += (*fn) (otmp); 670 if (--max == 0) 671 goto ret; 672 break; 673 case 'n': 674 default: 675 break; 676 case 'q': 677 goto ret; 678 } 679 } 680 pline(cnt ? "That was all." : "No applicable objects."); 681ret: 682 return (cnt); 683} 684 685/* should of course only be called for things in invent */ 686static char 687obj_to_let(struct obj *obj) 688{ 689 struct obj *otmp; 690 char ilet; 691 692 if (flags.invlet_constant) 693 return (obj->invlet); 694 ilet = 'a'; 695 for (otmp = invent; otmp && otmp != obj; otmp = otmp->nobj) 696 if (++ilet > 'z') 697 ilet = 'A'; 698 return (otmp ? ilet : NOINVSYM); 699} 700 701void 702prinv(struct obj *obj) 703{ 704 pline("%s", xprname(obj, obj_to_let(obj))); 705} 706 707static char * 708xprname(struct obj *obj, char let) 709{ 710 static char li[BUFSZ]; 711 712 (void) snprintf(li, sizeof(li), "%c - %s.", 713 flags.invlet_constant ? obj->invlet : let, 714 doname(obj)); 715 return (li); 716} 717 718int 719ddoinv(void) 720{ 721 doinv(NULL); 722 return (0); 723} 724 725/* called with 0 or "": all objects in inventory */ 726/* otherwise: all objects with (serial) letter in lets */ 727static void 728doinv(const char *lets) 729{ 730 struct obj *otmp; 731 char ilet; 732 unsigned ct = 0; 733 char any[BUFSZ]; 734 735 morc = 0; /* just to be sure */ 736 737 if (!invent) { 738 pline("Not carrying anything."); 739 return; 740 } 741 cornline(0, NULL); 742 ilet = 'a'; 743 for (otmp = invent; otmp; otmp = otmp->nobj) { 744 if (flags.invlet_constant) 745 ilet = otmp->invlet; 746 if (!lets || !*lets || strchr(lets, ilet)) { 747 cornline(1, xprname(otmp, ilet)); 748 any[ct++] = ilet; 749 } 750 if (!flags.invlet_constant) 751 if (++ilet > 'z') 752 ilet = 'A'; 753 } 754 any[ct] = 0; 755 assert(ct < sizeof(any)); 756 cornline(2, any); 757} 758 759int 760dotypeinv(void) 761{ /* free after Robert Viduya */ 762 /* Changed to one type only, so he doesnt have to type cr */ 763 char c, ilet; 764 char stuff[BUFSZ]; 765 unsigned stct; 766 struct obj *otmp; 767 boolean billx = inshop() && doinvbill(0); 768 boolean unpd = FALSE; 769 770 if (!invent && !u.ugold && !billx) { 771 pline("You aren't carrying anything."); 772 return (0); 773 } 774 stct = 0; 775 if (u.ugold) 776 stuff[stct++] = '$'; 777 stuff[stct] = 0; 778 for (otmp = invent; otmp; otmp = otmp->nobj) { 779 if (!strchr(stuff, otmp->olet)) { 780 stuff[stct++] = otmp->olet; 781 stuff[stct] = 0; 782 } 783 if (otmp->unpaid) 784 unpd = TRUE; 785 } 786 if (unpd) 787 stuff[stct++] = 'u'; 788 if (billx) 789 stuff[stct++] = 'x'; 790 stuff[stct] = 0; 791 assert(stct < sizeof(stuff)); 792 793 if (stct > 1) { 794 pline("What type of object [%s] do you want an inventory of? ", 795 stuff); 796 c = readchar(); 797 if (strchr(quitchars, c)) 798 return (0); 799 } else 800 c = stuff[0]; 801 802 if (c == '$') 803 return (doprgold()); 804 805 if (c == 'x' || c == 'X') { 806 if (billx) 807 (void) doinvbill(1); 808 else 809 pline("No used-up objects on the shopping bill."); 810 return (0); 811 } 812 if ((c == 'u' || c == 'U') && !unpd) { 813 pline("You are not carrying any unpaid objects."); 814 return (0); 815 } 816 stct = 0; 817 ilet = 'a'; 818 for (otmp = invent; otmp; otmp = otmp->nobj) { 819 if (flags.invlet_constant) 820 ilet = otmp->invlet; 821 if (c == otmp->olet || (c == 'u' && otmp->unpaid)) 822 stuff[stct++] = ilet; 823 if (!flags.invlet_constant) 824 if (++ilet > 'z') 825 ilet = 'A'; 826 } 827 stuff[stct] = '\0'; 828 assert(stct < sizeof(stuff)); 829 830 if (stct == 0) 831 pline("You have no such objects."); 832 else 833 doinv(stuff); 834 835 return (0); 836} 837 838/* look at what is here */ 839int 840dolook(void) 841{ 842 struct obj *otmp = NULL, *otmp0 = NULL; 843 struct gold *gold = NULL; 844 const char *verb = Blind ? "feel" : "see"; 845 int ct = 0; 846 847 if (!u.uswallow) { 848 if (Blind) { 849 pline("You try to feel what is lying here on the floor."); 850 if (Levitation) { /* ab@unido */ 851 pline("You cannot reach the floor!"); 852 return (1); 853 } 854 } 855 otmp0 = o_at(u.ux, u.uy); 856 gold = g_at(u.ux, u.uy); 857 } 858 if (u.uswallow || (!otmp0 && !gold)) { 859 pline("You %s no objects here.", verb); 860 return (!!Blind); 861 } 862 cornline(0, "Things that are here:"); 863 for (otmp = otmp0; otmp; otmp = otmp->nobj) { 864 if (otmp->ox == u.ux && otmp->oy == u.uy) { 865 ct++; 866 cornline(1, doname(otmp)); 867 if (Blind && otmp->otyp == DEAD_COCKATRICE && !uarmg) { 868 pline("Touching the dead cockatrice is a fatal mistake ..."); 869 pline("You die ..."); 870 killer = "dead cockatrice"; 871 done("died"); 872 } 873 } 874 } 875 876 if (gold) { 877 char gbuf[30]; 878 879 (void) snprintf(gbuf, sizeof(gbuf), "%ld gold piece%s", 880 gold->amount, plur(gold->amount)); 881 if (!ct++) 882 pline("You %s here %s.", verb, gbuf); 883 else 884 cornline(1, gbuf); 885 } 886 if (ct == 1 && !gold) { 887 pline("You %s here %s.", verb, doname(otmp0)); 888 cornline(3, NULL); 889 } 890 if (ct > 1) 891 cornline(2, NULL); 892 return (!!Blind); 893} 894 895void 896stackobj(struct obj *obj) 897{ 898 struct obj *otmp = fobj; 899 for (otmp = fobj; otmp; otmp = otmp->nobj) 900 if (otmp != obj) 901 if (otmp->ox == obj->ox && otmp->oy == obj->oy && 902 merged(obj, otmp, 1)) 903 return; 904} 905 906/* merge obj with otmp and delete obj if types agree */ 907static int 908merged(struct obj *otmp, struct obj *obj, int lose) 909{ 910 if (obj->otyp == otmp->otyp && 911 obj->unpaid == otmp->unpaid && 912 obj->spe == otmp->spe && 913 obj->dknown == otmp->dknown && 914 obj->cursed == otmp->cursed && 915 (strchr("%*?!", obj->olet) || 916 (obj->known == otmp->known && 917 (obj->olet == WEAPON_SYM && obj->otyp < BOOMERANG)))) { 918 otmp->quan += obj->quan; 919 otmp->owt += obj->owt; 920 if (lose) 921 freeobj(obj); 922 obfree(obj, otmp); /* free(obj), bill->otmp */ 923 return (1); 924 } else 925 return (0); 926} 927 928static long goldcounted; 929/* 930 * Gold is no longer displayed; in fact, when you have a lot of money, 931 * it may take a while before you have counted it all. 932 * [Bug: d$ and pickup still tell you how much it was.] 933 */ 934static int 935countgold(void) 936{ 937 if ((goldcounted += 100 * (u.ulevel + 1)) >= u.ugold) { 938 long eps = 0; 939 if (!rn2(2)) 940 eps = rnd((int) (u.ugold / 100 + 1)); 941 pline("You probably have about %ld gold pieces.", 942 u.ugold + eps); 943 return (0); /* done */ 944 } 945 return (1); /* continue */ 946} 947 948int 949doprgold(void) 950{ 951 if (!u.ugold) 952 pline("You do not carry any gold."); 953 else if (u.ugold <= 500) 954 pline("You are carrying %ld gold pieces.", u.ugold); 955 else { 956 pline("You sit down in order to count your gold pieces."); 957 goldcounted = 500; 958 occupation = countgold; 959 occtxt = "counting your gold"; 960 } 961 return (1); 962} 963 964/* --- end of gold counting section --- */ 965int 966doprwep(void) 967{ 968 if (!uwep) 969 pline("You are empty handed."); 970 else 971 prinv(uwep); 972 return (0); 973} 974 975int 976doprarm(void) 977{ 978 if (!uarm && !uarmg && !uarms && !uarmh) 979 pline("You are not wearing any armor."); 980 else { 981 char lets[6]; 982 int ct = 0; 983 984 if (uarm) 985 lets[ct++] = obj_to_let(uarm); 986 if (uarm2) 987 lets[ct++] = obj_to_let(uarm2); 988 if (uarmh) 989 lets[ct++] = obj_to_let(uarmh); 990 if (uarms) 991 lets[ct++] = obj_to_let(uarms); 992 if (uarmg) 993 lets[ct++] = obj_to_let(uarmg); 994 lets[ct] = 0; 995 doinv(lets); 996 } 997 return (0); 998} 999 1000int 1001doprring(void) 1002{ 1003 if (!uleft && !uright) 1004 pline("You are not wearing any rings."); 1005 else { 1006 char lets[3]; 1007 int ct = 0; 1008 1009 if (uleft) 1010 lets[ct++] = obj_to_let(uleft); 1011 if (uright) 1012 lets[ct++] = obj_to_let(uright); 1013 lets[ct] = 0; 1014 doinv(lets); 1015 } 1016 return (0); 1017} 1018 1019int 1020digit(int c) 1021{ 1022 return (c >= '0' && c <= '9'); 1023} 1024