1/* SCCS Id: @(#)invent.c 3.4 2003/12/02 */ 2/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3/* NetHack may be freely redistributed. See license for details. */ 4 5#include "hack.h" 6 7#define NOINVSYM '#' 8#define CONTAINED_SYM '>' /* designator for inside a container */ 9 10#ifdef OVL1 11STATIC_DCL void NDECL(reorder_invent); 12STATIC_DCL boolean FDECL(mergable,(struct obj *,struct obj *)); 13STATIC_DCL void FDECL(invdisp_nothing, (const char *,const char *)); 14STATIC_DCL boolean FDECL(worn_wield_only, (struct obj *)); 15STATIC_DCL boolean FDECL(only_here, (struct obj *)); 16#endif /* OVL1 */ 17STATIC_DCL void FDECL(compactify,(char *)); 18STATIC_DCL boolean FDECL(taking_off, (const char *)); 19STATIC_DCL boolean FDECL(putting_on, (const char *)); 20STATIC_PTR int FDECL(ckunpaid,(struct obj *)); 21STATIC_PTR int FDECL(ckvalidcat,(struct obj *)); 22static char FDECL(display_pickinv, (const char *,BOOLEAN_P, long *)); 23#ifdef OVLB 24STATIC_DCL boolean FDECL(this_type_only, (struct obj *)); 25STATIC_DCL void NDECL(dounpaid); 26STATIC_DCL struct obj *FDECL(find_unpaid,(struct obj *,struct obj **)); 27STATIC_DCL void FDECL(menu_identify, (int)); 28STATIC_DCL boolean FDECL(tool_in_use, (struct obj *)); 29#endif /* OVLB */ 30STATIC_DCL char FDECL(obj_to_let,(struct obj *)); 31 32#ifdef OVLB 33 34static int lastinvnr = 51; /* 0 ... 51 (never saved&restored) */ 35 36#ifdef WIZARD 37/* wizards can wish for venom, which will become an invisible inventory 38 * item without this. putting it in inv_order would mean venom would 39 * suddenly become a choice for all the inventory-class commands, which 40 * would probably cause mass confusion. the test for inventory venom 41 * is only WIZARD and not wizard because the wizard can leave venom lying 42 * around on a bones level for normal players to find. 43 */ 44static char venom_inv[] = { VENOM_CLASS, 0 }; /* (constant) */ 45#endif 46 47void 48assigninvlet(otmp) 49register struct obj *otmp; 50{ 51 boolean inuse[52]; 52 register int i; 53 register struct obj *obj; 54 55#ifdef GOLDOBJ 56 /* There is only one of these in inventory... */ 57 if (otmp->oclass == COIN_CLASS) { 58 otmp->invlet = GOLD_SYM; 59 return; 60 } 61#endif 62 63 for(i = 0; i < 52; i++) inuse[i] = FALSE; 64 for(obj = invent; obj; obj = obj->nobj) if(obj != otmp) { 65 i = obj->invlet; 66 if('a' <= i && i <= 'z') inuse[i - 'a'] = TRUE; else 67 if('A' <= i && i <= 'Z') inuse[i - 'A' + 26] = TRUE; 68 if(i == otmp->invlet) otmp->invlet = 0; 69 } 70 if((i = otmp->invlet) && 71 (('a' <= i && i <= 'z') || ('A' <= i && i <= 'Z'))) 72 return; 73 for(i = lastinvnr+1; i != lastinvnr; i++) { 74 if(i == 52) { i = -1; continue; } 75 if(!inuse[i]) break; 76 } 77 otmp->invlet = (inuse[i] ? NOINVSYM : 78 (i < 26) ? ('a'+i) : ('A'+i-26)); 79 lastinvnr = i; 80} 81 82#endif /* OVLB */ 83#ifdef OVL1 84 85/* note: assumes ASCII; toggling a bit puts lowercase in front of uppercase */ 86#define inv_rank(o) ((o)->invlet ^ 040) 87 88/* sort the inventory; used by addinv() and doorganize() */ 89STATIC_OVL void 90reorder_invent() 91{ 92 struct obj *otmp, *prev, *next; 93 boolean need_more_sorting; 94 95 do { 96 /* 97 * We expect at most one item to be out of order, so this 98 * isn't nearly as inefficient as it may first appear. 99 */ 100 need_more_sorting = FALSE; 101 for (otmp = invent, prev = 0; otmp; ) { 102 next = otmp->nobj; 103 if (next && inv_rank(next) < inv_rank(otmp)) { 104 need_more_sorting = TRUE; 105 if (prev) prev->nobj = next; 106 else invent = next; 107 otmp->nobj = next->nobj; 108 next->nobj = otmp; 109 prev = next; 110 } else { 111 prev = otmp; 112 otmp = next; 113 } 114 } 115 } while (need_more_sorting); 116} 117 118#undef inv_rank 119 120/* scan a list of objects to see whether another object will merge with 121 one of them; used in pickup.c when all 52 inventory slots are in use, 122 to figure out whether another object could still be picked up */ 123struct obj * 124merge_choice(objlist, obj) 125struct obj *objlist, *obj; 126{ 127 struct monst *shkp; 128 int save_nocharge; 129 130 if (obj->otyp == SCR_SCARE_MONSTER) /* punt on these */ 131 return (struct obj *)0; 132 /* if this is an item on the shop floor, the attributes it will 133 have when carried are different from what they are now; prevent 134 that from eliciting an incorrect result from mergable() */ 135 save_nocharge = obj->no_charge; 136 if (objlist == invent && obj->where == OBJ_FLOOR && 137 (shkp = shop_keeper(inside_shop(obj->ox, obj->oy))) != 0) { 138 if (obj->no_charge) obj->no_charge = 0; 139 /* A billable object won't have its `unpaid' bit set, so would 140 erroneously seem to be a candidate to merge with a similar 141 ordinary object. That's no good, because once it's really 142 picked up, it won't merge after all. It might merge with 143 another unpaid object, but we can't check that here (depends 144 too much upon shk's bill) and if it doesn't merge it would 145 end up in the '#' overflow inventory slot, so reject it now. */ 146 else if (inhishop(shkp)) return (struct obj *)0; 147 } 148 while (objlist) { 149 if (mergable(objlist, obj)) break; 150 objlist = objlist->nobj; 151 } 152 obj->no_charge = save_nocharge; 153 return objlist; 154} 155 156/* merge obj with otmp and delete obj if types agree */ 157int 158merged(potmp, pobj) 159struct obj **potmp, **pobj; 160{ 161 register struct obj *otmp = *potmp, *obj = *pobj; 162 163 if(mergable(otmp, obj)) { 164 /* Approximate age: we do it this way because if we were to 165 * do it "accurately" (merge only when ages are identical) 166 * we'd wind up never merging any corpses. 167 * otmp->age = otmp->age*(1-proportion) + obj->age*proportion; 168 * 169 * Don't do the age manipulation if lit. We would need 170 * to stop the burn on both items, then merge the age, 171 * then restart the burn. 172 */ 173 if (!obj->lamplit) 174 otmp->age = ((otmp->age*otmp->quan) + (obj->age*obj->quan)) 175 / (otmp->quan + obj->quan); 176 177 otmp->quan += obj->quan; 178#ifdef GOLDOBJ 179 /* temporary special case for gold objects!!!! */ 180#endif 181 if (otmp->oclass == COIN_CLASS) otmp->owt = weight(otmp); 182 else otmp->owt += obj->owt; 183 if(!otmp->onamelth && obj->onamelth) 184 otmp = *potmp = oname(otmp, ONAME(obj)); 185 obj_extract_self(obj); 186 187 /* really should merge the timeouts */ 188 if (obj->lamplit) obj_merge_light_sources(obj, otmp); 189 if (obj->timed) obj_stop_timers(obj); /* follows lights */ 190 191 /* fixup for `#adjust' merging wielded darts, daggers, &c */ 192 if (obj->owornmask && carried(otmp)) { 193 long wmask = otmp->owornmask | obj->owornmask; 194 195 /* Both the items might be worn in competing slots; 196 merger preference (regardless of which is which): 197 primary weapon + alternate weapon -> primary weapon; 198 primary weapon + quiver -> primary weapon; 199 alternate weapon + quiver -> alternate weapon. 200 (Prior to 3.3.0, it was not possible for the two 201 stacks to be worn in different slots and `obj' 202 didn't need to be unworn when merging.) */ 203 if (wmask & W_WEP) wmask = W_WEP; 204 else if (wmask & W_SWAPWEP) wmask = W_SWAPWEP; 205 else if (wmask & W_QUIVER) wmask = W_QUIVER; 206 else { 207 impossible("merging strangely worn items (%lx)", wmask); 208 wmask = otmp->owornmask; 209 } 210 if ((otmp->owornmask & ~wmask) != 0L) setnotworn(otmp); 211 setworn(otmp, wmask); 212 setnotworn(obj); 213 } 214#if 0 215 /* (this should not be necessary, since items 216 already in a monster's inventory don't ever get 217 merged into other objects [only vice versa]) */ 218 else if (obj->owornmask && mcarried(otmp)) { 219 if (obj == MON_WEP(otmp->ocarry)) { 220 MON_WEP(otmp->ocarry) = otmp; 221 otmp->owornmask = W_WEP; 222 } 223 } 224#endif /*0*/ 225 226 obfree(obj,otmp); /* free(obj), bill->otmp */ 227 return(1); 228 } 229 return 0; 230} 231 232/* 233Adjust hero intrinsics as if this object was being added to the hero's 234inventory. Called _before_ the object has been added to the hero's 235inventory. 236 237This is called when adding objects to the hero's inventory normally (via 238addinv) or when an object in the hero's inventory has been polymorphed 239in-place. 240 241It may be valid to merge this code with with addinv_core2(). 242*/ 243void 244addinv_core1(obj) 245struct obj *obj; 246{ 247 if (obj->oclass == COIN_CLASS) { 248#ifndef GOLDOBJ 249 u.ugold += obj->quan; 250#else 251 flags.botl = 1; 252#endif 253 } else if (obj->otyp == AMULET_OF_YENDOR) { 254 if (u.uhave.amulet) impossible("already have amulet?"); 255 u.uhave.amulet = 1; 256 } else if (obj->otyp == CANDELABRUM_OF_INVOCATION) { 257 if (u.uhave.menorah) impossible("already have candelabrum?"); 258 u.uhave.menorah = 1; 259 } else if (obj->otyp == BELL_OF_OPENING) { 260 if (u.uhave.bell) impossible("already have silver bell?"); 261 u.uhave.bell = 1; 262 } else if (obj->otyp == SPE_BOOK_OF_THE_DEAD) { 263 if (u.uhave.book) impossible("already have the book?"); 264 u.uhave.book = 1; 265 } else if (obj->oartifact) { 266 if (is_quest_artifact(obj)) { 267 if (u.uhave.questart) 268 impossible("already have quest artifact?"); 269 u.uhave.questart = 1; 270 artitouch(); 271 } 272 set_artifact_intrinsic(obj, 1, W_ART); 273 } 274} 275 276/* 277Adjust hero intrinsics as if this object was being added to the hero's 278inventory. Called _after_ the object has been added to the hero's 279inventory. 280 281This is called when adding objects to the hero's inventory normally (via 282addinv) or when an object in the hero's inventory has been polymorphed 283in-place. 284*/ 285void 286addinv_core2(obj) 287struct obj *obj; 288{ 289 if (confers_luck(obj)) { 290 /* new luckstone must be in inventory by this point 291 * for correct calculation */ 292 set_moreluck(); 293 } 294} 295 296/* 297Add obj to the hero's inventory. Make sure the object is "free". 298Adjust hero attributes as necessary. 299*/ 300struct obj * 301addinv(obj) 302struct obj *obj; 303{ 304 struct obj *otmp, *prev; 305 306 if (obj->where != OBJ_FREE) 307 panic("addinv: obj not free"); 308 obj->no_charge = 0; /* not meaningful for invent */ 309 310 addinv_core1(obj); 311#ifndef GOLDOBJ 312 /* if handed gold, we're done */ 313 if (obj->oclass == COIN_CLASS) 314 return obj; 315#endif 316 317 /* merge if possible; find end of chain in the process */ 318 for (prev = 0, otmp = invent; otmp; prev = otmp, otmp = otmp->nobj) 319 if (merged(&otmp, &obj)) { 320 obj = otmp; 321 goto added; 322 } 323 /* didn't merge, so insert into chain */ 324 if (flags.invlet_constant || !prev) { 325 if (flags.invlet_constant) assigninvlet(obj); 326 obj->nobj = invent; /* insert at beginning */ 327 invent = obj; 328 if (flags.invlet_constant) reorder_invent(); 329 } else { 330 prev->nobj = obj; /* insert at end */ 331 obj->nobj = 0; 332 } 333 obj->where = OBJ_INVENT; 334 335added: 336 addinv_core2(obj); 337 carry_obj_effects(obj); /* carrying affects the obj */ 338 update_inventory(); 339 return(obj); 340} 341 342/* 343 * Some objects are affected by being carried. 344 * Make those adjustments here. Called _after_ the object 345 * has been added to the hero's or monster's inventory, 346 * and after hero's intrinsics have been updated. 347 */ 348void 349carry_obj_effects(obj) 350struct obj *obj; 351{ 352 /* Cursed figurines can spontaneously transform 353 when carried. */ 354 if (obj->otyp == FIGURINE) { 355 if (obj->cursed 356 && obj->corpsenm != NON_PM 357 && !dead_species(obj->corpsenm,TRUE)) { 358 attach_fig_transform_timeout(obj); 359 } 360 } 361} 362 363#endif /* OVL1 */ 364#ifdef OVLB 365 366/* Add an item to the inventory unless we're fumbling or it refuses to be 367 * held (via touch_artifact), and give a message. 368 * If there aren't any free inventory slots, we'll drop it instead. 369 * If both success and failure messages are NULL, then we're just doing the 370 * fumbling/slot-limit checking for a silent grab. In any case, 371 * touch_artifact will print its own messages if they are warranted. 372 */ 373struct obj * 374hold_another_object(obj, drop_fmt, drop_arg, hold_msg) 375struct obj *obj; 376const char *drop_fmt, *drop_arg, *hold_msg; 377{ 378 char buf[BUFSZ]; 379 380 if (!Blind) obj->dknown = 1; /* maximize mergibility */ 381 if (obj->oartifact) { 382 /* place_object may change these */ 383 boolean crysknife = (obj->otyp == CRYSKNIFE); 384 int oerode = obj->oerodeproof; 385 boolean wasUpolyd = Upolyd; 386 387 /* in case touching this object turns out to be fatal */ 388 place_object(obj, u.ux, u.uy); 389 390 if (!touch_artifact(obj, &youmonst)) { 391 obj_extract_self(obj); /* remove it from the floor */ 392 dropy(obj); /* now put it back again :-) */ 393 return obj; 394 } else if (wasUpolyd && !Upolyd) { 395 /* loose your grip if you revert your form */ 396 if (drop_fmt) pline(drop_fmt, drop_arg); 397 obj_extract_self(obj); 398 dropy(obj); 399 return obj; 400 } 401 obj_extract_self(obj); 402 if (crysknife) { 403 obj->otyp = CRYSKNIFE; 404 obj->oerodeproof = oerode; 405 } 406 } 407 if (Fumbling) { 408 if (drop_fmt) pline(drop_fmt, drop_arg); 409 dropy(obj); 410 } else { 411 long oquan = obj->quan; 412 int prev_encumbr = near_capacity(); /* before addinv() */ 413 414 /* encumbrance only matters if it would now become worse 415 than max( current_value, stressed ) */ 416 if (prev_encumbr < MOD_ENCUMBER) prev_encumbr = MOD_ENCUMBER; 417 /* addinv() may redraw the entire inventory, overwriting 418 drop_arg when it comes from something like doname() */ 419 if (drop_arg) drop_arg = strcpy(buf, drop_arg); 420 421 obj = addinv(obj); 422 if (inv_cnt() > 52 423 || ((obj->otyp != LOADSTONE || !obj->cursed) 424 && near_capacity() > prev_encumbr)) { 425 if (drop_fmt) pline(drop_fmt, drop_arg); 426 /* undo any merge which took place */ 427 if (obj->quan > oquan) obj = splitobj(obj, oquan); 428 dropx(obj); 429 } else { 430 if (flags.autoquiver && !uquiver && !obj->owornmask && 431 (is_missile(obj) || 432 ammo_and_launcher(obj, uwep) || 433 ammo_and_launcher(obj, uswapwep))) 434 setuqwep(obj); 435 if (hold_msg || drop_fmt) prinv(hold_msg, obj, oquan); 436 } 437 } 438 return obj; 439} 440 441/* useup() all of an item regardless of its quantity */ 442void 443useupall(obj) 444struct obj *obj; 445{ 446 setnotworn(obj); 447 freeinv(obj); 448 obfree(obj, (struct obj *)0); /* deletes contents also */ 449} 450 451void 452useup(obj) 453register struct obj *obj; 454{ 455 /* Note: This works correctly for containers because they */ 456 /* (containers) don't merge. */ 457 if (obj->quan > 1L) { 458 obj->in_use = FALSE; /* no longer in use */ 459 obj->quan--; 460 obj->owt = weight(obj); 461 update_inventory(); 462 } else { 463 useupall(obj); 464 } 465} 466 467/* use one charge from an item and possibly incur shop debt for it */ 468void 469consume_obj_charge(obj, maybe_unpaid) 470struct obj *obj; 471boolean maybe_unpaid; /* false if caller handles shop billing */ 472{ 473 if (maybe_unpaid) check_unpaid(obj); 474 obj->spe -= 1; 475 if (obj->known) update_inventory(); 476} 477 478#endif /* OVLB */ 479#ifdef OVL3 480 481/* 482Adjust hero's attributes as if this object was being removed from the 483hero's inventory. This should only be called from freeinv() and 484where we are polymorphing an object already in the hero's inventory. 485 486Should think of a better name... 487*/ 488void 489freeinv_core(obj) 490struct obj *obj; 491{ 492 if (obj->oclass == COIN_CLASS) { 493#ifndef GOLDOBJ 494 u.ugold -= obj->quan; 495 obj->in_use = FALSE; 496#endif 497 flags.botl = 1; 498 return; 499 } else if (obj->otyp == AMULET_OF_YENDOR) { 500 if (!u.uhave.amulet) impossible("don't have amulet?"); 501 u.uhave.amulet = 0; 502 } else if (obj->otyp == CANDELABRUM_OF_INVOCATION) { 503 if (!u.uhave.menorah) impossible("don't have candelabrum?"); 504 u.uhave.menorah = 0; 505 } else if (obj->otyp == BELL_OF_OPENING) { 506 if (!u.uhave.bell) impossible("don't have silver bell?"); 507 u.uhave.bell = 0; 508 } else if (obj->otyp == SPE_BOOK_OF_THE_DEAD) { 509 if (!u.uhave.book) impossible("don't have the book?"); 510 u.uhave.book = 0; 511 } else if (obj->oartifact) { 512 if (is_quest_artifact(obj)) { 513 if (!u.uhave.questart) 514 impossible("don't have quest artifact?"); 515 u.uhave.questart = 0; 516 } 517 set_artifact_intrinsic(obj, 0, W_ART); 518 } 519 520 if (obj->otyp == LOADSTONE) { 521 curse(obj); 522 } else if (confers_luck(obj)) { 523 set_moreluck(); 524 flags.botl = 1; 525 } else if (obj->otyp == FIGURINE && obj->timed) { 526 (void) stop_timer(FIG_TRANSFORM, (genericptr_t) obj); 527 } 528} 529 530/* remove an object from the hero's inventory */ 531void 532freeinv(obj) 533register struct obj *obj; 534{ 535 extract_nobj(obj, &invent); 536 freeinv_core(obj); 537 update_inventory(); 538} 539 540void 541delallobj(x, y) 542int x, y; 543{ 544 struct obj *otmp, *otmp2; 545 546 for (otmp = level.objects[x][y]; otmp; otmp = otmp2) { 547 if (otmp == uball) 548 unpunish(); 549 /* after unpunish(), or might get deallocated chain */ 550 otmp2 = otmp->nexthere; 551 if (otmp == uchain) 552 continue; 553 delobj(otmp); 554 } 555} 556 557#endif /* OVL3 */ 558#ifdef OVL2 559 560/* destroy object in fobj chain (if unpaid, it remains on the bill) */ 561void 562delobj(obj) 563register struct obj *obj; 564{ 565 boolean update_map; 566 567 if (obj->otyp == AMULET_OF_YENDOR || 568 obj->otyp == CANDELABRUM_OF_INVOCATION || 569 obj->otyp == BELL_OF_OPENING || 570 obj->otyp == SPE_BOOK_OF_THE_DEAD) { 571 /* player might be doing something stupid, but we 572 * can't guarantee that. assume special artifacts 573 * are indestructible via drawbridges, and exploding 574 * chests, and golem creation, and ... 575 */ 576 return; 577 } 578 update_map = (obj->where == OBJ_FLOOR); 579 obj_extract_self(obj); 580 if (update_map) newsym(obj->ox, obj->oy); 581 obfree(obj, (struct obj *) 0); /* frees contents also */ 582} 583 584#endif /* OVL2 */ 585#ifdef OVL0 586 587struct obj * 588sobj_at(n,x,y) 589register int n, x, y; 590{ 591 register struct obj *otmp; 592 593 for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) 594 if(otmp->otyp == n) 595 return(otmp); 596 return((struct obj *)0); 597} 598 599#endif /* OVL0 */ 600#ifdef OVLB 601 602struct obj * 603carrying(type) 604register int type; 605{ 606 register struct obj *otmp; 607 608 for(otmp = invent; otmp; otmp = otmp->nobj) 609 if(otmp->otyp == type) 610 return(otmp); 611 return((struct obj *) 0); 612} 613 614const char * 615currency(amount) 616long amount; 617{ 618 if (amount == 1L) return "zorkmid"; 619 else return "zorkmids"; 620} 621 622boolean 623have_lizard() 624{ 625 register struct obj *otmp; 626 627 for(otmp = invent; otmp; otmp = otmp->nobj) 628 if(otmp->otyp == CORPSE && otmp->corpsenm == PM_LIZARD) 629 return(TRUE); 630 return(FALSE); 631} 632 633struct obj * 634o_on(id, objchn) 635unsigned int id; 636register struct obj *objchn; 637{ 638 struct obj *temp; 639 640 while(objchn) { 641 if(objchn->o_id == id) return(objchn); 642 if (Has_contents(objchn) && (temp = o_on(id,objchn->cobj))) 643 return temp; 644 objchn = objchn->nobj; 645 } 646 return((struct obj *) 0); 647} 648 649boolean 650obj_here(obj, x, y) 651register struct obj *obj; 652int x, y; 653{ 654 register struct obj *otmp; 655 656 for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) 657 if(obj == otmp) return(TRUE); 658 return(FALSE); 659} 660 661#endif /* OVLB */ 662#ifdef OVL2 663 664struct obj * 665g_at(x,y) 666register int x, y; 667{ 668 register struct obj *obj = level.objects[x][y]; 669 while(obj) { 670 if (obj->oclass == COIN_CLASS) return obj; 671 obj = obj->nexthere; 672 } 673 return((struct obj *)0); 674} 675 676#endif /* OVL2 */ 677#ifdef OVLB 678#ifndef GOLDOBJ 679/* Make a gold object from the hero's gold. */ 680struct obj * 681mkgoldobj(q) 682register long q; 683{ 684 register struct obj *otmp; 685 686 otmp = mksobj(GOLD_PIECE, FALSE, FALSE); 687 u.ugold -= q; 688 otmp->quan = q; 689 otmp->owt = weight(otmp); 690 flags.botl = 1; 691 return(otmp); 692} 693#endif 694#endif /* OVLB */ 695#ifdef OVL1 696 697STATIC_OVL void 698compactify(buf) 699register char *buf; 700/* compact a string of inventory letters by dashing runs of letters */ 701{ 702 register int i1 = 1, i2 = 1; 703 register char ilet, ilet1, ilet2; 704 705 ilet2 = buf[0]; 706 ilet1 = buf[1]; 707 buf[++i2] = buf[++i1]; 708 ilet = buf[i1]; 709 while(ilet) { 710 if(ilet == ilet1+1) { 711 if(ilet1 == ilet2+1) 712 buf[i2 - 1] = ilet1 = '-'; 713 else if(ilet2 == '-') { 714 buf[i2 - 1] = ++ilet1; 715 buf[i2] = buf[++i1]; 716 ilet = buf[i1]; 717 continue; 718 } 719 } 720 ilet2 = ilet1; 721 ilet1 = ilet; 722 buf[++i2] = buf[++i1]; 723 ilet = buf[i1]; 724 } 725} 726 727/* match the prompt for either 'T' or 'R' command */ 728STATIC_OVL boolean 729taking_off(action) 730const char *action; 731{ 732 return !strcmp(action, "take off") || !strcmp(action, "remove"); 733} 734 735/* match the prompt for either 'W' or 'P' command */ 736STATIC_OVL boolean 737putting_on(action) 738const char *action; 739{ 740 return !strcmp(action, "wear") || !strcmp(action, "put on"); 741} 742 743/* 744 * getobj returns: 745 * struct obj *xxx: object to do something with. 746 * (struct obj *) 0 error return: no object. 747 * &zeroobj explicitly no object (as in w-). 748#ifdef GOLDOBJ 749!!!! test if gold can be used in unusual ways (eaten etc.) 750!!!! may be able to remove "usegold" 751#endif 752 */ 753struct obj * 754getobj(let,word) 755register const char *let,*word; 756{ 757 register struct obj *otmp; 758 register char ilet; 759 char buf[BUFSZ], qbuf[QBUFSZ]; 760 char lets[BUFSZ], altlets[BUFSZ], *ap; 761 register int foo = 0; 762 register char *bp = buf; 763 xchar allowcnt = 0; /* 0, 1 or 2 */ 764#ifndef GOLDOBJ 765 boolean allowgold = FALSE; /* can't use gold because they don't have any */ 766#endif 767 boolean usegold = FALSE; /* can't use gold because its illegal */ 768 boolean allowall = FALSE; 769 boolean allownone = FALSE; 770 boolean useboulder = FALSE; 771 xchar foox = 0; 772 long cnt; 773 boolean prezero = FALSE; 774 long dummymask; 775 776 if(*let == ALLOW_COUNT) let++, allowcnt = 1; 777#ifndef GOLDOBJ 778 if(*let == COIN_CLASS) let++, 779 usegold = TRUE, allowgold = (u.ugold ? TRUE : FALSE); 780#else 781 if(*let == COIN_CLASS) let++, usegold = TRUE; 782#endif 783 784 /* Equivalent of an "ugly check" for gold */ 785 if (usegold && !strcmp(word, "eat") && 786 (!metallivorous(youmonst.data) 787 || youmonst.data == &mons[PM_RUST_MONSTER])) 788#ifndef GOLDOBJ 789 usegold = allowgold = FALSE; 790#else 791 usegold = FALSE; 792#endif 793 794 if(*let == ALL_CLASSES) let++, allowall = TRUE; 795 if(*let == ALLOW_NONE) let++, allownone = TRUE; 796 /* "ugly check" for reading fortune cookies, part 1 */ 797 /* The normal 'ugly check' keeps the object on the inventory list. 798 * We don't want to do that for shirts/cookies, so the check for 799 * them is handled a bit differently (and also requires that we set 800 * allowall in the caller) 801 */ 802 if(allowall && !strcmp(word, "read")) allowall = FALSE; 803 804 /* another ugly check: show boulders (not statues) */ 805 if(*let == WEAPON_CLASS && 806 !strcmp(word, "throw") && throws_rocks(youmonst.data)) 807 useboulder = TRUE; 808 809 if(allownone) *bp++ = '-'; 810#ifndef GOLDOBJ 811 if(allowgold) *bp++ = def_oc_syms[COIN_CLASS]; 812#endif 813 if(bp > buf && bp[-1] == '-') *bp++ = ' '; 814 ap = altlets; 815 816 ilet = 'a'; 817 for (otmp = invent; otmp; otmp = otmp->nobj) { 818 if (!flags.invlet_constant) 819#ifdef GOLDOBJ 820 if (otmp->invlet != GOLD_SYM) /* don't reassign this */ 821#endif 822 otmp->invlet = ilet; /* reassign() */ 823 if (!*let || index(let, otmp->oclass) 824#ifdef GOLDOBJ 825 || (usegold && otmp->invlet == GOLD_SYM) 826#endif 827 || (useboulder && otmp->otyp == BOULDER) 828 ) { 829 register int otyp = otmp->otyp; 830 bp[foo++] = otmp->invlet; 831 832 /* ugly check: remove inappropriate things */ 833 if ((taking_off(word) && 834 (!(otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL)) 835 || (otmp==uarm && uarmc) 836#ifdef TOURIST 837 || (otmp==uarmu && (uarm || uarmc)) 838#endif 839 )) 840 || (putting_on(word) && 841 (otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL))) 842 /* already worn */ 843#if 0 /* 3.4.1 -- include currently wielded weapon among the choices */ 844 || (!strcmp(word, "wield") && 845 (otmp->owornmask & W_WEP)) 846#endif 847 || (!strcmp(word, "ready") && 848 (otmp == uwep || (otmp == uswapwep && u.twoweap))) 849 ) { 850 foo--; 851 foox++; 852 } 853 854 /* Second ugly check; unlike the first it won't trigger an 855 * "else" in "you don't have anything else to ___". 856 */ 857 else if ((putting_on(word) && 858 ((otmp->oclass == FOOD_CLASS && otmp->otyp != MEAT_RING) || 859 (otmp->oclass == TOOL_CLASS && 860 otyp != BLINDFOLD && otyp != TOWEL && otyp != LENSES))) 861 || (!strcmp(word, "wield") && 862 (otmp->oclass == TOOL_CLASS && !is_weptool(otmp))) 863 || (!strcmp(word, "eat") && !is_edible(otmp)) 864 || (!strcmp(word, "sacrifice") && 865 (otyp != CORPSE && 866 otyp != AMULET_OF_YENDOR && otyp != FAKE_AMULET_OF_YENDOR)) 867 || (!strcmp(word, "write with") && 868 (otmp->oclass == TOOL_CLASS && 869 otyp != MAGIC_MARKER && otyp != TOWEL)) 870 || (!strcmp(word, "tin") && 871 (otyp != CORPSE || !tinnable(otmp))) 872 || (!strcmp(word, "rub") && 873 ((otmp->oclass == TOOL_CLASS && 874 otyp != OIL_LAMP && otyp != MAGIC_LAMP && 875 otyp != BRASS_LANTERN) || 876 (otmp->oclass == GEM_CLASS && !is_graystone(otmp)))) 877 || (!strncmp(word, "rub on the stone", 16) && 878 *let == GEM_CLASS && /* using known touchstone */ 879 otmp->dknown && objects[otyp].oc_name_known) 880 || ((!strcmp(word, "use or apply") || 881 !strcmp(word, "untrap with")) && 882 /* Picks, axes, pole-weapons, bullwhips */ 883 ((otmp->oclass == WEAPON_CLASS && !is_pick(otmp) && 884 !is_axe(otmp) && !is_pole(otmp) && otyp != BULLWHIP) || 885 (otmp->oclass == POTION_CLASS && 886 /* only applicable potion is oil, and it will only 887 be offered as a choice when already discovered */ 888 (otyp != POT_OIL || !otmp->dknown || 889 !objects[POT_OIL].oc_name_known)) || 890 (otmp->oclass == FOOD_CLASS && 891 otyp != CREAM_PIE && otyp != EUCALYPTUS_LEAF) || 892 (otmp->oclass == GEM_CLASS && !is_graystone(otmp)))) 893 || (!strcmp(word, "invoke") && 894 (!otmp->oartifact && !objects[otyp].oc_unique && 895 (otyp != FAKE_AMULET_OF_YENDOR || otmp->known) && 896 otyp != CRYSTAL_BALL && /* #invoke synonym for apply */ 897 /* note: presenting the possibility of invoking non-artifact 898 mirrors and/or lamps is a simply a cruel deception... */ 899 otyp != MIRROR && otyp != MAGIC_LAMP && 900 (otyp != OIL_LAMP || /* don't list known oil lamp */ 901 (otmp->dknown && objects[OIL_LAMP].oc_name_known)))) 902 || (!strcmp(word, "untrap with") && 903 (otmp->oclass == TOOL_CLASS && otyp != CAN_OF_GREASE)) 904 || (!strcmp(word, "charge") && !is_chargeable(otmp)) 905 ) 906 foo--; 907 /* ugly check for unworn armor that can't be worn */ 908 else if (putting_on(word) && *let == ARMOR_CLASS && 909 !canwearobj(otmp, &dummymask, FALSE)) { 910 foo--; 911 allowall = TRUE; 912 *ap++ = otmp->invlet; 913 } 914 } else { 915 916 /* "ugly check" for reading fortune cookies, part 2 */ 917 if ((!strcmp(word, "read") && 918 (otmp->otyp == FORTUNE_COOKIE 919#ifdef TOURIST 920 || otmp->otyp == T_SHIRT 921#endif 922 ))) 923 allowall = TRUE; 924 } 925 926 if(ilet == 'z') ilet = 'A'; else ilet++; 927 } 928 bp[foo] = 0; 929 if(foo == 0 && bp > buf && bp[-1] == ' ') *--bp = 0; 930 Strcpy(lets, bp); /* necessary since we destroy buf */ 931 if(foo > 5) /* compactify string */ 932 compactify(bp); 933 *ap = '\0'; 934 935#ifndef GOLDOBJ 936 if(!foo && !allowall && !allowgold && !allownone) { 937#else 938 if(!foo && !allowall && !allownone) { 939#endif 940 You("don't have anything %sto %s.", 941 foox ? "else " : "", word); 942 return((struct obj *)0); 943 } 944 for(;;) { 945 cnt = 0; 946 if (allowcnt == 2) allowcnt = 1; /* abort previous count */ 947 if(!buf[0]) { 948 Sprintf(qbuf, "What do you want to %s? [*]", word); 949 } else { 950 Sprintf(qbuf, "What do you want to %s? [%s or ?*]", 951 word, buf); 952 } 953#ifdef REDO 954 if (in_doagain) 955 ilet = readchar(); 956 else 957#endif 958 ilet = yn_function(qbuf, (char *)0, '\0'); 959 if(ilet == '0') prezero = TRUE; 960 while(digit(ilet) && allowcnt) { 961#ifdef REDO 962 if (ilet != '?' && ilet != '*') savech(ilet); 963#endif 964 cnt = 10*cnt + (ilet - '0'); 965 allowcnt = 2; /* signal presence of cnt */ 966 ilet = readchar(); 967 } 968 if(digit(ilet)) { 969 pline("No count allowed with this command."); 970 continue; 971 } 972 if(index(quitchars,ilet)) { 973 if(flags.verbose) 974 pline(Never_mind); 975 return((struct obj *)0); 976 } 977 if(ilet == '-') { 978 return(allownone ? &zeroobj : (struct obj *) 0); 979 } 980 if(ilet == def_oc_syms[COIN_CLASS]) { 981 if (!usegold) { 982 if (!strncmp(word, "rub on ", 7)) { 983 /* the dangers of building sentences... */ 984 You("cannot rub gold%s.", word + 3); 985 } else { 986 You("cannot %s gold.", word); 987 } 988 return(struct obj *)0; 989#ifndef GOLDOBJ 990 } else if (!allowgold) { 991 You("are not carrying any gold."); 992 return(struct obj *)0; 993#endif 994 } 995 if(cnt == 0 && prezero) return((struct obj *)0); 996 /* Historic note: early Nethack had a bug which was 997 * first reported for Larn, where trying to drop 2^32-n 998 * gold pieces was allowed, and did interesting things 999 * to your money supply. The LRS is the tax bureau 1000 * from Larn. 1001 */ 1002 if(cnt < 0) { 1003 pline_The("LRS would be very interested to know you have that much."); 1004 return(struct obj *)0; 1005 } 1006 1007#ifndef GOLDOBJ 1008 if(!(allowcnt == 2 && cnt < u.ugold)) 1009 cnt = u.ugold; 1010 return(mkgoldobj(cnt)); 1011#endif 1012 } 1013 if(ilet == '?' || ilet == '*') { 1014 char *allowed_choices = (ilet == '?') ? lets : (char *)0; 1015 long ctmp = 0; 1016 1017 if (ilet == '?' && !*lets && *altlets) 1018 allowed_choices = altlets; 1019 ilet = display_pickinv(allowed_choices, TRUE, 1020 allowcnt ? &ctmp : (long *)0); 1021 if(!ilet) continue; 1022 if (allowcnt && ctmp >= 0) { 1023 cnt = ctmp; 1024 if (!cnt) prezero = TRUE; 1025 allowcnt = 2; 1026 } 1027 if(ilet == '\033') { 1028 if(flags.verbose) 1029 pline(Never_mind); 1030 return((struct obj *)0); 1031 } 1032 /* they typed a letter (not a space) at the prompt */ 1033 } 1034 if(allowcnt == 2 && !strcmp(word,"throw")) { 1035 /* permit counts for throwing gold, but don't accept 1036 * counts for other things since the throw code will 1037 * split off a single item anyway */ 1038#ifdef GOLDOBJ 1039 if (ilet != def_oc_syms[COIN_CLASS]) 1040#endif 1041 allowcnt = 1; 1042 if(cnt == 0 && prezero) return((struct obj *)0); 1043 if(cnt > 1) { 1044 You("can only throw one item at a time."); 1045 continue; 1046 } 1047 } 1048#ifdef GOLDOBJ 1049 flags.botl = 1; /* May have changed the amount of money */ 1050#endif 1051#ifdef REDO 1052 savech(ilet); 1053#endif 1054 for (otmp = invent; otmp; otmp = otmp->nobj) 1055 if (otmp->invlet == ilet) break; 1056 if(!otmp) { 1057 You("don't have that object."); 1058#ifdef REDO 1059 if (in_doagain) return((struct obj *) 0); 1060#endif 1061 continue; 1062 } else if (cnt < 0 || otmp->quan < cnt) { 1063 You("don't have that many! You have only %ld.", 1064 otmp->quan); 1065#ifdef REDO 1066 if (in_doagain) return((struct obj *) 0); 1067#endif 1068 continue; 1069 } 1070 break; 1071 } 1072 if(!allowall && let && !index(let,otmp->oclass) 1073#ifdef GOLDOBJ 1074 && !(usegold && otmp->oclass == COIN_CLASS) 1075#endif 1076 ) { 1077 silly_thing(word, otmp); 1078 return((struct obj *)0); 1079 } 1080 if(allowcnt == 2) { /* cnt given */ 1081 if(cnt == 0) return (struct obj *)0; 1082 if(cnt != otmp->quan) { 1083 /* don't split a stack of cursed loadstones */ 1084 if (otmp->otyp == LOADSTONE && otmp->cursed) 1085 /* kludge for canletgo()'s can't-drop-this message */ 1086 otmp->corpsenm = (int) cnt; 1087 else 1088 otmp = splitobj(otmp, cnt); 1089 } 1090 } 1091 return(otmp); 1092} 1093 1094void 1095silly_thing(word, otmp) 1096const char *word; 1097struct obj *otmp; 1098{ 1099 const char *s1, *s2, *s3, *what; 1100 int ocls = otmp->oclass, otyp = otmp->otyp; 1101 1102 s1 = s2 = s3 = 0; 1103 /* check for attempted use of accessory commands ('P','R') on armor 1104 and for corresponding armor commands ('W','T') on accessories */ 1105 if (ocls == ARMOR_CLASS) { 1106 if (!strcmp(word, "put on")) 1107 s1 = "W", s2 = "wear", s3 = ""; 1108 else if (!strcmp(word, "remove")) 1109 s1 = "T", s2 = "take", s3 = " off"; 1110 } else if ((ocls == RING_CLASS || otyp == MEAT_RING) || 1111 ocls == AMULET_CLASS || 1112 (otyp == BLINDFOLD || otyp == TOWEL || otyp == LENSES)) { 1113 if (!strcmp(word, "wear")) 1114 s1 = "P", s2 = "put", s3 = " on"; 1115 else if (!strcmp(word, "take off")) 1116 s1 = "R", s2 = "remove", s3 = ""; 1117 } 1118 if (s1) { 1119 what = "that"; 1120 /* quantity for armor and accessory objects is always 1, 1121 but some things should be referred to as plural */ 1122 if (otyp == LENSES || is_gloves(otmp) || is_boots(otmp)) 1123 what = "those"; 1124 pline("Use the '%s' command to %s %s%s.", s1, s2, what, s3); 1125 } else { 1126 pline(silly_thing_to, word); 1127 } 1128} 1129 1130#endif /* OVL1 */ 1131#ifdef OVLB 1132 1133STATIC_PTR int 1134ckvalidcat(otmp) 1135register struct obj *otmp; 1136{ 1137 /* use allow_category() from pickup.c */ 1138 return((int)allow_category(otmp)); 1139} 1140 1141STATIC_PTR int 1142ckunpaid(otmp) 1143register struct obj *otmp; 1144{ 1145 return((int)(otmp->unpaid)); 1146} 1147 1148boolean 1149wearing_armor() 1150{ 1151 return((boolean)(uarm || uarmc || uarmf || uarmg || uarmh || uarms 1152#ifdef TOURIST 1153 || uarmu 1154#endif 1155 )); 1156} 1157 1158boolean 1159is_worn(otmp) 1160register struct obj *otmp; 1161{ 1162 return((boolean)(!!(otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL | 1163#ifdef STEED 1164 W_SADDLE | 1165#endif 1166 W_WEP | W_SWAPWEP | W_QUIVER)))); 1167} 1168 1169static NEARDATA const char removeables[] = 1170 { ARMOR_CLASS, WEAPON_CLASS, RING_CLASS, AMULET_CLASS, TOOL_CLASS, 0 }; 1171 1172/* interactive version of getobj - used for Drop, Identify and */ 1173/* Takeoff (A). Return the number of times fn was called successfully */ 1174/* If combo is TRUE, we just use this to get a category list */ 1175int 1176ggetobj(word, fn, mx, combo, resultflags) 1177const char *word; 1178int FDECL((*fn),(OBJ_P)), mx; 1179boolean combo; /* combination menu flag */ 1180unsigned *resultflags; 1181{ 1182 int FDECL((*ckfn),(OBJ_P)) = (int FDECL((*),(OBJ_P))) 0; 1183 boolean FDECL((*filter),(OBJ_P)) = (boolean FDECL((*),(OBJ_P))) 0; 1184 boolean takeoff, ident, allflag, m_seen; 1185 int itemcount; 1186#ifndef GOLDOBJ 1187 int oletct, iletct, allowgold, unpaid, oc_of_sym; 1188#else 1189 int oletct, iletct, unpaid, oc_of_sym; 1190#endif 1191 char sym, *ip, olets[MAXOCLASSES+5], ilets[MAXOCLASSES+5]; 1192 char extra_removeables[3+1]; /* uwep,uswapwep,uquiver */ 1193 char buf[BUFSZ], qbuf[QBUFSZ]; 1194 1195 if (resultflags) *resultflags = 0; 1196#ifndef GOLDOBJ 1197 allowgold = (u.ugold && !strcmp(word, "drop")) ? 1 : 0; 1198#endif 1199 takeoff = ident = allflag = m_seen = FALSE; 1200#ifndef GOLDOBJ 1201 if(!invent && !allowgold){ 1202#else 1203 if(!invent){ 1204#endif 1205 You("have nothing to %s.", word); 1206 return(0); 1207 } 1208 add_valid_menu_class(0); /* reset */ 1209 if (taking_off(word)) { 1210 takeoff = TRUE; 1211 filter = is_worn; 1212 } else if (!strcmp(word, "identify")) { 1213 ident = TRUE; 1214 filter = not_fully_identified; 1215 } 1216 1217 iletct = collect_obj_classes(ilets, invent, 1218 FALSE, 1219#ifndef GOLDOBJ 1220 (allowgold != 0), 1221#endif 1222 filter, &itemcount); 1223 unpaid = count_unpaid(invent); 1224 1225 if (ident && !iletct) { 1226 return -1; /* no further identifications */ 1227 } else if (!takeoff && (unpaid || invent)) { 1228 ilets[iletct++] = ' '; 1229 if (unpaid) ilets[iletct++] = 'u'; 1230 if (count_buc(invent, BUC_BLESSED)) ilets[iletct++] = 'B'; 1231 if (count_buc(invent, BUC_UNCURSED)) ilets[iletct++] = 'U'; 1232 if (count_buc(invent, BUC_CURSED)) ilets[iletct++] = 'C'; 1233 if (count_buc(invent, BUC_UNKNOWN)) ilets[iletct++] = 'X'; 1234 if (invent) ilets[iletct++] = 'a'; 1235 } else if (takeoff && invent) { 1236 ilets[iletct++] = ' '; 1237 } 1238 ilets[iletct++] = 'i'; 1239 if (!combo) 1240 ilets[iletct++] = 'm'; /* allow menu presentation on request */ 1241 ilets[iletct] = '\0'; 1242 1243 for (;;) { 1244 Sprintf(qbuf,"What kinds of thing do you want to %s? [%s]", 1245 word, ilets); 1246 getlin(qbuf, buf); 1247 if (buf[0] == '\033') return(0); 1248 if (index(buf, 'i')) { 1249 if (display_inventory((char *)0, TRUE) == '\033') return 0; 1250 } else 1251 break; 1252 } 1253 1254 extra_removeables[0] = '\0'; 1255 if (takeoff) { 1256 /* arbitrary types of items can be placed in the weapon slots 1257 [any duplicate entries in extra_removeables[] won't matter] */ 1258 if (uwep) (void)strkitten(extra_removeables, uwep->oclass); 1259 if (uswapwep) (void)strkitten(extra_removeables, uswapwep->oclass); 1260 if (uquiver) (void)strkitten(extra_removeables, uquiver->oclass); 1261 } 1262 1263 ip = buf; 1264 olets[oletct = 0] = '\0'; 1265 while ((sym = *ip++) != '\0') { 1266 if (sym == ' ') continue; 1267 oc_of_sym = def_char_to_objclass(sym); 1268 if (takeoff && oc_of_sym != MAXOCLASSES) { 1269 if (index(extra_removeables, oc_of_sym)) { 1270 ; /* skip rest of takeoff checks */ 1271 } else if (!index(removeables, oc_of_sym)) { 1272 pline("Not applicable."); 1273 return 0; 1274 } else if (oc_of_sym == ARMOR_CLASS && !wearing_armor()) { 1275 You("are not wearing any armor."); 1276 return 0; 1277 } else if (oc_of_sym == WEAPON_CLASS && 1278 !uwep && !uswapwep && !uquiver) { 1279 You("are not wielding anything."); 1280 return 0; 1281 } else if (oc_of_sym == RING_CLASS && !uright && !uleft) { 1282 You("are not wearing rings."); 1283 return 0; 1284 } else if (oc_of_sym == AMULET_CLASS && !uamul) { 1285 You("are not wearing an amulet."); 1286 return 0; 1287 } else if (oc_of_sym == TOOL_CLASS && !ublindf) { 1288 You("are not wearing a blindfold."); 1289 return 0; 1290 } 1291 } 1292 1293 if (oc_of_sym == COIN_CLASS && !combo) { 1294#ifndef GOLDOBJ 1295 if (allowgold == 1) 1296 (*fn)(mkgoldobj(u.ugold)); 1297 else if (!u.ugold) 1298 You("have no gold."); 1299 allowgold = 2; 1300#else 1301 flags.botl = 1; 1302#endif 1303 } else if (sym == 'a') { 1304 allflag = TRUE; 1305 } else if (sym == 'A') { 1306 /* same as the default */ ; 1307 } else if (sym == 'u') { 1308 add_valid_menu_class('u'); 1309 ckfn = ckunpaid; 1310 } else if (sym == 'B') { 1311 add_valid_menu_class('B'); 1312 ckfn = ckvalidcat; 1313 } else if (sym == 'U') { 1314 add_valid_menu_class('U'); 1315 ckfn = ckvalidcat; 1316 } else if (sym == 'C') { 1317 add_valid_menu_class('C'); 1318 ckfn = ckvalidcat; 1319 } else if (sym == 'X') { 1320 add_valid_menu_class('X'); 1321 ckfn = ckvalidcat; 1322 } else if (sym == 'm') { 1323 m_seen = TRUE; 1324 } else if (oc_of_sym == MAXOCLASSES) { 1325 You("don't have any %c's.", sym); 1326 } else if (oc_of_sym != VENOM_CLASS) { /* suppress venom */ 1327 if (!index(olets, oc_of_sym)) { 1328 add_valid_menu_class(oc_of_sym); 1329 olets[oletct++] = oc_of_sym; 1330 olets[oletct] = 0; 1331 } 1332 } 1333 } 1334 1335 if (m_seen) 1336 return (allflag || (!oletct && ckfn != ckunpaid)) ? -2 : -3; 1337 else if (flags.menu_style != MENU_TRADITIONAL && combo && !allflag) 1338 return 0; 1339#ifndef GOLDOBJ 1340 else if (allowgold == 2 && !oletct) 1341 return 1; /* you dropped gold (or at least tried to) */ 1342 else { 1343#else 1344 else /*!!!! if (allowgold == 2 && !oletct) 1345 !!!! return 1; you dropped gold (or at least tried to) 1346 !!!! test gold dropping 1347 else*/ { 1348#endif 1349 int cnt = askchain(&invent, olets, allflag, fn, ckfn, mx, word); 1350 /* 1351 * askchain() has already finished the job in this case 1352 * so set a special flag to convey that back to the caller 1353 * so that it won't continue processing. 1354 * Fix for bug C331-1 reported by Irina Rempt-Drijfhout. 1355 */ 1356 if (combo && allflag && resultflags) 1357 *resultflags |= ALL_FINISHED; 1358 return cnt; 1359 } 1360} 1361 1362/* 1363 * Walk through the chain starting at objchn and ask for all objects 1364 * with olet in olets (if nonNULL) and satisfying ckfn (if nonnull) 1365 * whether the action in question (i.e., fn) has to be performed. 1366 * If allflag then no questions are asked. Max gives the max nr of 1367 * objects to be treated. Return the number of objects treated. 1368 */ 1369int 1370askchain(objchn, olets, allflag, fn, ckfn, mx, word) 1371struct obj **objchn; 1372register int allflag, mx; 1373register const char *olets, *word; /* olets is an Obj Class char array */ 1374register int FDECL((*fn),(OBJ_P)), FDECL((*ckfn),(OBJ_P)); 1375{ 1376 struct obj *otmp, *otmp2, *otmpo; 1377 register char sym, ilet; 1378 register int cnt = 0, dud = 0, tmp; 1379 boolean takeoff, nodot, ident, ininv; 1380 char qbuf[QBUFSZ]; 1381 1382 takeoff = taking_off(word); 1383 ident = !strcmp(word, "identify"); 1384 nodot = (!strcmp(word, "nodot") || !strcmp(word, "drop") || 1385 ident || takeoff); 1386 ininv = (*objchn == invent); 1387 /* Changed so the askchain is interrogated in the order specified. 1388 * For example, if a person specifies =/ then first all rings will be 1389 * asked about followed by all wands -dgk 1390 */ 1391nextclass: 1392 ilet = 'a'-1; 1393 if (*objchn && (*objchn)->oclass == COIN_CLASS) 1394 ilet--; /* extra iteration */ 1395 for (otmp = *objchn; otmp; otmp = otmp2) { 1396 if(ilet == 'z') ilet = 'A'; else ilet++; 1397 otmp2 = otmp->nobj; 1398 if (olets && *olets && otmp->oclass != *olets) continue; 1399 if (takeoff && !is_worn(otmp)) continue; 1400 if (ident && !not_fully_identified(otmp)) continue; 1401 if (ckfn && !(*ckfn)(otmp)) continue; 1402 if (!allflag) { 1403 Strcpy(qbuf, !ininv ? doname(otmp) : 1404 xprname(otmp, (char *)0, ilet, !nodot, 0L, 0L)); 1405 Strcat(qbuf, "?"); 1406 sym = (takeoff || ident || otmp->quan < 2L) ? 1407 nyaq(qbuf) : nyNaq(qbuf); 1408 } 1409 else sym = 'y'; 1410 1411 otmpo = otmp; 1412 if (sym == '#') { 1413 /* Number was entered; split the object unless it corresponds 1414 to 'none' or 'all'. 2 special cases: cursed loadstones and 1415 welded weapons (eg, multiple daggers) will remain as merged 1416 unit; done to avoid splitting an object that won't be 1417 droppable (even if we're picking up rather than dropping). 1418 */ 1419 if (!yn_number) 1420 sym = 'n'; 1421 else { 1422 sym = 'y'; 1423 if (yn_number < otmp->quan && !welded(otmp) && 1424 (!otmp->cursed || otmp->otyp != LOADSTONE)) { 1425 otmp = splitobj(otmp, yn_number); 1426 } 1427 } 1428 } 1429 switch(sym){ 1430 case 'a': 1431 allflag = 1; 1432 case 'y': 1433 tmp = (*fn)(otmp); 1434 if(tmp < 0) { 1435 if (otmp != otmpo) { 1436 /* split occurred, merge again */ 1437 (void) merged(&otmpo, &otmp); 1438 } 1439 goto ret; 1440 } 1441 cnt += tmp; 1442 if(--mx == 0) goto ret; 1443 case 'n': 1444 if(nodot) dud++; 1445 default: 1446 break; 1447 case 'q': 1448 /* special case for seffects() */ 1449 if (ident) cnt = -1; 1450 goto ret; 1451 } 1452 } 1453 if (olets && *olets && *++olets) 1454 goto nextclass; 1455 if(!takeoff && (dud || cnt)) pline("That was all."); 1456 else if(!dud && !cnt) pline("No applicable objects."); 1457ret: 1458 return(cnt); 1459} 1460 1461 1462/* 1463 * Object identification routines: 1464 */ 1465 1466/* make an object actually be identified; no display updating */ 1467void 1468fully_identify_obj(otmp) 1469struct obj *otmp; 1470{ 1471 makeknown(otmp->otyp); 1472 if (otmp->oartifact) discover_artifact((xchar)otmp->oartifact); 1473 otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1; 1474 if (otmp->otyp == EGG && otmp->corpsenm != NON_PM) 1475 learn_egg_type(otmp->corpsenm); 1476} 1477 1478/* ggetobj callback routine; identify an object and give immediate feedback */ 1479int 1480identify(otmp) 1481struct obj *otmp; 1482{ 1483 fully_identify_obj(otmp); 1484 prinv((char *)0, otmp, 0L); 1485 return 1; 1486} 1487 1488/* menu of unidentified objects; select and identify up to id_limit of them */ 1489STATIC_OVL void 1490menu_identify(id_limit) 1491int id_limit; 1492{ 1493 menu_item *pick_list; 1494 int n, i, first = 1; 1495 char buf[BUFSZ]; 1496 /* assumptions: id_limit > 0 and at least one unID'd item is present */ 1497 1498 while (id_limit) { 1499 Sprintf(buf, "What would you like to identify %s?", 1500 first ? "first" : "next"); 1501 n = query_objlist(buf, invent, SIGNAL_NOMENU|USE_INVLET|INVORDER_SORT, 1502 &pick_list, PICK_ANY, not_fully_identified); 1503 1504 if (n > 0) { 1505 if (n > id_limit) n = id_limit; 1506 for (i = 0; i < n; i++, id_limit--) 1507 (void) identify(pick_list[i].item.a_obj); 1508 free((genericptr_t) pick_list); 1509 mark_synch(); /* Before we loop to pop open another menu */ 1510 } else { 1511 if (n < 0) pline("That was all."); 1512 id_limit = 0; /* Stop now */ 1513 } 1514 first = 0; 1515 } 1516} 1517 1518/* dialog with user to identify a given number of items; 0 means all */ 1519void 1520identify_pack(id_limit) 1521int id_limit; 1522{ 1523 struct obj *obj, *the_obj; 1524 int n, unid_cnt; 1525 1526 unid_cnt = 0; 1527 the_obj = 0; /* if unid_cnt ends up 1, this will be it */ 1528 for (obj = invent; obj; obj = obj->nobj) 1529 if (not_fully_identified(obj)) ++unid_cnt, the_obj = obj; 1530 1531 if (!unid_cnt) { 1532 You("have already identified all of your possessions."); 1533 } else if (!id_limit) { 1534 /* identify everything */ 1535 if (unid_cnt == 1) { 1536 (void) identify(the_obj); 1537 } else { 1538 1539 /* TODO: use fully_identify_obj and cornline/menu/whatever here */ 1540 for (obj = invent; obj; obj = obj->nobj) 1541 if (not_fully_identified(obj)) (void) identify(obj); 1542 1543 } 1544 } else { 1545 /* identify up to `id_limit' items */ 1546 n = 0; 1547 if (flags.menu_style == MENU_TRADITIONAL) 1548 do { 1549 n = ggetobj("identify", identify, id_limit, FALSE, (unsigned *)0); 1550 if (n < 0) break; /* quit or no eligible items */ 1551 } while ((id_limit -= n) > 0); 1552 if (n == 0 || n < -1) 1553 menu_identify(id_limit); 1554 } 1555 update_inventory(); 1556} 1557 1558#endif /* OVLB */ 1559#ifdef OVL2 1560 1561STATIC_OVL char 1562obj_to_let(obj) /* should of course only be called for things in invent */ 1563register struct obj *obj; 1564{ 1565#ifndef GOLDOBJ 1566 if (obj->oclass == COIN_CLASS) 1567 return GOLD_SYM; 1568#endif 1569 if (!flags.invlet_constant) { 1570 obj->invlet = NOINVSYM; 1571 reassign(); 1572 } 1573 return obj->invlet; 1574} 1575 1576/* 1577 * Print the indicated quantity of the given object. If quan == 0L then use 1578 * the current quantity. 1579 */ 1580void 1581prinv(prefix, obj, quan) 1582const char *prefix; 1583register struct obj *obj; 1584long quan; 1585{ 1586 if (!prefix) prefix = ""; 1587 pline("%s%s%s", 1588 prefix, *prefix ? " " : "", 1589 xprname(obj, (char *)0, obj_to_let(obj), TRUE, 0L, quan)); 1590} 1591 1592#endif /* OVL2 */ 1593#ifdef OVL1 1594 1595char * 1596xprname(obj, txt, let, dot, cost, quan) 1597struct obj *obj; 1598const char *txt; /* text to print instead of obj */ 1599char let; /* inventory letter */ 1600boolean dot; /* append period; (dot && cost => Iu) */ 1601long cost; /* cost (for inventory of unpaid or expended items) */ 1602long quan; /* if non-0, print this quantity, not obj->quan */ 1603{ 1604#ifdef LINT /* handle static char li[BUFSZ]; */ 1605 char li[BUFSZ]; 1606#else 1607 static char li[BUFSZ]; 1608#endif 1609 boolean use_invlet = flags.invlet_constant && let != CONTAINED_SYM; 1610 long savequan = 0; 1611 1612 if (quan && obj) { 1613 savequan = obj->quan; 1614 obj->quan = quan; 1615 } 1616 1617 /* 1618 * If let is: 1619 * * Then obj == null and we are printing a total amount. 1620 * > Then the object is contained and doesn't have an inventory letter. 1621 */ 1622 if (cost != 0 || let == '*') { 1623 /* if dot is true, we're doing Iu, otherwise Ix */ 1624 Sprintf(li, "%c - %-45s %6ld %s", 1625 (dot && use_invlet ? obj->invlet : let), 1626 (txt ? txt : doname(obj)), cost, currency(cost)); 1627#ifndef GOLDOBJ 1628 } else if (obj && obj->oclass == COIN_CLASS) { 1629 Sprintf(li, "%ld gold piece%s%s", obj->quan, plur(obj->quan), 1630 (dot ? "." : "")); 1631#endif 1632 } else { 1633 /* ordinary inventory display or pickup message */ 1634 Sprintf(li, "%c - %s%s", 1635 (use_invlet ? obj->invlet : let), 1636 (txt ? txt : doname(obj)), (dot ? "." : "")); 1637 } 1638 if (savequan) obj->quan = savequan; 1639 1640 return li; 1641} 1642 1643#endif /* OVL1 */ 1644#ifdef OVLB 1645 1646/* the 'i' command */ 1647int 1648ddoinv() 1649{ 1650 (void) display_inventory((char *)0, FALSE); 1651 return 0; 1652} 1653 1654/* 1655 * find_unpaid() 1656 * 1657 * Scan the given list of objects. If last_found is NULL, return the first 1658 * unpaid object found. If last_found is not NULL, then skip over unpaid 1659 * objects until last_found is reached, then set last_found to NULL so the 1660 * next unpaid object is returned. This routine recursively follows 1661 * containers. 1662 */ 1663STATIC_OVL struct obj * 1664find_unpaid(list, last_found) 1665 struct obj *list, **last_found; 1666{ 1667 struct obj *obj; 1668 1669 while (list) { 1670 if (list->unpaid) { 1671 if (*last_found) { 1672 /* still looking for previous unpaid object */ 1673 if (list == *last_found) 1674 *last_found = (struct obj *) 0; 1675 } else 1676 return (*last_found = list); 1677 } 1678 if (Has_contents(list)) { 1679 if ((obj = find_unpaid(list->cobj, last_found)) != 0) 1680 return obj; 1681 } 1682 list = list->nobj; 1683 } 1684 return (struct obj *) 0; 1685} 1686 1687/* 1688 * Internal function used by display_inventory and getobj that can display 1689 * inventory and return a count as well as a letter. If out_cnt is not null, 1690 * any count returned from the menu selection is placed here. 1691 */ 1692static char 1693display_pickinv(lets, want_reply, out_cnt) 1694register const char *lets; 1695boolean want_reply; 1696long* out_cnt; 1697{ 1698 struct obj *otmp; 1699 char ilet, ret; 1700 char *invlet = flags.inv_order; 1701 int n, classcount; 1702 winid win; /* windows being used */ 1703 static winid local_win = WIN_ERR; /* window for partial menus */ 1704 anything any; 1705 menu_item *selected; 1706 1707 /* overriden by global flag */ 1708 if (flags.perm_invent) { 1709 win = (lets && *lets) ? local_win : WIN_INVEN; 1710 /* create the first time used */ 1711 if (win == WIN_ERR) 1712 win = local_win = create_nhwindow(NHW_MENU); 1713 } else 1714 win = WIN_INVEN; 1715 1716 /* 1717 Exit early if no inventory -- but keep going if we are doing 1718 a permanent inventory update. We need to keep going so the 1719 permanent inventory window updates itself to remove the last 1720 item(s) dropped. One down side: the addition of the exception 1721 for permanent inventory window updates _can_ pop the window 1722 up when it's not displayed -- even if it's empty -- because we 1723 don't know at this level if its up or not. This may not be 1724 an issue if empty checks are done before hand and the call 1725 to here is short circuited away. 1726 */ 1727 if (!invent && !(flags.perm_invent && !lets && !want_reply)) { 1728#ifndef GOLDOBJ 1729 pline("Not carrying anything%s.", u.ugold ? " except gold" : ""); 1730#else 1731 pline("Not carrying anything."); 1732#endif 1733 return 0; 1734 } 1735 1736 /* oxymoron? temporarily assign permanent inventory letters */ 1737 if (!flags.invlet_constant) reassign(); 1738 1739 if (lets && strlen(lets) == 1) { 1740 /* when only one item of interest, use pline instead of menus; 1741 we actually use a fake message-line menu in order to allow 1742 the user to perform selection at the --More-- prompt for tty */ 1743 ret = '\0'; 1744 for (otmp = invent; otmp; otmp = otmp->nobj) { 1745 if (otmp->invlet == lets[0]) { 1746 ret = message_menu(lets[0], 1747 want_reply ? PICK_ONE : PICK_NONE, 1748 xprname(otmp, (char *)0, lets[0], TRUE, 0L, 0L)); 1749 if (out_cnt) *out_cnt = -1L; /* select all */ 1750 break; 1751 } 1752 } 1753 return ret; 1754 } 1755 1756 start_menu(win); 1757nextclass: 1758 classcount = 0; 1759 any.a_void = 0; /* set all bits to zero */ 1760 for(otmp = invent; otmp; otmp = otmp->nobj) { 1761 ilet = otmp->invlet; 1762 if(!lets || !*lets || index(lets, ilet)) { 1763 if (!flags.sortpack || otmp->oclass == *invlet) { 1764 if (flags.sortpack && !classcount) { 1765 any.a_void = 0; /* zero */ 1766 add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings, 1767 let_to_name(*invlet, FALSE), MENU_UNSELECTED); 1768 classcount++; 1769 } 1770 any.a_char = ilet; 1771 add_menu(win, obj_to_glyph(otmp), 1772 &any, ilet, 0, ATR_NONE, doname(otmp), 1773 MENU_UNSELECTED); 1774 } 1775 } 1776 } 1777 if (flags.sortpack) { 1778 if (*++invlet) goto nextclass; 1779#ifdef WIZARD 1780 if (--invlet != venom_inv) { 1781 invlet = venom_inv; 1782 goto nextclass; 1783 } 1784#endif 1785 } 1786 end_menu(win, (char *) 0); 1787 1788 n = select_menu(win, want_reply ? PICK_ONE : PICK_NONE, &selected); 1789 if (n > 0) { 1790 ret = selected[0].item.a_char; 1791 if (out_cnt) *out_cnt = selected[0].count; 1792 free((genericptr_t)selected); 1793 } else 1794 ret = !n ? '\0' : '\033'; /* cancelled */ 1795 1796 return ret; 1797} 1798 1799/* 1800 * If lets == NULL or "", list all objects in the inventory. Otherwise, 1801 * list all objects with object classes that match the order in lets. 1802 * 1803 * Returns the letter identifier of a selected item, or 0 if nothing 1804 * was selected. 1805 */ 1806char 1807display_inventory(lets, want_reply) 1808register const char *lets; 1809boolean want_reply; 1810{ 1811 return display_pickinv(lets, want_reply, (long *)0); 1812} 1813 1814/* 1815 * Returns the number of unpaid items within the given list. This includes 1816 * contained objects. 1817 */ 1818int 1819count_unpaid(list) 1820 struct obj *list; 1821{ 1822 int count = 0; 1823 1824 while (list) { 1825 if (list->unpaid) count++; 1826 if (Has_contents(list)) 1827 count += count_unpaid(list->cobj); 1828 list = list->nobj; 1829 } 1830 return count; 1831} 1832 1833/* 1834 * Returns the number of items with b/u/c/unknown within the given list. 1835 * This does NOT include contained objects. 1836 */ 1837int 1838count_buc(list, type) 1839 struct obj *list; 1840 int type; 1841{ 1842 int count = 0; 1843 1844 while (list) { 1845 if (Role_if(PM_PRIEST)) list->bknown = TRUE; 1846 switch(type) { 1847 case BUC_BLESSED: 1848 if (list->oclass != COIN_CLASS && list->bknown && list->blessed) 1849 count++; 1850 break; 1851 case BUC_CURSED: 1852 if (list->oclass != COIN_CLASS && list->bknown && list->cursed) 1853 count++; 1854 break; 1855 case BUC_UNCURSED: 1856 if (list->oclass != COIN_CLASS && 1857 list->bknown && !list->blessed && !list->cursed) 1858 count++; 1859 break; 1860 case BUC_UNKNOWN: 1861 if (list->oclass != COIN_CLASS && !list->bknown) 1862 count++; 1863 break; 1864 default: 1865 impossible("need count of curse status %d?", type); 1866 return 0; 1867 } 1868 list = list->nobj; 1869 } 1870 return count; 1871} 1872 1873STATIC_OVL void 1874dounpaid() 1875{ 1876 winid win; 1877 struct obj *otmp, *marker; 1878 register char ilet; 1879 char *invlet = flags.inv_order; 1880 int classcount, count, num_so_far; 1881 int save_unpaid = 0; /* lint init */ 1882 long cost, totcost; 1883 1884 count = count_unpaid(invent); 1885 1886 if (count == 1) { 1887 marker = (struct obj *) 0; 1888 otmp = find_unpaid(invent, &marker); 1889 1890 /* see if the unpaid item is in the top level inventory */ 1891 for (marker = invent; marker; marker = marker->nobj) 1892 if (marker == otmp) break; 1893 1894 pline("%s", xprname(otmp, distant_name(otmp, doname), 1895 marker ? otmp->invlet : CONTAINED_SYM, 1896 TRUE, unpaid_cost(otmp), 0L)); 1897 return; 1898 } 1899 1900 win = create_nhwindow(NHW_MENU); 1901 cost = totcost = 0; 1902 num_so_far = 0; /* count of # printed so far */ 1903 if (!flags.invlet_constant) reassign(); 1904 1905 do { 1906 classcount = 0; 1907 for (otmp = invent; otmp; otmp = otmp->nobj) { 1908 ilet = otmp->invlet; 1909 if (otmp->unpaid) { 1910 if (!flags.sortpack || otmp->oclass == *invlet) { 1911 if (flags.sortpack && !classcount) { 1912 putstr(win, 0, let_to_name(*invlet, TRUE)); 1913 classcount++; 1914 } 1915 1916 totcost += cost = unpaid_cost(otmp); 1917 /* suppress "(unpaid)" suffix */ 1918 save_unpaid = otmp->unpaid; 1919 otmp->unpaid = 0; 1920 putstr(win, 0, xprname(otmp, distant_name(otmp, doname), 1921 ilet, TRUE, cost, 0L)); 1922 otmp->unpaid = save_unpaid; 1923 num_so_far++; 1924 } 1925 } 1926 } 1927 } while (flags.sortpack && (*++invlet)); 1928 1929 if (count > num_so_far) { 1930 /* something unpaid is contained */ 1931 if (flags.sortpack) 1932 putstr(win, 0, let_to_name(CONTAINED_SYM, TRUE)); 1933 /* 1934 * Search through the container objects in the inventory for 1935 * unpaid items. The top level inventory items have already 1936 * been listed. 1937 */ 1938 for (otmp = invent; otmp; otmp = otmp->nobj) { 1939 if (Has_contents(otmp)) { 1940 marker = (struct obj *) 0; /* haven't found any */ 1941 while (find_unpaid(otmp->cobj, &marker)) { 1942 totcost += cost = unpaid_cost(marker); 1943 save_unpaid = marker->unpaid; 1944 marker->unpaid = 0; /* suppress "(unpaid)" suffix */ 1945 putstr(win, 0, 1946 xprname(marker, distant_name(marker, doname), 1947 CONTAINED_SYM, TRUE, cost, 0L)); 1948 marker->unpaid = save_unpaid; 1949 } 1950 } 1951 } 1952 } 1953 1954 putstr(win, 0, ""); 1955 putstr(win, 0, xprname((struct obj *)0, "Total:", '*', FALSE, totcost, 0L)); 1956 display_nhwindow(win, FALSE); 1957 destroy_nhwindow(win); 1958} 1959 1960 1961/* query objlist callback: return TRUE if obj type matches "this_type" */ 1962static int this_type; 1963 1964STATIC_OVL boolean 1965this_type_only(obj) 1966 struct obj *obj; 1967{ 1968 return (obj->oclass == this_type); 1969} 1970 1971/* the 'I' command */ 1972int 1973dotypeinv() 1974{ 1975 char c = '\0'; 1976 int n, i = 0; 1977 char *extra_types, types[BUFSZ]; 1978 int class_count, oclass, unpaid_count, itemcount; 1979 boolean billx = *u.ushops && doinvbill(0); 1980 menu_item *pick_list; 1981 boolean traditional = TRUE; 1982 const char *prompt = "What type of object do you want an inventory of?"; 1983 1984#ifndef GOLDOBJ 1985 if (!invent && !u.ugold && !billx) { 1986#else 1987 if (!invent && !billx) { 1988#endif 1989 You("aren't carrying anything."); 1990 return 0; 1991 } 1992 unpaid_count = count_unpaid(invent); 1993 if (flags.menu_style != MENU_TRADITIONAL) { 1994 if (flags.menu_style == MENU_FULL || 1995 flags.menu_style == MENU_PARTIAL) { 1996 traditional = FALSE; 1997 i = UNPAID_TYPES; 1998 if (billx) i |= BILLED_TYPES; 1999 n = query_category(prompt, invent, i, &pick_list, PICK_ONE); 2000 if (!n) return 0; 2001 this_type = c = pick_list[0].item.a_int; 2002 free((genericptr_t) pick_list); 2003 } 2004 } 2005 if (traditional) { 2006 /* collect a list of classes of objects carried, for use as a prompt */ 2007 types[0] = 0; 2008 class_count = collect_obj_classes(types, invent, 2009 FALSE, 2010#ifndef GOLDOBJ 2011 (u.ugold != 0), 2012#endif 2013 (boolean FDECL((*),(OBJ_P))) 0, &itemcount); 2014 if (unpaid_count) { 2015 Strcat(types, "u"); 2016 class_count++; 2017 } 2018 if (billx) { 2019 Strcat(types, "x"); 2020 class_count++; 2021 } 2022 /* add everything not already included; user won't see these */ 2023 extra_types = eos(types); 2024 *extra_types++ = '\033'; 2025 if (!unpaid_count) *extra_types++ = 'u'; 2026 if (!billx) *extra_types++ = 'x'; 2027 *extra_types = '\0'; /* for index() */ 2028 for (i = 0; i < MAXOCLASSES; i++) 2029 if (!index(types, def_oc_syms[i])) { 2030 *extra_types++ = def_oc_syms[i]; 2031 *extra_types = '\0'; 2032 } 2033 2034 if(class_count > 1) { 2035 c = yn_function(prompt, types, '\0'); 2036#ifdef REDO 2037 savech(c); 2038#endif 2039 if(c == '\0') { 2040 clear_nhwindow(WIN_MESSAGE); 2041 return 0; 2042 } 2043 } else { 2044 /* only one thing to itemize */ 2045 if (unpaid_count) 2046 c = 'u'; 2047 else if (billx) 2048 c = 'x'; 2049 else 2050 c = types[0]; 2051 } 2052 } 2053 if (c == 'x') { 2054 if (billx) 2055 (void) doinvbill(1); 2056 else 2057 pline("No used-up objects on your shopping bill."); 2058 return 0; 2059 } 2060 if (c == 'u') { 2061 if (unpaid_count) 2062 dounpaid(); 2063 else 2064 You("are not carrying any unpaid objects."); 2065 return 0; 2066 } 2067 if (traditional) { 2068 oclass = def_char_to_objclass(c); /* change to object class */ 2069 if (oclass == COIN_CLASS) { 2070 return doprgold(); 2071 } else if (index(types, c) > index(types, '\033')) { 2072 You("have no such objects."); 2073 return 0; 2074 } 2075 this_type = oclass; 2076 } 2077 if (query_objlist((char *) 0, invent, 2078 (flags.invlet_constant ? USE_INVLET : 0)|INVORDER_SORT, 2079 &pick_list, PICK_NONE, this_type_only) > 0) 2080 free((genericptr_t)pick_list); 2081 return 0; 2082} 2083 2084/* return a string describing the dungeon feature at <x,y> if there 2085 is one worth mentioning at that location; otherwise null */ 2086const char * 2087dfeature_at(x, y, buf) 2088int x, y; 2089char *buf; 2090{ 2091 struct rm *lev = &levl[x][y]; 2092 int ltyp = lev->typ, cmap = -1; 2093 const char *dfeature = 0; 2094 static char altbuf[BUFSZ]; 2095 2096 if (IS_DOOR(ltyp)) { 2097 switch (lev->doormask) { 2098 case D_NODOOR: cmap = S_ndoor; break; /* "doorway" */ 2099 case D_ISOPEN: cmap = S_vodoor; break; /* "open door" */ 2100 case D_BROKEN: dfeature = "broken door"; break; 2101 default: cmap = S_vcdoor; break; /* "closed door" */ 2102 } 2103 /* override door description for open drawbridge */ 2104 if (is_drawbridge_wall(x, y) >= 0) 2105 dfeature = "open drawbridge portcullis", cmap = -1; 2106 } else if (IS_FOUNTAIN(ltyp)) 2107 cmap = S_fountain; /* "fountain" */ 2108 else if (IS_THRONE(ltyp)) 2109 cmap = S_throne; /* "opulent throne" */ 2110 else if (is_lava(x,y)) 2111 cmap = S_lava; /* "molten lava" */ 2112 else if (is_ice(x,y)) 2113 cmap = S_ice; /* "ice" */ 2114 else if (is_pool(x,y)) 2115 dfeature = "pool of water"; 2116#ifdef SINKS 2117 else if (IS_SINK(ltyp)) 2118 cmap = S_sink; /* "sink" */ 2119#endif 2120 else if (IS_ALTAR(ltyp)) { 2121 Sprintf(altbuf, "altar to %s (%s)", a_gname(), 2122 align_str(Amask2align(lev->altarmask & ~AM_SHRINE))); 2123 dfeature = altbuf; 2124 } else if ((x == xupstair && y == yupstair) || 2125 (x == sstairs.sx && y == sstairs.sy && sstairs.up)) 2126 cmap = S_upstair; /* "staircase up" */ 2127 else if ((x == xdnstair && y == ydnstair) || 2128 (x == sstairs.sx && y == sstairs.sy && !sstairs.up)) 2129 cmap = S_dnstair; /* "staircase down" */ 2130 else if (x == xupladder && y == yupladder) 2131 cmap = S_upladder; /* "ladder up" */ 2132 else if (x == xdnladder && y == ydnladder) 2133 cmap = S_dnladder; /* "ladder down" */ 2134 else if (ltyp == DRAWBRIDGE_DOWN) 2135 cmap = S_vodbridge; /* "lowered drawbridge" */ 2136 else if (ltyp == DBWALL) 2137 cmap = S_vcdbridge; /* "raised drawbridge" */ 2138 else if (IS_GRAVE(ltyp)) 2139 cmap = S_grave; /* "grave" */ 2140 else if (ltyp == TREE) 2141 cmap = S_tree; /* "tree" */ 2142 else if (ltyp == IRONBARS) 2143 dfeature = "set of iron bars"; 2144 2145 if (cmap >= 0) dfeature = defsyms[cmap].explanation; 2146 if (dfeature) Strcpy(buf, dfeature); 2147 return dfeature; 2148} 2149 2150/* look at what is here; if there are many objects (5 or more), 2151 don't show them unless obj_cnt is 0 */ 2152int 2153look_here(obj_cnt, picked_some) 2154int obj_cnt; /* obj_cnt > 0 implies that autopickup is in progess */ 2155boolean picked_some; 2156{ 2157 struct obj *otmp; 2158 struct trap *trap; 2159 const char *verb = Blind ? "feel" : "see"; 2160 const char *dfeature = (char *)0; 2161 char fbuf[BUFSZ], fbuf2[BUFSZ]; 2162 winid tmpwin; 2163 boolean skip_objects = (obj_cnt >= 5), felt_cockatrice = FALSE; 2164 2165 if (u.uswallow && u.ustuck) { 2166 struct monst *mtmp = u.ustuck; 2167 Sprintf(fbuf, "Contents of %s %s", 2168 s_suffix(mon_nam(mtmp)), mbodypart(mtmp, STOMACH)); 2169 /* Skip "Contents of " by using fbuf index 12 */ 2170 You("%s to %s what is lying in %s.", 2171 Blind ? "try" : "look around", verb, &fbuf[12]); 2172 otmp = mtmp->minvent; 2173 if (otmp) { 2174 for ( ; otmp; otmp = otmp->nobj) { 2175 /* If swallower is an animal, it should have become stone but... */ 2176 if (otmp->otyp == CORPSE) feel_cockatrice(otmp, FALSE); 2177 } 2178 if (Blind) Strcpy(fbuf, "You feel"); 2179 Strcat(fbuf,":"); 2180 (void) display_minventory(mtmp, MINV_ALL, fbuf); 2181 } else { 2182 You("%s no objects here.", verb); 2183 } 2184 return(!!Blind); 2185 } 2186 if (!skip_objects && (trap = t_at(u.ux,u.uy)) && trap->tseen) 2187 There("is %s here.", 2188 an(defsyms[trap_to_defsym(trap->ttyp)].explanation)); 2189 2190 otmp = level.objects[u.ux][u.uy]; 2191 dfeature = dfeature_at(u.ux, u.uy, fbuf2); 2192 if (dfeature && !strcmp(dfeature, "pool of water") && Underwater) 2193 dfeature = 0; 2194 2195 if (Blind) { 2196 boolean drift = Is_airlevel(&u.uz) || Is_waterlevel(&u.uz); 2197 if (dfeature && !strncmp(dfeature, "altar ", 6)) { 2198 /* don't say "altar" twice, dfeature has more info */ 2199 You("try to feel what is here."); 2200 } else { 2201 You("try to feel what is %s%s.", 2202 drift ? "floating here" : "lying here on the ", 2203 drift ? "" : surface(u.ux, u.uy)); 2204 } 2205 if (dfeature && !drift && !strcmp(dfeature, surface(u.ux,u.uy))) 2206 dfeature = 0; /* ice already identifed */ 2207 if (!can_reach_floor()) { 2208 pline("But you can't reach it!"); 2209 return(0); 2210 } 2211 } 2212 2213 if (dfeature) 2214 Sprintf(fbuf, "There is %s here.", an(dfeature)); 2215 2216 if (!otmp || is_lava(u.ux,u.uy) || (is_pool(u.ux,u.uy) && !Underwater)) { 2217 if (dfeature) pline(fbuf); 2218 read_engr_at(u.ux, u.uy); /* Eric Backus */ 2219 if (!skip_objects && (Blind || !dfeature)) 2220 You("%s no objects here.", verb); 2221 return(!!Blind); 2222 } 2223 /* we know there is something here */ 2224 2225 if (skip_objects) { 2226 if (dfeature) pline(fbuf); 2227 read_engr_at(u.ux, u.uy); /* Eric Backus */ 2228 There("are %s%s objects here.", 2229 (obj_cnt <= 10) ? "several" : "many", 2230 picked_some ? " more" : ""); 2231 } else if (!otmp->nexthere) { 2232 /* only one object */ 2233 if (dfeature) pline(fbuf); 2234 read_engr_at(u.ux, u.uy); /* Eric Backus */ 2235#ifdef INVISIBLE_OBJECTS 2236 if (otmp->oinvis && !See_invisible) verb = "feel"; 2237#endif 2238 You("%s here %s.", verb, doname(otmp)); 2239 if (otmp->otyp == CORPSE) feel_cockatrice(otmp, FALSE); 2240 } else { 2241 display_nhwindow(WIN_MESSAGE, FALSE); 2242 tmpwin = create_nhwindow(NHW_MENU); 2243 if(dfeature) { 2244 putstr(tmpwin, 0, fbuf); 2245 putstr(tmpwin, 0, ""); 2246 } 2247 putstr(tmpwin, 0, Blind ? "Things that you feel here:" : 2248 "Things that are here:"); 2249 for ( ; otmp; otmp = otmp->nexthere) { 2250 if (otmp->otyp == CORPSE && will_feel_cockatrice(otmp, FALSE)) { 2251 char buf[BUFSZ]; 2252 felt_cockatrice = TRUE; 2253 Strcpy(buf, doname(otmp)); 2254 Strcat(buf, "..."); 2255 putstr(tmpwin, 0, buf); 2256 break; 2257 } 2258 putstr(tmpwin, 0, doname(otmp)); 2259 } 2260 display_nhwindow(tmpwin, TRUE); 2261 destroy_nhwindow(tmpwin); 2262 if (felt_cockatrice) feel_cockatrice(otmp, FALSE); 2263 read_engr_at(u.ux, u.uy); /* Eric Backus */ 2264 } 2265 return(!!Blind); 2266} 2267 2268/* explicilty look at what is here, including all objects */ 2269int 2270dolook() 2271{ 2272 return look_here(0, FALSE); 2273} 2274 2275boolean 2276will_feel_cockatrice(otmp, force_touch) 2277struct obj *otmp; 2278boolean force_touch; 2279{ 2280 if ((Blind || force_touch) && !uarmg && !Stone_resistance && 2281 (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm]))) 2282 return TRUE; 2283 return FALSE; 2284} 2285 2286void 2287feel_cockatrice(otmp, force_touch) 2288struct obj *otmp; 2289boolean force_touch; 2290{ 2291 char kbuf[BUFSZ]; 2292 2293 if (will_feel_cockatrice(otmp, force_touch)) { 2294 if(poly_when_stoned(youmonst.data)) 2295 You("touched the %s corpse with your bare %s.", 2296 mons[otmp->corpsenm].mname, makeplural(body_part(HAND))); 2297 else 2298 pline("Touching the %s corpse is a fatal mistake...", 2299 mons[otmp->corpsenm].mname); 2300 Sprintf(kbuf, "%s corpse", an(mons[otmp->corpsenm].mname)); 2301 instapetrify(kbuf); 2302 } 2303} 2304 2305#endif /* OVLB */ 2306#ifdef OVL1 2307 2308void 2309stackobj(obj) 2310struct obj *obj; 2311{ 2312 struct obj *otmp; 2313 2314 for(otmp = level.objects[obj->ox][obj->oy]; otmp; otmp = otmp->nexthere) 2315 if(otmp != obj && merged(&obj,&otmp)) 2316 break; 2317 return; 2318} 2319 2320STATIC_OVL boolean 2321mergable(otmp, obj) /* returns TRUE if obj & otmp can be merged */ 2322 register struct obj *otmp, *obj; 2323{ 2324 if (obj->otyp != otmp->otyp) return FALSE; 2325#ifdef GOLDOBJ 2326 /* coins of the same kind will always merge */ 2327 if (obj->oclass == COIN_CLASS) return TRUE; 2328#endif 2329 if (obj->unpaid != otmp->unpaid || 2330 obj->spe != otmp->spe || obj->dknown != otmp->dknown || 2331 (obj->bknown != otmp->bknown && !Role_if(PM_PRIEST)) || 2332 obj->cursed != otmp->cursed || obj->blessed != otmp->blessed || 2333 obj->no_charge != otmp->no_charge || 2334 obj->obroken != otmp->obroken || 2335 obj->otrapped != otmp->otrapped || 2336 obj->lamplit != otmp->lamplit || 2337#ifdef INVISIBLE_OBJECTS 2338 obj->oinvis != otmp->oinvis || 2339#endif 2340 obj->greased != otmp->greased || 2341 obj->oeroded != otmp->oeroded || 2342 obj->oeroded2 != otmp->oeroded2 || 2343 obj->bypass != otmp->bypass) 2344 return(FALSE); 2345 2346 if ((obj->oclass==WEAPON_CLASS || obj->oclass==ARMOR_CLASS) && 2347 (obj->oerodeproof!=otmp->oerodeproof || obj->rknown!=otmp->rknown)) 2348 return FALSE; 2349 2350 if (obj->oclass == FOOD_CLASS && (obj->oeaten != otmp->oeaten || 2351 obj->orotten != otmp->orotten)) 2352 return(FALSE); 2353 2354 if (obj->otyp == CORPSE || obj->otyp == EGG || obj->otyp == TIN) { 2355 if (obj->corpsenm != otmp->corpsenm) 2356 return FALSE; 2357 } 2358 2359 /* hatching eggs don't merge; ditto for revivable corpses */ 2360 if ((obj->otyp == EGG && (obj->timed || otmp->timed)) || 2361 (obj->otyp == CORPSE && otmp->corpsenm >= LOW_PM && 2362 is_reviver(&mons[otmp->corpsenm]))) 2363 return FALSE; 2364 2365 /* allow candle merging only if their ages are close */ 2366 /* see begin_burn() for a reference for the magic "25" */ 2367 if (Is_candle(obj) && obj->age/25 != otmp->age/25) 2368 return(FALSE); 2369 2370 /* burning potions of oil never merge */ 2371 if (obj->otyp == POT_OIL && obj->lamplit) 2372 return FALSE; 2373 2374 /* don't merge surcharged item with base-cost item */ 2375 if (obj->unpaid && !same_price(obj, otmp)) 2376 return FALSE; 2377 2378 /* if they have names, make sure they're the same */ 2379 if ( (obj->onamelth != otmp->onamelth && 2380 ((obj->onamelth && otmp->onamelth) || obj->otyp == CORPSE) 2381 ) || 2382 (obj->onamelth && otmp->onamelth && 2383 strncmp(ONAME(obj), ONAME(otmp), (int)obj->onamelth))) 2384 return FALSE; 2385 2386 /* for the moment, any additional information is incompatible */ 2387 if (obj->oxlth || otmp->oxlth) return FALSE; 2388 2389 if(obj->oartifact != otmp->oartifact) return FALSE; 2390 2391 if(obj->known == otmp->known || 2392 !objects[otmp->otyp].oc_uses_known) { 2393 return((boolean)(objects[obj->otyp].oc_merge)); 2394 } else return(FALSE); 2395} 2396 2397int 2398doprgold() 2399{ 2400 /* the messages used to refer to "carrying gold", but that didn't 2401 take containers into account */ 2402#ifndef GOLDOBJ 2403 if(!u.ugold) 2404 Your("wallet is empty."); 2405 else 2406 Your("wallet contains %ld gold piece%s.", u.ugold, plur(u.ugold)); 2407#else 2408 long umoney = money_cnt(invent); 2409 if(!umoney) 2410 Your("wallet is empty."); 2411 else 2412 Your("wallet contains %ld %s.", umoney, currency(umoney)); 2413#endif 2414 shopper_financial_report(); 2415 return 0; 2416} 2417 2418#endif /* OVL1 */ 2419#ifdef OVLB 2420 2421int 2422doprwep() 2423{ 2424 if (!uwep) { 2425 You("are empty %s.", body_part(HANDED)); 2426 } else { 2427 prinv((char *)0, uwep, 0L); 2428 if (u.twoweap) prinv((char *)0, uswapwep, 0L); 2429 } 2430 return 0; 2431} 2432 2433int 2434doprarm() 2435{ 2436 if(!wearing_armor()) 2437 You("are not wearing any armor."); 2438 else { 2439#ifdef TOURIST 2440 char lets[8]; 2441#else 2442 char lets[7]; 2443#endif 2444 register int ct = 0; 2445 2446#ifdef TOURIST 2447 if(uarmu) lets[ct++] = obj_to_let(uarmu); 2448#endif 2449 if(uarm) lets[ct++] = obj_to_let(uarm); 2450 if(uarmc) lets[ct++] = obj_to_let(uarmc); 2451 if(uarmh) lets[ct++] = obj_to_let(uarmh); 2452 if(uarms) lets[ct++] = obj_to_let(uarms); 2453 if(uarmg) lets[ct++] = obj_to_let(uarmg); 2454 if(uarmf) lets[ct++] = obj_to_let(uarmf); 2455 lets[ct] = 0; 2456 (void) display_inventory(lets, FALSE); 2457 } 2458 return 0; 2459} 2460 2461int 2462doprring() 2463{ 2464 if(!uleft && !uright) 2465 You("are not wearing any rings."); 2466 else { 2467 char lets[3]; 2468 register int ct = 0; 2469 2470 if(uleft) lets[ct++] = obj_to_let(uleft); 2471 if(uright) lets[ct++] = obj_to_let(uright); 2472 lets[ct] = 0; 2473 (void) display_inventory(lets, FALSE); 2474 } 2475 return 0; 2476} 2477 2478int 2479dopramulet() 2480{ 2481 if (!uamul) 2482 You("are not wearing an amulet."); 2483 else 2484 prinv((char *)0, uamul, 0L); 2485 return 0; 2486} 2487 2488STATIC_OVL boolean 2489tool_in_use(obj) 2490struct obj *obj; 2491{ 2492 if ((obj->owornmask & (W_TOOL 2493#ifdef STEED 2494 | W_SADDLE 2495#endif 2496 )) != 0L) return TRUE; 2497 if (obj->oclass != TOOL_CLASS) return FALSE; 2498 return (boolean)(obj == uwep || obj->lamplit || 2499 (obj->otyp == LEASH && obj->leashmon)); 2500} 2501 2502int 2503doprtool() 2504{ 2505 struct obj *otmp; 2506 int ct = 0; 2507 char lets[52+1]; 2508 2509 for (otmp = invent; otmp; otmp = otmp->nobj) 2510 if (tool_in_use(otmp)) 2511 lets[ct++] = obj_to_let(otmp); 2512 lets[ct] = '\0'; 2513 if (!ct) You("are not using any tools."); 2514 else (void) display_inventory(lets, FALSE); 2515 return 0; 2516} 2517 2518/* '*' command; combines the ')' + '[' + '=' + '"' + '(' commands; 2519 show inventory of all currently wielded, worn, or used objects */ 2520int 2521doprinuse() 2522{ 2523 struct obj *otmp; 2524 int ct = 0; 2525 char lets[52+1]; 2526 2527 for (otmp = invent; otmp; otmp = otmp->nobj) 2528 if (is_worn(otmp) || tool_in_use(otmp)) 2529 lets[ct++] = obj_to_let(otmp); 2530 lets[ct] = '\0'; 2531 if (!ct) You("are not wearing or wielding anything."); 2532 else (void) display_inventory(lets, FALSE); 2533 return 0; 2534} 2535 2536/* 2537 * uses up an object that's on the floor, charging for it as necessary 2538 */ 2539void 2540useupf(obj, numused) 2541register struct obj *obj; 2542long numused; 2543{ 2544 register struct obj *otmp; 2545 boolean at_u = (obj->ox == u.ux && obj->oy == u.uy); 2546 2547 /* burn_floor_paper() keeps an object pointer that it tries to 2548 * useupf() multiple times, so obj must survive if plural */ 2549 if (obj->quan > numused) 2550 otmp = splitobj(obj, numused); 2551 else 2552 otmp = obj; 2553 if(costly_spot(otmp->ox, otmp->oy)) { 2554 if(index(u.urooms, *in_rooms(otmp->ox, otmp->oy, 0))) 2555 addtobill(otmp, FALSE, FALSE, FALSE); 2556 else (void)stolen_value(otmp, otmp->ox, otmp->oy, FALSE, FALSE); 2557 } 2558 delobj(otmp); 2559 if (at_u && u.uundetected && hides_under(youmonst.data)) 2560 u.uundetected = OBJ_AT(u.ux, u.uy); 2561} 2562 2563#endif /* OVLB */ 2564 2565 2566#ifdef OVL1 2567 2568/* 2569 * Conversion from a class to a string for printing. 2570 * This must match the object class order. 2571 */ 2572STATIC_VAR NEARDATA const char *names[] = { 0, 2573 "Illegal objects", "Weapons", "Armor", "Rings", "Amulets", 2574 "Tools", "Comestibles", "Potions", "Scrolls", "Spellbooks", 2575 "Wands", "Coins", "Gems", "Boulders/Statues", "Iron balls", 2576 "Chains", "Venoms" 2577}; 2578 2579static NEARDATA const char oth_symbols[] = { 2580 CONTAINED_SYM, 2581 '\0' 2582}; 2583 2584static NEARDATA const char *oth_names[] = { 2585 "Bagged/Boxed items" 2586}; 2587 2588static NEARDATA char *invbuf = (char *)0; 2589static NEARDATA unsigned invbufsiz = 0; 2590 2591char * 2592let_to_name(let,unpaid) 2593char let; 2594boolean unpaid; 2595{ 2596 const char *class_name; 2597 const char *pos; 2598 int oclass = (let >= 1 && let < MAXOCLASSES) ? let : 0; 2599 unsigned len; 2600 2601 if (oclass) 2602 class_name = names[oclass]; 2603 else if ((pos = index(oth_symbols, let)) != 0) 2604 class_name = oth_names[pos - oth_symbols]; 2605 else 2606 class_name = names[0]; 2607 2608 len = strlen(class_name) + (unpaid ? sizeof "unpaid_" : sizeof ""); 2609 if (len > invbufsiz) { 2610 if (invbuf) free((genericptr_t)invbuf); 2611 invbufsiz = len + 10; /* add slop to reduce incremental realloc */ 2612 invbuf = (char *) alloc(invbufsiz); 2613 } 2614 if (unpaid) 2615 Strcat(strcpy(invbuf, "Unpaid "), class_name); 2616 else 2617 Strcpy(invbuf, class_name); 2618 return invbuf; 2619} 2620 2621void 2622free_invbuf() 2623{ 2624 if (invbuf) free((genericptr_t)invbuf), invbuf = (char *)0; 2625 invbufsiz = 0; 2626} 2627 2628#endif /* OVL1 */ 2629#ifdef OVLB 2630 2631void 2632reassign() 2633{ 2634 register int i; 2635 register struct obj *obj; 2636 2637 for(obj = invent, i = 0; obj; obj = obj->nobj, i++) 2638 obj->invlet = (i < 26) ? ('a'+i) : ('A'+i-26); 2639 lastinvnr = i; 2640} 2641 2642#endif /* OVLB */ 2643#ifdef OVL1 2644 2645int 2646doorganize() /* inventory organizer by Del Lamb */ 2647{ 2648 struct obj *obj, *otmp; 2649 register int ix, cur; 2650 register char let; 2651 char alphabet[52+1], buf[52+1]; 2652 char qbuf[QBUFSZ]; 2653 char allowall[2]; 2654 const char *adj_type; 2655 2656 if (!flags.invlet_constant) reassign(); 2657 /* get a pointer to the object the user wants to organize */ 2658 allowall[0] = ALL_CLASSES; allowall[1] = '\0'; 2659 if (!(obj = getobj(allowall,"adjust"))) return(0); 2660 2661 /* initialize the list with all upper and lower case letters */ 2662 for (let = 'a', ix = 0; let <= 'z';) alphabet[ix++] = let++; 2663 for (let = 'A', ix = 26; let <= 'Z';) alphabet[ix++] = let++; 2664 alphabet[52] = 0; 2665 2666 /* blank out all the letters currently in use in the inventory */ 2667 /* except those that will be merged with the selected object */ 2668 for (otmp = invent; otmp; otmp = otmp->nobj) 2669 if (otmp != obj && !mergable(otmp,obj)) { 2670 if (otmp->invlet <= 'Z') 2671 alphabet[(otmp->invlet) - 'A' + 26] = ' '; 2672 else alphabet[(otmp->invlet) - 'a'] = ' '; 2673 } 2674 2675 /* compact the list by removing all the blanks */ 2676 for (ix = cur = 0; ix <= 52; ix++) 2677 if (alphabet[ix] != ' ') buf[cur++] = alphabet[ix]; 2678 2679 /* and by dashing runs of letters */ 2680 if(cur > 5) compactify(buf); 2681 2682 /* get new letter to use as inventory letter */ 2683 for (;;) { 2684 Sprintf(qbuf, "Adjust letter to what [%s]?",buf); 2685 let = yn_function(qbuf, (char *)0, '\0'); 2686 if(index(quitchars,let)) { 2687 pline(Never_mind); 2688 return(0); 2689 } 2690 if (let == '@' || !letter(let)) 2691 pline("Select an inventory slot letter."); 2692 else 2693 break; 2694 } 2695 2696 /* change the inventory and print the resulting item */ 2697 adj_type = "Moving:"; 2698 2699 /* 2700 * don't use freeinv/addinv to avoid double-touching artifacts, 2701 * dousing lamps, losing luck, cursing loadstone, etc. 2702 */ 2703 extract_nobj(obj, &invent); 2704 2705 for (otmp = invent; otmp;) 2706 if (merged(&otmp,&obj)) { 2707 adj_type = "Merging:"; 2708 obj = otmp; 2709 otmp = otmp->nobj; 2710 extract_nobj(obj, &invent); 2711 } else { 2712 if (otmp->invlet == let) { 2713 adj_type = "Swapping:"; 2714 otmp->invlet = obj->invlet; 2715 } 2716 otmp = otmp->nobj; 2717 } 2718 2719 /* inline addinv (assuming flags.invlet_constant and !merged) */ 2720 obj->invlet = let; 2721 obj->nobj = invent; /* insert at beginning */ 2722 obj->where = OBJ_INVENT; 2723 invent = obj; 2724 reorder_invent(); 2725 2726 prinv(adj_type, obj, 0L); 2727 update_inventory(); 2728 return(0); 2729} 2730 2731/* common to display_minventory and display_cinventory */ 2732STATIC_OVL void 2733invdisp_nothing(hdr, txt) 2734const char *hdr, *txt; 2735{ 2736 winid win; 2737 anything any; 2738 menu_item *selected; 2739 2740 any.a_void = 0; 2741 win = create_nhwindow(NHW_MENU); 2742 start_menu(win); 2743 add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings, hdr, MENU_UNSELECTED); 2744 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); 2745 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, txt, MENU_UNSELECTED); 2746 end_menu(win, (char *)0); 2747 if (select_menu(win, PICK_NONE, &selected) > 0) 2748 free((genericptr_t)selected); 2749 destroy_nhwindow(win); 2750 return; 2751} 2752 2753/* query_objlist callback: return things that could possibly be worn/wielded */ 2754STATIC_OVL boolean 2755worn_wield_only(obj) 2756struct obj *obj; 2757{ 2758 return (obj->oclass == WEAPON_CLASS 2759 || obj->oclass == ARMOR_CLASS 2760 || obj->oclass == AMULET_CLASS 2761 || obj->oclass == RING_CLASS 2762 || obj->oclass == TOOL_CLASS); 2763} 2764 2765/* 2766 * Display a monster's inventory. 2767 * Returns a pointer to the object from the monster's inventory selected 2768 * or NULL if nothing was selected. 2769 * 2770 * By default, only worn and wielded items are displayed. The caller 2771 * can pick one. Modifier flags are: 2772 * 2773 * MINV_NOLET - nothing selectable 2774 * MINV_ALL - display all inventory 2775 */ 2776struct obj * 2777display_minventory(mon, dflags, title) 2778register struct monst *mon; 2779int dflags; 2780char *title; 2781{ 2782 struct obj *ret; 2783#ifndef GOLDOBJ 2784 struct obj m_gold; 2785#endif 2786 char tmp[QBUFSZ]; 2787 int n; 2788 menu_item *selected = 0; 2789#ifndef GOLDOBJ 2790 int do_all = (dflags & MINV_ALL) != 0, 2791 do_gold = (do_all && mon->mgold); 2792#else 2793 int do_all = (dflags & MINV_ALL) != 0; 2794#endif 2795 2796 Sprintf(tmp,"%s %s:", s_suffix(noit_Monnam(mon)), 2797 do_all ? "possessions" : "armament"); 2798 2799#ifndef GOLDOBJ 2800 if (do_all ? (mon->minvent || mon->mgold) 2801#else 2802 if (do_all ? (mon->minvent != 0) 2803#endif 2804 : (mon->misc_worn_check || MON_WEP(mon))) { 2805 /* Fool the 'weapon in hand' routine into 2806 * displaying 'weapon in claw', etc. properly. 2807 */ 2808 youmonst.data = mon->data; 2809 2810#ifndef GOLDOBJ 2811 if (do_gold) { 2812 /* 2813 * Make temporary gold object and insert at the head of 2814 * the mon's inventory. We can get away with using a 2815 * stack variable object because monsters don't carry 2816 * gold in their inventory, so it won't merge. 2817 */ 2818 m_gold = zeroobj; 2819 m_gold.otyp = GOLD_PIECE; m_gold.oclass = COIN_CLASS; 2820 m_gold.quan = mon->mgold; m_gold.dknown = 1; 2821 m_gold.where = OBJ_FREE; 2822 /* we had better not merge and free this object... */ 2823 if (add_to_minv(mon, &m_gold)) 2824 panic("display_minventory: static object freed."); 2825 } 2826 2827#endif 2828 n = query_objlist(title ? title : tmp, mon->minvent, INVORDER_SORT, &selected, 2829 (dflags & MINV_NOLET) ? PICK_NONE : PICK_ONE, 2830 do_all ? allow_all : worn_wield_only); 2831 2832#ifndef GOLDOBJ 2833 if (do_gold) obj_extract_self(&m_gold); 2834#endif 2835 2836 set_uasmon(); 2837 } else { 2838 invdisp_nothing(title ? title : tmp, "(none)"); 2839 n = 0; 2840 } 2841 2842 if (n > 0) { 2843 ret = selected[0].item.a_obj; 2844 free((genericptr_t)selected); 2845#ifndef GOLDOBJ 2846 /* 2847 * Unfortunately, we can't return a pointer to our temporary 2848 * gold object. We'll have to work out a scheme where this 2849 * can happen. Maybe even put gold in the inventory list... 2850 */ 2851 if (ret == &m_gold) ret = (struct obj *) 0; 2852#endif 2853 } else 2854 ret = (struct obj *) 0; 2855 return ret; 2856} 2857 2858/* 2859 * Display the contents of a container in inventory style. 2860 * Currently, this is only used for statues, via wand of probing. 2861 */ 2862struct obj * 2863display_cinventory(obj) 2864register struct obj *obj; 2865{ 2866 struct obj *ret; 2867 char tmp[QBUFSZ]; 2868 int n; 2869 menu_item *selected = 0; 2870 2871 Sprintf(tmp,"Contents of %s:", doname(obj)); 2872 2873 if (obj->cobj) { 2874 n = query_objlist(tmp, obj->cobj, INVORDER_SORT, &selected, 2875 PICK_NONE, allow_all); 2876 } else { 2877 invdisp_nothing(tmp, "(empty)"); 2878 n = 0; 2879 } 2880 if (n > 0) { 2881 ret = selected[0].item.a_obj; 2882 free((genericptr_t)selected); 2883 } else 2884 ret = (struct obj *) 0; 2885 return ret; 2886} 2887 2888/* query objlist callback: return TRUE if obj is at given location */ 2889static coord only; 2890 2891STATIC_OVL boolean 2892only_here(obj) 2893 struct obj *obj; 2894{ 2895 return (obj->ox == only.x && obj->oy == only.y); 2896} 2897 2898/* 2899 * Display a list of buried items in inventory style. Return a non-zero 2900 * value if there were items at that spot. 2901 * 2902 * Currently, this is only used with a wand of probing zapped downwards. 2903 */ 2904int 2905display_binventory(x, y, as_if_seen) 2906int x, y; 2907boolean as_if_seen; 2908{ 2909 struct obj *obj; 2910 menu_item *selected = 0; 2911 int n; 2912 2913 /* count # of objects here */ 2914 for (n = 0, obj = level.buriedobjlist; obj; obj = obj->nobj) 2915 if (obj->ox == x && obj->oy == y) { 2916 if (as_if_seen) obj->dknown = 1; 2917 n++; 2918 } 2919 2920 if (n) { 2921 only.x = x; 2922 only.y = y; 2923 if (query_objlist("Things that are buried here:", 2924 level.buriedobjlist, INVORDER_SORT, 2925 &selected, PICK_NONE, only_here) > 0) 2926 free((genericptr_t)selected); 2927 only.x = only.y = 0; 2928 } 2929 return n; 2930} 2931 2932#endif /* OVL1 */ 2933 2934/*invent.c*/ 2935