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