1 /* NetHack 3.6 invent.c $NHDT-Date: 1575245062 2019/12/02 00:04:22 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.267 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Derek S. Ray, 2015. */
4 /* NetHack may be freely redistributed. See license for details. */
5
6 #include "hack.h"
7
8 #ifndef C /* same as cmd.c */
9 #define C(c) (0x1f & (c))
10 #endif
11
12 #define NOINVSYM '#'
13 #define CONTAINED_SYM '>' /* designator for inside a container */
14 #define HANDS_SYM '-'
15
16 STATIC_DCL void FDECL(loot_classify, (Loot *, struct obj *));
17 STATIC_DCL char *FDECL(loot_xname, (struct obj *));
18 STATIC_DCL int FDECL(CFDECLSPEC sortloot_cmp, (const genericptr,
19 const genericptr));
20 STATIC_DCL void NDECL(reorder_invent);
21 STATIC_DCL void FDECL(noarmor, (BOOLEAN_P));
22 STATIC_DCL void FDECL(invdisp_nothing, (const char *, const char *));
23 STATIC_DCL boolean FDECL(worn_wield_only, (struct obj *));
24 STATIC_DCL boolean FDECL(only_here, (struct obj *));
25 STATIC_DCL void FDECL(compactify, (char *));
26 STATIC_DCL boolean FDECL(taking_off, (const char *));
27 STATIC_DCL boolean FDECL(putting_on, (const char *));
28 STATIC_PTR int FDECL(ckvalidcat, (struct obj *));
29 STATIC_PTR int FDECL(ckunpaid, (struct obj *));
30 STATIC_PTR char *FDECL(safeq_xprname, (struct obj *));
31 STATIC_PTR char *FDECL(safeq_shortxprname, (struct obj *));
32 STATIC_DCL char FDECL(display_pickinv, (const char *, const char *,
33 const char *, BOOLEAN_P, long *));
34 STATIC_DCL char FDECL(display_used_invlets, (CHAR_P));
35 STATIC_DCL boolean FDECL(this_type_only, (struct obj *));
36 STATIC_DCL void NDECL(dounpaid);
37 STATIC_DCL struct obj *FDECL(find_unpaid, (struct obj *, struct obj **));
38 STATIC_DCL void FDECL(menu_identify, (int));
39 STATIC_DCL boolean FDECL(tool_in_use, (struct obj *));
40 STATIC_DCL char FDECL(obj_to_let, (struct obj *));
41
42 static int lastinvnr = 51; /* 0 ... 51 (never saved&restored) */
43
44 /* wizards can wish for venom, which will become an invisible inventory
45 * item without this. putting it in inv_order would mean venom would
46 * suddenly become a choice for all the inventory-class commands, which
47 * would probably cause mass confusion. the test for inventory venom
48 * is only WIZARD and not wizard because the wizard can leave venom lying
49 * around on a bones level for normal players to find. [Note to the
50 * confused: 'WIZARD' used to be a compile-time conditional so this was
51 * guarded by #ifdef WIZARD/.../#endif.]
52 */
53 static char venom_inv[] = { VENOM_CLASS, 0 }; /* (constant) */
54
55 /* sortloot() classification; called at most once [per sort] for each object */
56 STATIC_OVL void
loot_classify(sort_item,obj)57 loot_classify(sort_item, obj)
58 Loot *sort_item;
59 struct obj *obj;
60 {
61 /* we may eventually make this a settable option to always use
62 with sortloot instead of only when the 'sortpack' option isn't
63 set; it is similar to sortpack's inv_order but items most
64 likely to be picked up are moved to the front */
65 static char def_srt_order[MAXOCLASSES] = {
66 COIN_CLASS, AMULET_CLASS, RING_CLASS, WAND_CLASS, POTION_CLASS,
67 SCROLL_CLASS, SPBOOK_CLASS, GEM_CLASS, FOOD_CLASS, TOOL_CLASS,
68 WEAPON_CLASS, ARMOR_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0,
69 };
70 static char armcat[8];
71 const char *classorder;
72 char *p;
73 int k, otyp = obj->otyp, oclass = obj->oclass;
74 boolean seen, discovered = objects[otyp].oc_name_known ? TRUE : FALSE;
75
76 /*
77 * For the value types assigned by this classification, sortloot()
78 * will put lower valued ones before higher valued ones.
79 */
80 if (!Blind)
81 obj->dknown = 1; /* xname(obj) does this; we want it sooner */
82 seen = obj->dknown ? TRUE : FALSE,
83 /* class order */
84 classorder = flags.sortpack ? flags.inv_order : def_srt_order;
85 p = index(classorder, oclass);
86 if (p)
87 k = 1 + (int) (p - classorder);
88 else
89 k = 1 + (int) strlen(classorder) + (oclass != VENOM_CLASS);
90 sort_item->orderclass = (xchar) k;
91 /* subclass designation; only a few classes have subclasses
92 and the non-armor ones we use are fairly arbitrary */
93 switch (oclass) {
94 case ARMOR_CLASS:
95 if (!armcat[7]) {
96 /* one-time init; we use a different order than the subclass
97 values defined by objclass.h */
98 armcat[ARM_HELM] = 1; /* [2] */
99 armcat[ARM_GLOVES] = 2; /* [3] */
100 armcat[ARM_BOOTS] = 3; /* [4] */
101 armcat[ARM_SHIELD] = 4; /* [1] */
102 armcat[ARM_CLOAK] = 5; /* [5] */
103 armcat[ARM_SHIRT] = 6; /* [6] */
104 armcat[ARM_SUIT] = 7; /* [0] */
105 armcat[7] = 8; /* sanity protection */
106 }
107 k = objects[otyp].oc_armcat;
108 /* oc_armcat overloads oc_subtyp which is an 'schar' so guard
109 against somebody assigning something unexpected to it */
110 if (k < 0 || k >= 7)
111 k = 7;
112 k = armcat[k];
113 break;
114 case WEAPON_CLASS:
115 /* for weapons, group by ammo (arrows, bolts), launcher (bows),
116 missile (darts, boomerangs), stackable (daggers, knives, spears),
117 'other' (swords, axes, &c), polearms */
118 k = objects[otyp].oc_skill;
119 k = (k < 0) ? ((k >= -P_CROSSBOW && k <= -P_BOW) ? 1 : 3)
120 : ((k >= P_BOW && k <= P_CROSSBOW) ? 2
121 : (k == P_SPEAR || k == P_DAGGER || k == P_KNIFE) ? 4
122 : !is_pole(obj) ? 5 : 6);
123 break;
124 case TOOL_CLASS:
125 if (seen && discovered
126 && (otyp == BAG_OF_TRICKS || otyp == HORN_OF_PLENTY))
127 k = 2; /* known pseudo-container */
128 else if (Is_container(obj))
129 k = 1; /* regular container or unknown bag of tricks */
130 else
131 switch (otyp) {
132 case WOODEN_FLUTE:
133 case MAGIC_FLUTE:
134 case TOOLED_HORN:
135 case FROST_HORN:
136 case FIRE_HORN:
137 case WOODEN_HARP:
138 case MAGIC_HARP:
139 case BUGLE:
140 case LEATHER_DRUM:
141 case DRUM_OF_EARTHQUAKE:
142 case HORN_OF_PLENTY: /* not a musical instrument */
143 k = 3; /* instrument or unknown horn of plenty */
144 break;
145 default:
146 k = 4; /* 'other' tool */
147 break;
148 }
149 break;
150 case FOOD_CLASS:
151 /* [what about separating "partly eaten" within each group?] */
152 switch (otyp) {
153 case SLIME_MOLD:
154 k = 1;
155 break;
156 default:
157 /* [maybe separate one-bite foods from rations and such?] */
158 k = obj->globby ? 6 : 2;
159 break;
160 case TIN:
161 k = 3;
162 break;
163 case EGG:
164 k = 4;
165 break;
166 case CORPSE:
167 k = 5;
168 break;
169 }
170 break;
171 case GEM_CLASS:
172 /*
173 * Normally subclass takes priority over discovery status, but
174 * that would give away information for gems (assuming we'll
175 * group them as valuable gems, next glass, then gray stones,
176 * and finally rocks once they're all fully identified).
177 *
178 * Order:
179 * 1) unseen gems and glass ("gem")
180 * 2) seen but undiscovered gems and glass ("blue gem"),
181 * 3) discovered gems ("sapphire"),
182 * 4) discovered glass ("worthless pieced of blue glass"),
183 * 5) unseen gray stones and rocks ("stone"),
184 * 6) seen but undiscovered gray stones ("gray stone"),
185 * 7) discovered gray stones ("touchstone"),
186 * 8) seen rocks ("rock").
187 */
188 switch (objects[obj->otyp].oc_material) {
189 case GEMSTONE:
190 k = !seen ? 1 : !discovered ? 2 : 3;
191 break;
192 case GLASS:
193 k = !seen ? 1 : !discovered ? 2 : 4;
194 break;
195 default: /* MINERAL */
196 k = !seen ? 5 : (obj->otyp != ROCK) ? (!discovered ? 6 : 7) : 8;
197 break;
198 }
199 break;
200 default:
201 /* other classes don't have subclasses; we assign a nonzero
202 value because sortloot() uses 0 to mean 'not yet classified' */
203 k = 1; /* any non-zero would do */
204 break;
205 }
206 sort_item->subclass = (xchar) k;
207 /* discovery status */
208 k = !seen ? 1 /* unseen */
209 : (discovered || !OBJ_DESCR(objects[otyp])) ? 4
210 : (objects[otyp].oc_uname) ? 3 /* named (partially discovered) */
211 : 2; /* undiscovered */
212 sort_item->disco = (xchar) k;
213 }
214
215 /* sortloot() formatting routine; for alphabetizing, not shown to user */
216 STATIC_OVL char *
loot_xname(obj)217 loot_xname(obj)
218 struct obj *obj;
219 {
220 struct obj saveo;
221 boolean save_debug;
222 char *res, *save_oname;
223
224 /*
225 * Deal with things that xname() includes as a prefix. We don't
226 * want such because they change alphabetical ordering. First,
227 * remember 'obj's current settings.
228 */
229 saveo.odiluted = obj->odiluted;
230 saveo.blessed = obj->blessed, saveo.cursed = obj->cursed;
231 saveo.spe = obj->spe;
232 saveo.owt = obj->owt;
233 save_oname = has_oname(obj) ? ONAME(obj) : 0;
234 save_debug = flags.debug;
235 /* suppress "diluted" for potions and "holy/unholy" for water;
236 sortloot() will deal with them using other criteria than name */
237 if (obj->oclass == POTION_CLASS) {
238 obj->odiluted = 0;
239 if (obj->otyp == POT_WATER)
240 obj->blessed = 0, obj->cursed = 0;
241 }
242 /* make "wet towel" and "moist towel" format as "towel" so that all
243 three group together */
244 if (obj->otyp == TOWEL)
245 obj->spe = 0;
246 /* group "<size> glob of <foo>" by <foo> rather than by <size> */
247 if (obj->globby)
248 obj->owt = 200; /* 200: weight of combined glob from ten creatures
249 (five or fewer is "small", more than fifteen is
250 "large", in between has no prefix) */
251 /* suppress user-assigned name */
252 if (save_oname && !obj->oartifact)
253 ONAME(obj) = 0;
254 /* avoid wizard mode formatting variations */
255 if (wizard) { /* flags.debug */
256 /* paranoia: before toggling off wizard mode, guard against a
257 panic in xname() producing a normal mode panic save file */
258 program_state.something_worth_saving = 0;
259 flags.debug = FALSE;
260 }
261
262 res = cxname_singular(obj);
263
264 if (save_debug) {
265 flags.debug = TRUE;
266 program_state.something_worth_saving = 1;
267 }
268 /* restore the object */
269 if (obj->oclass == POTION_CLASS) {
270 obj->odiluted = saveo.odiluted;
271 if (obj->otyp == POT_WATER)
272 obj->blessed = saveo.blessed, obj->cursed = saveo.cursed;
273 }
274 if (obj->otyp == TOWEL) {
275 obj->spe = saveo.spe;
276 /* give "towel" a suffix that will force wet ones to come first,
277 moist ones next, and dry ones last regardless of whether
278 they've been flagged as having spe known */
279 Strcat(res, is_wet_towel(obj) ? ((obj->spe >= 3) ? "x" : "y") : "z");
280 }
281 if (obj->globby) {
282 obj->owt = saveo.owt;
283 /* we've suppressed the size prefix (above); there normally won't
284 be more than one of a given creature type because they coalesce,
285 but globs with different bless/curse state won't merge so it is
286 feasible to have multiple at the same location; add a suffix to
287 get such sorted by size (small first) */
288 Strcat(res, (obj->owt <= 100) ? "a"
289 : (obj->owt <= 300) ? "b"
290 : (obj->owt <= 500) ? "c"
291 : "d");
292 }
293 if (save_oname && !obj->oartifact)
294 ONAME(obj) = save_oname;
295
296 return res;
297 }
298
299 /* set by sortloot() for use by sortloot_cmp(); reset by sortloot when done */
300 static unsigned sortlootmode = 0;
301
302 /* qsort comparison routine for sortloot() */
303 STATIC_OVL int CFDECLSPEC
sortloot_cmp(vptr1,vptr2)304 sortloot_cmp(vptr1, vptr2)
305 const genericptr vptr1;
306 const genericptr vptr2;
307 {
308 struct sortloot_item *sli1 = (struct sortloot_item *) vptr1,
309 *sli2 = (struct sortloot_item *) vptr2;
310 struct obj *obj1 = sli1->obj,
311 *obj2 = sli2->obj;
312 char *nam1, *nam2;
313 int val1, val2, c, namcmp;
314
315 /* order by object class unless we're doing by-invlet without sortpack */
316 if ((sortlootmode & (SORTLOOT_PACK | SORTLOOT_INVLET))
317 != SORTLOOT_INVLET) {
318 /* Classify each object at most once no matter how many
319 comparisons it is involved in. */
320 if (!sli1->orderclass)
321 loot_classify(sli1, obj1);
322 if (!sli2->orderclass)
323 loot_classify(sli2, obj2);
324
325 /* Sort by class. */
326 val1 = sli1->orderclass;
327 val2 = sli2->orderclass;
328 if (val1 != val2)
329 return (int) (val1 - val2);
330
331 /* skip sub-classes when ordering by sortpack+invlet */
332 if ((sortlootmode & SORTLOOT_INVLET) == 0) {
333 /* Class matches; sort by subclass. */
334 val1 = sli1->subclass;
335 val2 = sli2->subclass;
336 if (val1 != val2)
337 return val1 - val2;
338
339 /* Class and subclass match; sort by discovery status:
340 * first unseen, then seen but not named or discovered,
341 * then named, lastly discovered.
342 * 1) potion
343 * 2) pink potion
344 * 3) dark green potion called confusion
345 * 4) potion of healing
346 * Multiple entries within each group will be put into
347 * alphabetical order below.
348 */
349 val1 = sli1->disco;
350 val2 = sli2->disco;
351 if (val1 != val2)
352 return val1 - val2;
353 }
354 }
355
356 /* order by assigned inventory letter */
357 if ((sortlootmode & SORTLOOT_INVLET) != 0) {
358 c = obj1->invlet;
359 val1 = ('a' <= c && c <= 'z') ? (c - 'a' + 2)
360 : ('A' <= c && c <= 'Z') ? (c - 'A' + 2 + 26)
361 : (c == '$') ? 1
362 : (c == '#') ? 1 + 52 + 1
363 : 1 + 52 + 1 + 1; /* none of the above */
364 c = obj2->invlet;
365 val2 = ('a' <= c && c <= 'z') ? (c - 'a' + 2)
366 : ('A' <= c && c <= 'Z') ? (c - 'A' + 2 + 26)
367 : (c == '$') ? 1
368 : (c == '#') ? 1 + 52 + 1
369 : 1 + 52 + 1 + 1; /* none of the above */
370 if (val1 != val2)
371 return val1 - val2;
372 }
373
374 if ((sortlootmode & SORTLOOT_LOOT) == 0)
375 goto tiebreak;
376
377 /*
378 * Sort object names in lexicographical order, ignoring quantity.
379 *
380 * Each obj gets formatted at most once (per sort) no matter how many
381 * comparisons it gets subjected to.
382 */
383 nam1 = sli1->str;
384 if (!nam1)
385 nam1 = sli1->str = dupstr(loot_xname(obj1));
386 nam2 = sli2->str;
387 if (!nam2)
388 nam2 = sli2->str = dupstr(loot_xname(obj2));
389 if ((namcmp = strcmpi(nam1, nam2)) != 0)
390 return namcmp;
391
392 /* Sort by BUCX. */
393 val1 = obj1->bknown ? (obj1->blessed ? 3 : !obj1->cursed ? 2 : 1) : 0;
394 val2 = obj2->bknown ? (obj2->blessed ? 3 : !obj2->cursed ? 2 : 1) : 0;
395 if (val1 != val2)
396 return val2 - val1; /* bigger is better */
397
398 /* Sort by greasing. This will put the objects in degreasing order. */
399 val1 = obj1->greased;
400 val2 = obj2->greased;
401 if (val1 != val2)
402 return val2 - val1; /* bigger is better */
403
404 /* Sort by erosion. The effective amount is what matters. */
405 val1 = greatest_erosion(obj1);
406 val2 = greatest_erosion(obj2);
407 if (val1 != val2)
408 return val1 - val2; /* bigger is WORSE */
409
410 /* Sort by erodeproofing. Map known-invulnerable to 1, and both
411 known-vulnerable and unknown-vulnerability to 0, because that's
412 how they're displayed. */
413 val1 = obj1->rknown && obj1->oerodeproof;
414 val2 = obj2->rknown && obj2->oerodeproof;
415 if (val1 != val2)
416 return val2 - val1; /* bigger is better */
417
418 /* Sort by enchantment. Map unknown to -1000, which is comfortably
419 below the range of obj->spe. oc_uses_known means that obj->known
420 matters, which usually indirectly means that obj->spe is relevant.
421 Lots of objects use obj->spe for some other purpose (see obj.h). */
422 if (objects[obj1->otyp].oc_uses_known
423 /* exclude eggs (laid by you) and tins (homemade, pureed, &c) */
424 && obj1->oclass != FOOD_CLASS) {
425 val1 = obj1->known ? obj1->spe : -1000;
426 val2 = obj2->known ? obj2->spe : -1000;
427 if (val1 != val2)
428 return val2 - val1; /* bigger is better */
429 }
430
431 tiebreak:
432 /* They're identical, as far as we're concerned. We want
433 to force a deterministic order, and do so by producing a
434 stable sort: maintain the original order of equal items. */
435 return (sli1->indx - sli2->indx);
436 }
437
438 /*
439 * sortloot() - the story so far...
440 *
441 * The original implementation constructed and returned an array
442 * of pointers to objects in the requested order. Callers had to
443 * count the number of objects, allocate the array, pass one
444 * object at a time to the routine which populates it, traverse
445 * the objects via stepping through the array, then free the
446 * array. The ordering process used a basic insertion sort which
447 * is fine for short lists but inefficient for long ones.
448 *
449 * 3.6.0 (and continuing with 3.6.1) changed all that so that
450 * sortloot was self-contained as far as callers were concerned.
451 * It reordered the linked list into the requested order and then
452 * normal list traversal was used to process it. It also switched
453 * to qsort() on the assumption that the C library implementation
454 * put some effort into sorting efficiently. It also checked
455 * whether the list was already sorted as it got ready to do the
456 * sorting, so re-examining inventory or a pile of objects without
457 * having changed anything would gobble up less CPU than a full
458 * sort. But it had at least two problems (aside from the ordinary
459 * complement of bugs):
460 * 1) some players wanted to get the original order back when they
461 * changed the 'sortloot' option back to 'none', but the list
462 * reordering made that infeasible;
463 * 2) object identification giving the 'ID whole pack' result
464 * would call makeknown() on each newly ID'd object, that would
465 * call update_inventory() to update the persistent inventory
466 * window if one existed, the interface would call the inventory
467 * display routine which would call sortloot() which might change
468 * the order of the list being traversed by the identify code,
469 * possibly skipping the ID of some objects. That could have been
470 * avoided by suppressing 'perm_invent' during identification
471 * (fragile) or by avoiding sortloot() during inventory display
472 * (more robust).
473 *
474 * As of 3.6.2: revert to the temporary array of ordered obj pointers
475 * but have sortloot() do the counting and allocation. Callers
476 * need to use array traversal instead of linked list traversal
477 * and need to free the temporary array when done. And the
478 * array contains 'struct sortloot_item' (aka 'Loot') entries
479 * instead of simple 'struct obj *' entries.
480 */
481 Loot *
sortloot(olist,mode,by_nexthere,filterfunc)482 sortloot(olist, mode, by_nexthere, filterfunc)
483 struct obj **olist; /* previous version might have changed *olist, we don't */
484 unsigned mode; /* flags for sortloot_cmp() */
485 boolean by_nexthere; /* T: traverse via obj->nexthere, F: via obj->nobj */
486 boolean FDECL((*filterfunc), (OBJ_P));
487 {
488 Loot *sliarray;
489 struct obj *o;
490 unsigned n, i;
491 boolean augment_filter;
492
493 for (n = 0, o = *olist; o; o = by_nexthere ? o->nexthere : o->nobj)
494 ++n;
495 /* note: if there is a filter function, this might overallocate */
496 sliarray = (Loot *) alloc((n + 1) * sizeof *sliarray);
497
498 /* the 'keep cockatrice corpses' flag is overloaded with sort mode */
499 augment_filter = (mode & SORTLOOT_PETRIFY) ? TRUE : FALSE;
500 mode &= ~SORTLOOT_PETRIFY; /* remove flag, leaving mode */
501 /* populate aliarray[0..n-1] */
502 for (i = 0, o = *olist; o; o = by_nexthere ? o->nexthere : o->nobj) {
503 if (filterfunc && !(*filterfunc)(o)
504 /* caller may be asking us to override filterfunc (in order
505 to do a cockatrice corpse touch check during pickup even
506 if/when the filter rejects food class) */
507 && (!augment_filter || o->otyp != CORPSE
508 || !touch_petrifies(&mons[o->corpsenm])))
509 continue;
510 sliarray[i].obj = o, sliarray[i].indx = (int) i;
511 sliarray[i].str = (char *) 0;
512 sliarray[i].orderclass = sliarray[i].subclass = sliarray[i].disco = 0;
513 ++i;
514 }
515 n = i;
516 /* add a terminator so that we don't have to pass 'n' back to caller */
517 sliarray[n].obj = (struct obj *) 0, sliarray[n].indx = -1;
518 sliarray[n].str = (char *) 0;
519 sliarray[n].orderclass = sliarray[n].subclass = sliarray[n].disco = 0;
520
521 /* do the sort; if no sorting is requested, we'll just return
522 a sortloot_item array reflecting the current ordering */
523 if (mode && n > 1) {
524 sortlootmode = mode; /* extra input for sortloot_cmp() */
525 qsort((genericptr_t) sliarray, n, sizeof *sliarray, sortloot_cmp);
526 sortlootmode = 0; /* reset static mode flags */
527 /* if sortloot_cmp formatted any objects, discard their strings now */
528 for (i = 0; i < n; ++i)
529 if (sliarray[i].str)
530 free((genericptr_t) sliarray[i].str), sliarray[i].str = 0;
531 }
532 return sliarray;
533 }
534
535 /* sortloot() callers should use this to free up memory it allocates */
536 void
unsortloot(loot_array_p)537 unsortloot(loot_array_p)
538 Loot **loot_array_p;
539 {
540 if (*loot_array_p)
541 free((genericptr_t) *loot_array_p), *loot_array_p = (Loot *) 0;
542 }
543
544 #if 0 /* 3.6.0 'revamp' */
545 void
546 sortloot(olist, mode, by_nexthere)
547 struct obj **olist;
548 unsigned mode; /* flags for sortloot_cmp() */
549 boolean by_nexthere; /* T: traverse via obj->nexthere, F: via obj->nobj */
550 {
551 struct sortloot_item *sliarray, osli, nsli;
552 struct obj *o, **nxt_p;
553 unsigned n, i;
554 boolean already_sorted = TRUE;
555
556 sortlootmode = mode; /* extra input for sortloot_cmp() */
557 for (n = osli.indx = 0, osli.obj = *olist; (o = osli.obj) != 0;
558 osli = nsli) {
559 nsli.obj = by_nexthere ? o->nexthere : o->nobj;
560 nsli.indx = (int) ++n;
561 if (nsli.obj && already_sorted
562 && sortloot_cmp((genericptr_t) &osli, (genericptr_t) &nsli) > 0)
563 already_sorted = FALSE;
564 }
565 if (n > 1 && !already_sorted) {
566 sliarray = (struct sortloot_item *) alloc(n * sizeof *sliarray);
567 for (i = 0, o = *olist; o;
568 ++i, o = by_nexthere ? o->nexthere : o->nobj)
569 sliarray[i].obj = o, sliarray[i].indx = (int) i;
570
571 qsort((genericptr_t) sliarray, n, sizeof *sliarray, sortloot_cmp);
572 for (i = 0; i < n; ++i) {
573 o = sliarray[i].obj;
574 nxt_p = by_nexthere ? &(o->nexthere) : &(o->nobj);
575 *nxt_p = (i < n - 1) ? sliarray[i + 1].obj : (struct obj *) 0;
576 }
577 *olist = sliarray[0].obj;
578 free((genericptr_t) sliarray);
579 }
580 sortlootmode = 0;
581 }
582 #endif /*0*/
583
584 void
assigninvlet(otmp)585 assigninvlet(otmp)
586 register struct obj *otmp;
587 {
588 boolean inuse[52];
589 register int i;
590 register struct obj *obj;
591
592 /* there should be at most one of these in inventory... */
593 if (otmp->oclass == COIN_CLASS) {
594 otmp->invlet = GOLD_SYM;
595 return;
596 }
597
598 for (i = 0; i < 52; i++)
599 inuse[i] = FALSE;
600 for (obj = invent; obj; obj = obj->nobj)
601 if (obj != otmp) {
602 i = obj->invlet;
603 if ('a' <= i && i <= 'z')
604 inuse[i - 'a'] = TRUE;
605 else if ('A' <= i && i <= 'Z')
606 inuse[i - 'A' + 26] = TRUE;
607 if (i == otmp->invlet)
608 otmp->invlet = 0;
609 }
610 if ((i = otmp->invlet)
611 && (('a' <= i && i <= 'z') || ('A' <= i && i <= 'Z')))
612 return;
613 for (i = lastinvnr + 1; i != lastinvnr; i++) {
614 if (i == 52) {
615 i = -1;
616 continue;
617 }
618 if (!inuse[i])
619 break;
620 }
621 otmp->invlet =
622 (inuse[i] ? NOINVSYM : (i < 26) ? ('a' + i) : ('A' + i - 26));
623 lastinvnr = i;
624 }
625
626 /* note: assumes ASCII; toggling a bit puts lowercase in front of uppercase */
627 #define inv_rank(o) ((o)->invlet ^ 040)
628
629 /* sort the inventory; used by addinv() and doorganize() */
630 STATIC_OVL void
reorder_invent()631 reorder_invent()
632 {
633 struct obj *otmp, *prev, *next;
634 boolean need_more_sorting;
635
636 do {
637 /*
638 * We expect at most one item to be out of order, so this
639 * isn't nearly as inefficient as it may first appear.
640 */
641 need_more_sorting = FALSE;
642 for (otmp = invent, prev = 0; otmp;) {
643 next = otmp->nobj;
644 if (next && inv_rank(next) < inv_rank(otmp)) {
645 need_more_sorting = TRUE;
646 if (prev)
647 prev->nobj = next;
648 else
649 invent = next;
650 otmp->nobj = next->nobj;
651 next->nobj = otmp;
652 prev = next;
653 } else {
654 prev = otmp;
655 otmp = next;
656 }
657 }
658 } while (need_more_sorting);
659 }
660
661 #undef inv_rank
662
663 /* scan a list of objects to see whether another object will merge with
664 one of them; used in pickup.c when all 52 inventory slots are in use,
665 to figure out whether another object could still be picked up */
666 struct obj *
merge_choice(objlist,obj)667 merge_choice(objlist, obj)
668 struct obj *objlist, *obj;
669 {
670 struct monst *shkp;
671 int save_nocharge;
672
673 if (obj->otyp == SCR_SCARE_MONSTER) /* punt on these */
674 return (struct obj *) 0;
675 /* if this is an item on the shop floor, the attributes it will
676 have when carried are different from what they are now; prevent
677 that from eliciting an incorrect result from mergable() */
678 save_nocharge = obj->no_charge;
679 if (objlist == invent && obj->where == OBJ_FLOOR
680 && (shkp = shop_keeper(inside_shop(obj->ox, obj->oy))) != 0) {
681 if (obj->no_charge)
682 obj->no_charge = 0;
683 /* A billable object won't have its `unpaid' bit set, so would
684 erroneously seem to be a candidate to merge with a similar
685 ordinary object. That's no good, because once it's really
686 picked up, it won't merge after all. It might merge with
687 another unpaid object, but we can't check that here (depends
688 too much upon shk's bill) and if it doesn't merge it would
689 end up in the '#' overflow inventory slot, so reject it now. */
690 else if (inhishop(shkp))
691 return (struct obj *) 0;
692 }
693 while (objlist) {
694 if (mergable(objlist, obj))
695 break;
696 objlist = objlist->nobj;
697 }
698 obj->no_charge = save_nocharge;
699 return objlist;
700 }
701
702 /* merge obj with otmp and delete obj if types agree */
703 int
merged(potmp,pobj)704 merged(potmp, pobj)
705 struct obj **potmp, **pobj;
706 {
707 register struct obj *otmp = *potmp, *obj = *pobj;
708
709 if (mergable(otmp, obj)) {
710 /* Approximate age: we do it this way because if we were to
711 * do it "accurately" (merge only when ages are identical)
712 * we'd wind up never merging any corpses.
713 * otmp->age = otmp->age*(1-proportion) + obj->age*proportion;
714 *
715 * Don't do the age manipulation if lit. We would need
716 * to stop the burn on both items, then merge the age,
717 * then restart the burn. Glob ages are averaged in the
718 * absorb routine, which uses weight rather than quantity
719 * to adjust for proportion (glob quantity is always 1).
720 */
721 if (!obj->lamplit && !obj->globby)
722 otmp->age = ((otmp->age * otmp->quan) + (obj->age * obj->quan))
723 / (otmp->quan + obj->quan);
724
725 if (!otmp->globby)
726 otmp->quan += obj->quan;
727 /* temporary special case for gold objects!!!! */
728 if (otmp->oclass == COIN_CLASS)
729 otmp->owt = weight(otmp), otmp->bknown = 0;
730 /* and puddings!!!1!!one! */
731 else if (!Is_pudding(otmp))
732 otmp->owt += obj->owt;
733 if (!has_oname(otmp) && has_oname(obj))
734 otmp = *potmp = oname(otmp, ONAME(obj));
735 obj_extract_self(obj);
736
737 /* really should merge the timeouts */
738 if (obj->lamplit)
739 obj_merge_light_sources(obj, otmp);
740 if (obj->timed)
741 obj_stop_timers(obj); /* follows lights */
742
743 /* fixup for `#adjust' merging wielded darts, daggers, &c */
744 if (obj->owornmask && carried(otmp)) {
745 long wmask = otmp->owornmask | obj->owornmask;
746
747 /* Both the items might be worn in competing slots;
748 merger preference (regardless of which is which):
749 primary weapon + alternate weapon -> primary weapon;
750 primary weapon + quiver -> primary weapon;
751 alternate weapon + quiver -> alternate weapon.
752 (Prior to 3.3.0, it was not possible for the two
753 stacks to be worn in different slots and `obj'
754 didn't need to be unworn when merging.) */
755 if (wmask & W_WEP)
756 wmask = W_WEP;
757 else if (wmask & W_SWAPWEP)
758 wmask = W_SWAPWEP;
759 else if (wmask & W_QUIVER)
760 wmask = W_QUIVER;
761 else {
762 impossible("merging strangely worn items (%lx)", wmask);
763 wmask = otmp->owornmask;
764 }
765 if ((otmp->owornmask & ~wmask) != 0L)
766 setnotworn(otmp);
767 setworn(otmp, wmask);
768 setnotworn(obj);
769 #if 0
770 /* (this should not be necessary, since items
771 already in a monster's inventory don't ever get
772 merged into other objects [only vice versa]) */
773 } else if (obj->owornmask && mcarried(otmp)) {
774 if (obj == MON_WEP(otmp->ocarry)) {
775 MON_WEP(otmp->ocarry) = otmp;
776 otmp->owornmask = W_WEP;
777 }
778 #endif /*0*/
779 }
780
781 /* handle puddings a bit differently; absorption will free the
782 other object automatically so we can just return out from here */
783 if (obj->globby) {
784 pudding_merge_message(otmp, obj);
785 obj_absorb(potmp, pobj);
786 return 1;
787 }
788
789 obfree(obj, otmp); /* free(obj), bill->otmp */
790 return 1;
791 }
792 return 0;
793 }
794
795 /*
796 * Adjust hero intrinsics as if this object was being added to the hero's
797 * inventory. Called _before_ the object has been added to the hero's
798 * inventory.
799 *
800 * This is called when adding objects to the hero's inventory normally (via
801 * addinv) or when an object in the hero's inventory has been polymorphed
802 * in-place.
803 *
804 * It may be valid to merge this code with with addinv_core2().
805 */
806 void
addinv_core1(obj)807 addinv_core1(obj)
808 struct obj *obj;
809 {
810 if (obj->oclass == COIN_CLASS) {
811 context.botl = 1;
812 } else if (obj->otyp == AMULET_OF_YENDOR) {
813 if (u.uhave.amulet)
814 impossible("already have amulet?");
815 u.uhave.amulet = 1;
816 u.uachieve.amulet = 1;
817 } else if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
818 if (u.uhave.menorah)
819 impossible("already have candelabrum?");
820 u.uhave.menorah = 1;
821 u.uachieve.menorah = 1;
822 } else if (obj->otyp == BELL_OF_OPENING) {
823 if (u.uhave.bell)
824 impossible("already have silver bell?");
825 u.uhave.bell = 1;
826 u.uachieve.bell = 1;
827 } else if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
828 if (u.uhave.book)
829 impossible("already have the book?");
830 u.uhave.book = 1;
831 u.uachieve.book = 1;
832 } else if (obj->oartifact) {
833 if (is_quest_artifact(obj)) {
834 if (u.uhave.questart)
835 impossible("already have quest artifact?");
836 u.uhave.questart = 1;
837 artitouch(obj);
838 }
839 set_artifact_intrinsic(obj, 1, W_ART);
840 }
841
842 /* "special achievements" aren't discoverable during play, they
843 end up being recorded in XLOGFILE at end of game, nowhere else;
844 record_achieve_special overloads corpsenm which is ordinarily
845 initialized to NON_PM (-1) rather than to 0; any special prize
846 must never be a corpse, egg, tin, figurine, or statue because
847 their use of obj->corpsenm for monster type would conflict,
848 nor be a leash (corpsenm overloaded for m_id of leashed
849 monster) or a novel (corpsenm overloaded for novel index) */
850 if (is_mines_prize(obj)) {
851 u.uachieve.mines_luckstone = 1;
852 obj->record_achieve_special = NON_PM;
853 obj->nomerge = 0;
854 } else if (is_soko_prize(obj)) {
855 u.uachieve.finish_sokoban = 1;
856 obj->record_achieve_special = NON_PM;
857 obj->nomerge = 0;
858 }
859 }
860
861 /*
862 * Adjust hero intrinsics as if this object was being added to the hero's
863 * inventory. Called _after_ the object has been added to the hero's
864 * inventory.
865 *
866 * This is called when adding objects to the hero's inventory normally (via
867 * addinv) or when an object in the hero's inventory has been polymorphed
868 * in-place.
869 */
870 void
addinv_core2(obj)871 addinv_core2(obj)
872 struct obj *obj;
873 {
874 if (confers_luck(obj)) {
875 /* new luckstone must be in inventory by this point
876 * for correct calculation */
877 set_moreluck();
878 }
879 }
880
881 /*
882 * Add obj to the hero's inventory. Make sure the object is "free".
883 * Adjust hero attributes as necessary.
884 */
885 struct obj *
addinv(obj)886 addinv(obj)
887 struct obj *obj;
888 {
889 struct obj *otmp, *prev;
890 int saved_otyp = (int) obj->otyp; /* for panic */
891 boolean obj_was_thrown;
892
893 if (obj->where != OBJ_FREE)
894 panic("addinv: obj not free");
895 /* normally addtobill() clears no_charge when items in a shop are
896 picked up, but won't do so if the shop has become untended */
897 obj->no_charge = 0; /* should not be set in hero's invent */
898 if (Has_contents(obj))
899 picked_container(obj); /* clear no_charge */
900 obj_was_thrown = obj->was_thrown;
901 obj->was_thrown = 0; /* not meaningful for invent */
902
903 addinv_core1(obj);
904
905 /* merge with quiver in preference to any other inventory slot
906 in case quiver and wielded weapon are both eligible; adding
907 extra to quivered stack is more useful than to wielded one */
908 if (uquiver && merged(&uquiver, &obj)) {
909 obj = uquiver;
910 if (!obj)
911 panic("addinv: null obj after quiver merge otyp=%d", saved_otyp);
912 goto added;
913 }
914 /* merge if possible; find end of chain in the process */
915 for (prev = 0, otmp = invent; otmp; prev = otmp, otmp = otmp->nobj)
916 if (merged(&otmp, &obj)) {
917 obj = otmp;
918 if (!obj)
919 panic("addinv: null obj after merge otyp=%d", saved_otyp);
920 goto added;
921 }
922 /* didn't merge, so insert into chain */
923 assigninvlet(obj);
924 if (flags.invlet_constant || !prev) {
925 obj->nobj = invent; /* insert at beginning */
926 invent = obj;
927 if (flags.invlet_constant)
928 reorder_invent();
929 } else {
930 prev->nobj = obj; /* insert at end */
931 obj->nobj = 0;
932 }
933 obj->where = OBJ_INVENT;
934
935 /* fill empty quiver if obj was thrown */
936 if (flags.pickup_thrown && !uquiver && obj_was_thrown
937 /* if Mjollnir is thrown and fails to return, we want to
938 auto-pick it when we move to its spot, but not into quiver;
939 aklyses behave like Mjollnir when thrown while wielded, but
940 we lack sufficient information here make them exceptions */
941 && obj->oartifact != ART_MJOLLNIR
942 && (throwing_weapon(obj) || is_ammo(obj)))
943 setuqwep(obj);
944 added:
945 addinv_core2(obj);
946 carry_obj_effects(obj); /* carrying affects the obj */
947 update_inventory();
948 return obj;
949 }
950
951 /*
952 * Some objects are affected by being carried.
953 * Make those adjustments here. Called _after_ the object
954 * has been added to the hero's or monster's inventory,
955 * and after hero's intrinsics have been updated.
956 */
957 void
carry_obj_effects(obj)958 carry_obj_effects(obj)
959 struct obj *obj;
960 {
961 /* Cursed figurines can spontaneously transform when carried. */
962 if (obj->otyp == FIGURINE) {
963 if (obj->cursed && obj->corpsenm != NON_PM
964 && !dead_species(obj->corpsenm, TRUE)) {
965 attach_fig_transform_timeout(obj);
966 }
967 }
968 }
969
970 /* Add an item to the inventory unless we're fumbling or it refuses to be
971 * held (via touch_artifact), and give a message.
972 * If there aren't any free inventory slots, we'll drop it instead.
973 * If both success and failure messages are NULL, then we're just doing the
974 * fumbling/slot-limit checking for a silent grab. In any case,
975 * touch_artifact will print its own messages if they are warranted.
976 */
977 struct obj *
hold_another_object(obj,drop_fmt,drop_arg,hold_msg)978 hold_another_object(obj, drop_fmt, drop_arg, hold_msg)
979 struct obj *obj;
980 const char *drop_fmt, *drop_arg, *hold_msg;
981 {
982 char buf[BUFSZ];
983
984 if (!Blind)
985 obj->dknown = 1; /* maximize mergibility */
986 if (obj->oartifact) {
987 /* place_object may change these */
988 boolean crysknife = (obj->otyp == CRYSKNIFE);
989 int oerode = obj->oerodeproof;
990 boolean wasUpolyd = Upolyd;
991
992 /* in case touching this object turns out to be fatal */
993 place_object(obj, u.ux, u.uy);
994
995 if (!touch_artifact(obj, &youmonst)) {
996 obj_extract_self(obj); /* remove it from the floor */
997 dropy(obj); /* now put it back again :-) */
998 return obj;
999 } else if (wasUpolyd && !Upolyd) {
1000 /* loose your grip if you revert your form */
1001 if (drop_fmt)
1002 pline(drop_fmt, drop_arg);
1003 obj_extract_self(obj);
1004 dropy(obj);
1005 return obj;
1006 }
1007 obj_extract_self(obj);
1008 if (crysknife) {
1009 obj->otyp = CRYSKNIFE;
1010 obj->oerodeproof = oerode;
1011 }
1012 }
1013 if (Fumbling) {
1014 obj->nomerge = 1;
1015 obj = addinv(obj); /* dropping expects obj to be in invent */
1016 goto drop_it;
1017 } else {
1018 long oquan = obj->quan;
1019 int prev_encumbr = near_capacity(); /* before addinv() */
1020
1021 /* encumbrance only matters if it would now become worse
1022 than max( current_value, stressed ) */
1023 if (prev_encumbr < MOD_ENCUMBER)
1024 prev_encumbr = MOD_ENCUMBER;
1025 /* addinv() may redraw the entire inventory, overwriting
1026 drop_arg when it comes from something like doname() */
1027 if (drop_arg)
1028 drop_arg = strcpy(buf, drop_arg);
1029
1030 obj = addinv(obj);
1031 if (inv_cnt(FALSE) > 52 || ((obj->otyp != LOADSTONE || !obj->cursed)
1032 && near_capacity() > prev_encumbr)) {
1033 /* undo any merge which took place */
1034 if (obj->quan > oquan)
1035 obj = splitobj(obj, oquan);
1036 goto drop_it;
1037 } else {
1038 if (flags.autoquiver && !uquiver && !obj->owornmask
1039 && (is_missile(obj) || ammo_and_launcher(obj, uwep)
1040 || ammo_and_launcher(obj, uswapwep)))
1041 setuqwep(obj);
1042 if (hold_msg || drop_fmt)
1043 prinv(hold_msg, obj, oquan);
1044 }
1045 }
1046 return obj;
1047
1048 drop_it:
1049 if (drop_fmt)
1050 pline(drop_fmt, drop_arg);
1051 obj->nomerge = 0;
1052 if (can_reach_floor(TRUE)) {
1053 dropx(obj);
1054 } else {
1055 freeinv(obj);
1056 hitfloor(obj, FALSE);
1057 }
1058 return (struct obj *) 0; /* might be gone */
1059 }
1060
1061 /* useup() all of an item regardless of its quantity */
1062 void
useupall(obj)1063 useupall(obj)
1064 struct obj *obj;
1065 {
1066 setnotworn(obj);
1067 freeinv(obj);
1068 obfree(obj, (struct obj *) 0); /* deletes contents also */
1069 }
1070
1071 void
useup(obj)1072 useup(obj)
1073 register struct obj *obj;
1074 {
1075 /* Note: This works correctly for containers because they (containers)
1076 don't merge. */
1077 if (obj->quan > 1L) {
1078 obj->in_use = FALSE; /* no longer in use */
1079 obj->quan--;
1080 obj->owt = weight(obj);
1081 update_inventory();
1082 } else {
1083 useupall(obj);
1084 }
1085 }
1086
1087 /* use one charge from an item and possibly incur shop debt for it */
1088 void
consume_obj_charge(obj,maybe_unpaid)1089 consume_obj_charge(obj, maybe_unpaid)
1090 struct obj *obj;
1091 boolean maybe_unpaid; /* false if caller handles shop billing */
1092 {
1093 if (maybe_unpaid)
1094 check_unpaid(obj);
1095 obj->spe -= 1;
1096 if (obj->known)
1097 update_inventory();
1098 }
1099
1100 /*
1101 * Adjust hero's attributes as if this object was being removed from the
1102 * hero's inventory. This should only be called from freeinv() and
1103 * where we are polymorphing an object already in the hero's inventory.
1104 *
1105 * Should think of a better name...
1106 */
1107 void
freeinv_core(obj)1108 freeinv_core(obj)
1109 struct obj *obj;
1110 {
1111 if (obj->oclass == COIN_CLASS) {
1112 context.botl = 1;
1113 return;
1114 } else if (obj->otyp == AMULET_OF_YENDOR) {
1115 if (!u.uhave.amulet)
1116 impossible("don't have amulet?");
1117 u.uhave.amulet = 0;
1118 } else if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
1119 if (!u.uhave.menorah)
1120 impossible("don't have candelabrum?");
1121 u.uhave.menorah = 0;
1122 } else if (obj->otyp == BELL_OF_OPENING) {
1123 if (!u.uhave.bell)
1124 impossible("don't have silver bell?");
1125 u.uhave.bell = 0;
1126 } else if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
1127 if (!u.uhave.book)
1128 impossible("don't have the book?");
1129 u.uhave.book = 0;
1130 } else if (obj->oartifact) {
1131 if (is_quest_artifact(obj)) {
1132 if (!u.uhave.questart)
1133 impossible("don't have quest artifact?");
1134 u.uhave.questart = 0;
1135 }
1136 set_artifact_intrinsic(obj, 0, W_ART);
1137 }
1138
1139 if (obj->otyp == LOADSTONE) {
1140 curse(obj);
1141 } else if (confers_luck(obj)) {
1142 set_moreluck();
1143 context.botl = 1;
1144 } else if (obj->otyp == FIGURINE && obj->timed) {
1145 (void) stop_timer(FIG_TRANSFORM, obj_to_any(obj));
1146 }
1147 }
1148
1149 /* remove an object from the hero's inventory */
1150 void
freeinv(obj)1151 freeinv(obj)
1152 register struct obj *obj;
1153 {
1154 extract_nobj(obj, &invent);
1155 freeinv_core(obj);
1156 update_inventory();
1157 }
1158
1159 void
delallobj(x,y)1160 delallobj(x, y)
1161 int x, y;
1162 {
1163 struct obj *otmp, *otmp2;
1164
1165 for (otmp = level.objects[x][y]; otmp; otmp = otmp2) {
1166 if (otmp == uball)
1167 unpunish();
1168 /* after unpunish(), or might get deallocated chain */
1169 otmp2 = otmp->nexthere;
1170 if (otmp == uchain)
1171 continue;
1172 delobj(otmp);
1173 }
1174 }
1175
1176 /* destroy object in fobj chain (if unpaid, it remains on the bill) */
1177 void
delobj(obj)1178 delobj(obj)
1179 register struct obj *obj;
1180 {
1181 boolean update_map;
1182
1183 if (obj->otyp == AMULET_OF_YENDOR
1184 || obj->otyp == CANDELABRUM_OF_INVOCATION
1185 || obj->otyp == BELL_OF_OPENING
1186 || obj->otyp == SPE_BOOK_OF_THE_DEAD) {
1187 /* player might be doing something stupid, but we
1188 * can't guarantee that. assume special artifacts
1189 * are indestructible via drawbridges, and exploding
1190 * chests, and golem creation, and ...
1191 */
1192 return;
1193 }
1194 update_map = (obj->where == OBJ_FLOOR);
1195 obj_extract_self(obj);
1196 if (update_map)
1197 newsym(obj->ox, obj->oy);
1198 obfree(obj, (struct obj *) 0); /* frees contents also */
1199 }
1200
1201 /* try to find a particular type of object at designated map location */
1202 struct obj *
sobj_at(otyp,x,y)1203 sobj_at(otyp, x, y)
1204 int otyp;
1205 int x, y;
1206 {
1207 register struct obj *otmp;
1208
1209 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
1210 if (otmp->otyp == otyp)
1211 break;
1212
1213 return otmp;
1214 }
1215
1216 /* sobj_at(&c) traversal -- find next object of specified type */
1217 struct obj *
nxtobj(obj,type,by_nexthere)1218 nxtobj(obj, type, by_nexthere)
1219 struct obj *obj;
1220 int type;
1221 boolean by_nexthere;
1222 {
1223 register struct obj *otmp;
1224
1225 otmp = obj; /* start with the object after this one */
1226 do {
1227 otmp = !by_nexthere ? otmp->nobj : otmp->nexthere;
1228 if (!otmp)
1229 break;
1230 } while (otmp->otyp != type);
1231
1232 return otmp;
1233 }
1234
1235 struct obj *
carrying(type)1236 carrying(type)
1237 register int type;
1238 {
1239 register struct obj *otmp;
1240
1241 for (otmp = invent; otmp; otmp = otmp->nobj)
1242 if (otmp->otyp == type)
1243 return otmp;
1244 return (struct obj *) 0;
1245 }
1246
1247 /* Fictional and not-so-fictional currencies.
1248 * http://concord.wikia.com/wiki/List_of_Fictional_Currencies
1249 */
1250 static const char *const currencies[] = {
1251 "Altarian Dollar", /* The Hitchhiker's Guide to the Galaxy */
1252 "Ankh-Morpork Dollar", /* Discworld */
1253 "auric", /* The Domination of Draka */
1254 "buckazoid", /* Space Quest */
1255 "cirbozoid", /* Starslip */
1256 "credit chit", /* Deus Ex */
1257 "cubit", /* Battlestar Galactica */
1258 "Flanian Pobble Bead", /* The Hitchhiker's Guide to the Galaxy */
1259 "fretzer", /* Jules Verne */
1260 "imperial credit", /* Star Wars */
1261 "Hong Kong Luna Dollar", /* The Moon is a Harsh Mistress */
1262 "kongbuck", /* Snow Crash */
1263 "nanite", /* System Shock 2 */
1264 "quatloo", /* Star Trek, Sim City */
1265 "simoleon", /* Sim City */
1266 "solari", /* Spaceballs */
1267 "spacebuck", /* Spaceballs */
1268 "sporebuck", /* Spore */
1269 "Triganic Pu", /* The Hitchhiker's Guide to the Galaxy */
1270 "woolong", /* Cowboy Bebop */
1271 "zorkmid", /* Zork, NetHack */
1272 };
1273
1274 const char *
currency(amount)1275 currency(amount)
1276 long amount;
1277 {
1278 const char *res;
1279
1280 res = Hallucination ? currencies[rn2(SIZE(currencies))] : "zorkmid";
1281 if (amount != 1L)
1282 res = makeplural(res);
1283 return res;
1284 }
1285
1286 boolean
have_lizard()1287 have_lizard()
1288 {
1289 register struct obj *otmp;
1290
1291 for (otmp = invent; otmp; otmp = otmp->nobj)
1292 if (otmp->otyp == CORPSE && otmp->corpsenm == PM_LIZARD)
1293 return TRUE;
1294 return FALSE;
1295 }
1296
1297 /* 3.6 tribute */
1298 struct obj *
u_have_novel()1299 u_have_novel()
1300 {
1301 register struct obj *otmp;
1302
1303 for (otmp = invent; otmp; otmp = otmp->nobj)
1304 if (otmp->otyp == SPE_NOVEL)
1305 return otmp;
1306 return (struct obj *) 0;
1307 }
1308
1309 struct obj *
o_on(id,objchn)1310 o_on(id, objchn)
1311 unsigned int id;
1312 register struct obj *objchn;
1313 {
1314 struct obj *temp;
1315
1316 while (objchn) {
1317 if (objchn->o_id == id)
1318 return objchn;
1319 if (Has_contents(objchn) && (temp = o_on(id, objchn->cobj)))
1320 return temp;
1321 objchn = objchn->nobj;
1322 }
1323 return (struct obj *) 0;
1324 }
1325
1326 boolean
obj_here(obj,x,y)1327 obj_here(obj, x, y)
1328 register struct obj *obj;
1329 int x, y;
1330 {
1331 register struct obj *otmp;
1332
1333 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
1334 if (obj == otmp)
1335 return TRUE;
1336 return FALSE;
1337 }
1338
1339 struct obj *
g_at(x,y)1340 g_at(x, y)
1341 register int x, y;
1342 {
1343 register struct obj *obj = level.objects[x][y];
1344
1345 while (obj) {
1346 if (obj->oclass == COIN_CLASS)
1347 return obj;
1348 obj = obj->nexthere;
1349 }
1350 return (struct obj *) 0;
1351 }
1352
1353 /* compact a string of inventory letters by dashing runs of letters */
1354 STATIC_OVL void
compactify(buf)1355 compactify(buf)
1356 register char *buf;
1357 {
1358 register int i1 = 1, i2 = 1;
1359 register char ilet, ilet1, ilet2;
1360
1361 ilet2 = buf[0];
1362 ilet1 = buf[1];
1363 buf[++i2] = buf[++i1];
1364 ilet = buf[i1];
1365 while (ilet) {
1366 if (ilet == ilet1 + 1) {
1367 if (ilet1 == ilet2 + 1)
1368 buf[i2 - 1] = ilet1 = '-';
1369 else if (ilet2 == '-') {
1370 buf[i2 - 1] = ++ilet1;
1371 buf[i2] = buf[++i1];
1372 ilet = buf[i1];
1373 continue;
1374 }
1375 } else if (ilet == NOINVSYM) {
1376 /* compact three or more consecutive '#'
1377 characters into "#-#" */
1378 if (i2 >= 2 && buf[i2 - 2] == NOINVSYM && buf[i2 - 1] == NOINVSYM)
1379 buf[i2 - 1] = '-';
1380 else if (i2 >= 3 && buf[i2 - 3] == NOINVSYM && buf[i2 - 2] == '-'
1381 && buf[i2 - 1] == NOINVSYM)
1382 --i2;
1383 }
1384 ilet2 = ilet1;
1385 ilet1 = ilet;
1386 buf[++i2] = buf[++i1];
1387 ilet = buf[i1];
1388 }
1389 }
1390
1391 /* some objects shouldn't be split when count given to getobj or askchain */
1392 boolean
splittable(obj)1393 splittable(obj)
1394 struct obj *obj;
1395 {
1396 return !((obj->otyp == LOADSTONE && obj->cursed)
1397 || (obj == uwep && welded(uwep)));
1398 }
1399
1400 /* match the prompt for either 'T' or 'R' command */
1401 STATIC_OVL boolean
taking_off(action)1402 taking_off(action)
1403 const char *action;
1404 {
1405 return !strcmp(action, "take off") || !strcmp(action, "remove");
1406 }
1407
1408 /* match the prompt for either 'W' or 'P' command */
1409 STATIC_OVL boolean
putting_on(action)1410 putting_on(action)
1411 const char *action;
1412 {
1413 return !strcmp(action, "wear") || !strcmp(action, "put on");
1414 }
1415
1416 /*
1417 * getobj returns:
1418 * struct obj *xxx: object to do something with.
1419 * (struct obj *) 0 error return: no object.
1420 * &zeroobj explicitly no object (as in w-).
1421 !!!! test if gold can be used in unusual ways (eaten etc.)
1422 !!!! may be able to remove "usegold"
1423 */
1424 struct obj *
getobj(let,word)1425 getobj(let, word)
1426 register const char *let, *word;
1427 {
1428 register struct obj *otmp;
1429 register char ilet = 0;
1430 char buf[BUFSZ], qbuf[QBUFSZ];
1431 char lets[BUFSZ], altlets[BUFSZ], *ap;
1432 register int foo = 0;
1433 register char *bp = buf;
1434 xchar allowcnt = 0; /* 0, 1 or 2 */
1435 boolean usegold = FALSE; /* can't use gold because its illegal */
1436 boolean allowall = FALSE;
1437 boolean allownone = FALSE;
1438 boolean useboulder = FALSE;
1439 xchar foox = 0;
1440 long cnt;
1441 boolean cntgiven = FALSE;
1442 boolean msggiven = FALSE;
1443 boolean oneloop = FALSE;
1444 long dummymask;
1445 Loot *sortedinvent, *srtinv;
1446
1447 if (*let == ALLOW_COUNT)
1448 let++, allowcnt = 1;
1449 if (*let == COIN_CLASS)
1450 let++, usegold = TRUE;
1451
1452 /* Equivalent of an "ugly check" for gold */
1453 if (usegold && !strcmp(word, "eat")
1454 && (!metallivorous(youmonst.data)
1455 || youmonst.data == &mons[PM_RUST_MONSTER]))
1456 usegold = FALSE;
1457
1458 if (*let == ALL_CLASSES)
1459 let++, allowall = TRUE;
1460 if (*let == ALLOW_NONE)
1461 let++, allownone = TRUE;
1462 /* "ugly check" for reading fortune cookies, part 1.
1463 * The normal 'ugly check' keeps the object on the inventory list.
1464 * We don't want to do that for shirts/cookies, so the check for
1465 * them is handled a bit differently (and also requires that we set
1466 * allowall in the caller).
1467 */
1468 if (allowall && !strcmp(word, "read"))
1469 allowall = FALSE;
1470
1471 /* another ugly check: show boulders (not statues) */
1472 if (*let == WEAPON_CLASS && !strcmp(word, "throw")
1473 && throws_rocks(youmonst.data))
1474 useboulder = TRUE;
1475
1476 if (allownone)
1477 *bp++ = HANDS_SYM, *bp++ = ' '; /* '-' */
1478 ap = altlets;
1479
1480 if (!flags.invlet_constant)
1481 reassign();
1482
1483 /* force invent to be in invlet order before collecting candidate
1484 inventory letters */
1485 sortedinvent = sortloot(&invent, SORTLOOT_INVLET, FALSE,
1486 (boolean FDECL((*), (OBJ_P))) 0);
1487
1488 for (srtinv = sortedinvent; (otmp = srtinv->obj) != 0; ++srtinv) {
1489 if (&bp[foo] == &buf[sizeof buf - 1]
1490 || ap == &altlets[sizeof altlets - 1]) {
1491 /* we must have a huge number of NOINVSYM items somehow */
1492 impossible("getobj: inventory overflow");
1493 break;
1494 }
1495
1496 if (!*let || index(let, otmp->oclass)
1497 || (usegold && otmp->invlet == GOLD_SYM)
1498 || (useboulder && otmp->otyp == BOULDER)) {
1499 register int otyp = otmp->otyp;
1500
1501 bp[foo++] = otmp->invlet;
1502 /* clang-format off */
1503 /* *INDENT-OFF* */
1504 /* ugly check: remove inappropriate things */
1505 if (
1506 (taking_off(word) /* exclude if not worn */
1507 && !(otmp->owornmask & (W_ARMOR | W_ACCESSORY)))
1508 || (putting_on(word) /* exclude if already worn */
1509 && (otmp->owornmask & (W_ARMOR | W_ACCESSORY)))
1510 #if 0 /* 3.4.1 -- include currently wielded weapon among 'wield' choices */
1511 || (!strcmp(word, "wield")
1512 && (otmp->owornmask & W_WEP))
1513 #endif
1514 || (!strcmp(word, "ready") /* exclude when wielded... */
1515 && ((otmp == uwep || (otmp == uswapwep && u.twoweap))
1516 && otmp->quan == 1L)) /* ...unless more than one */
1517 || ((!strcmp(word, "dip") || !strcmp(word, "grease"))
1518 && inaccessible_equipment(otmp, (const char *) 0, FALSE))
1519 ) {
1520 foo--;
1521 foox++;
1522 }
1523 /* Second ugly check; unlike the first it won't trigger an
1524 * "else" in "you don't have anything else to ___".
1525 */
1526 else if (
1527 (putting_on(word)
1528 && ((otmp->oclass == FOOD_CLASS && otmp->otyp != MEAT_RING)
1529 || (otmp->oclass == TOOL_CLASS && otyp != BLINDFOLD
1530 && otyp != TOWEL && otyp != LENSES)))
1531 || (!strcmp(word, "wield")
1532 && (otmp->oclass == TOOL_CLASS && !is_weptool(otmp)))
1533 || (!strcmp(word, "eat") && !is_edible(otmp))
1534 || (!strcmp(word, "sacrifice")
1535 && (otyp != CORPSE && otyp != AMULET_OF_YENDOR
1536 && otyp != FAKE_AMULET_OF_YENDOR))
1537 || (!strcmp(word, "write with")
1538 && (otmp->oclass == TOOL_CLASS
1539 && otyp != MAGIC_MARKER && otyp != TOWEL))
1540 || (!strcmp(word, "tin")
1541 && (otyp != CORPSE || !tinnable(otmp)))
1542 || (!strcmp(word, "rub")
1543 && ((otmp->oclass == TOOL_CLASS && otyp != OIL_LAMP
1544 && otyp != MAGIC_LAMP && otyp != BRASS_LANTERN)
1545 || (otmp->oclass == GEM_CLASS && !is_graystone(otmp))))
1546 || (!strcmp(word, "use or apply")
1547 /* Picks, axes, pole-weapons, bullwhips */
1548 && ((otmp->oclass == WEAPON_CLASS
1549 && !is_pick(otmp) && !is_axe(otmp)
1550 && !is_pole(otmp) && otyp != BULLWHIP)
1551 || (otmp->oclass == POTION_CLASS
1552 /* only applicable potion is oil, and it will only
1553 be offered as a choice when already discovered */
1554 && (otyp != POT_OIL || !otmp->dknown
1555 || !objects[POT_OIL].oc_name_known))
1556 || (otmp->oclass == FOOD_CLASS
1557 && otyp != CREAM_PIE && otyp != EUCALYPTUS_LEAF)
1558 || (otmp->oclass == GEM_CLASS && !is_graystone(otmp))))
1559 || (!strcmp(word, "invoke")
1560 && !otmp->oartifact
1561 && !objects[otyp].oc_unique
1562 && (otyp != FAKE_AMULET_OF_YENDOR || otmp->known)
1563 && otyp != CRYSTAL_BALL /* synonym for apply */
1564 /* note: presenting the possibility of invoking non-artifact
1565 mirrors and/or lamps is simply a cruel deception... */
1566 && otyp != MIRROR
1567 && otyp != MAGIC_LAMP
1568 && (otyp != OIL_LAMP /* don't list known oil lamp */
1569 || (otmp->dknown && objects[OIL_LAMP].oc_name_known)))
1570 || (!strcmp(word, "untrap with")
1571 && ((otmp->oclass == TOOL_CLASS && otyp != CAN_OF_GREASE)
1572 || (otmp->oclass == POTION_CLASS
1573 /* only applicable potion is oil, and it will only
1574 be offered as a choice when already discovered */
1575 && (otyp != POT_OIL || !otmp->dknown
1576 || !objects[POT_OIL].oc_name_known))))
1577 || (!strcmp(word, "tip") && !Is_container(otmp)
1578 /* include horn of plenty if sufficiently discovered */
1579 && (otmp->otyp != HORN_OF_PLENTY || !otmp->dknown
1580 || !objects[HORN_OF_PLENTY].oc_name_known))
1581 || (!strcmp(word, "charge") && !is_chargeable(otmp))
1582 || (!strcmp(word, "open") && otyp != TIN)
1583 || (!strcmp(word, "call") && !objtyp_is_callable(otyp))
1584 || (!strcmp(word, "adjust") && otmp->oclass == COIN_CLASS
1585 && !usegold)
1586 ) {
1587 foo--;
1588 }
1589 /* Third ugly check: acceptable but not listed as likely
1590 * candidates in the prompt or in the inventory subset if
1591 * player responds with '?'.
1592 */
1593 else if (
1594 /* ugly check for unworn armor that can't be worn */
1595 (putting_on(word) && *let == ARMOR_CLASS
1596 && !canwearobj(otmp, &dummymask, FALSE))
1597 /* or armor with 'P' or 'R' or accessory with 'W' or 'T' */
1598 || ((putting_on(word) || taking_off(word))
1599 && ((*let == ARMOR_CLASS) ^ (otmp->oclass == ARMOR_CLASS)))
1600 /* or unsuitable items rubbed on known touchstone */
1601 || (!strncmp(word, "rub on the stone", 16)
1602 && *let == GEM_CLASS && otmp->dknown
1603 && objects[otyp].oc_name_known)
1604 /* suppress corpses on astral, amulets elsewhere */
1605 || (!strcmp(word, "sacrifice")
1606 /* (!astral && amulet) || (astral && !amulet) */
1607 && (!Is_astralevel(&u.uz) ^ (otmp->oclass != AMULET_CLASS)))
1608 /* suppress container being stashed into */
1609 || (!strcmp(word, "stash") && !ck_bag(otmp))
1610 /* worn armor (shirt, suit) covered by worn armor (suit, cloak)
1611 or accessory (ring) covered by cursed worn armor (gloves) */
1612 || (taking_off(word)
1613 && inaccessible_equipment(otmp, (const char *) 0,
1614 (boolean) (otmp->oclass == RING_CLASS)))
1615 || (!strcmp(word, "write on")
1616 && (!(otyp == SCR_BLANK_PAPER || otyp == SPE_BLANK_PAPER)
1617 || !otmp->dknown || !objects[otyp].oc_name_known))
1618 ) {
1619 /* acceptable but not listed as likely candidate */
1620 foo--;
1621 allowall = TRUE;
1622 *ap++ = otmp->invlet;
1623 }
1624 /* *INDENT-ON* */
1625 /* clang-format on */
1626 } else {
1627 /* "ugly check" for reading fortune cookies, part 2 */
1628 if ((!strcmp(word, "read") && is_readable(otmp)))
1629 allowall = usegold = TRUE;
1630 }
1631 }
1632 unsortloot(&sortedinvent);
1633
1634 bp[foo] = 0;
1635 if (foo == 0 && bp > buf && bp[-1] == ' ')
1636 *--bp = 0;
1637 Strcpy(lets, bp); /* necessary since we destroy buf */
1638 if (foo > 5) /* compactify string */
1639 compactify(bp);
1640 *ap = '\0';
1641
1642 if (!foo && !allowall && !allownone) {
1643 You("don't have anything %sto %s.", foox ? "else " : "", word);
1644 return (struct obj *) 0;
1645 } else if (!strcmp(word, "write on")) { /* ugly check for magic marker */
1646 /* we wanted all scrolls and books in altlets[], but that came with
1647 'allowall' which we don't want since it prevents "silly thing"
1648 result if anything other than scroll or spellbook is chosen */
1649 allowall = FALSE;
1650 }
1651 for (;;) {
1652 cnt = 0;
1653 cntgiven = FALSE;
1654 Sprintf(qbuf, "What do you want to %s?", word);
1655 if (in_doagain)
1656 ilet = readchar();
1657 else if (iflags.force_invmenu) {
1658 /* don't overwrite a possible quitchars */
1659 if (!oneloop)
1660 ilet = *let ? '?' : '*';
1661 if (!msggiven)
1662 putmsghistory(qbuf, FALSE);
1663 msggiven = TRUE;
1664 oneloop = TRUE;
1665 } else {
1666 if (!buf[0])
1667 Strcat(qbuf, " [*]");
1668 else
1669 Sprintf(eos(qbuf), " [%s or ?*]", buf);
1670 ilet = yn_function(qbuf, (char *) 0, '\0');
1671 }
1672 if (digit(ilet)) {
1673 long tmpcnt = 0;
1674
1675 if (!allowcnt) {
1676 pline("No count allowed with this command.");
1677 continue;
1678 }
1679 ilet = get_count(NULL, ilet, LARGEST_INT, &tmpcnt, TRUE);
1680 if (tmpcnt) {
1681 cnt = tmpcnt;
1682 cntgiven = TRUE;
1683 }
1684 }
1685 if (index(quitchars, ilet)) {
1686 if (flags.verbose)
1687 pline1(Never_mind);
1688 return (struct obj *) 0;
1689 }
1690 if (ilet == HANDS_SYM) { /* '-' */
1691 if (!allownone) {
1692 char *suf = (char *) 0;
1693
1694 strcpy(buf, word);
1695 if ((bp = strstr(buf, " on the ")) != 0) {
1696 /* rub on the stone[s] */
1697 *bp = '\0';
1698 suf = (bp + 1);
1699 }
1700 if ((bp = strstr(buf, " or ")) != 0) {
1701 *bp = '\0';
1702 bp = (rn2(2) ? buf : (bp + 4));
1703 } else
1704 bp = buf;
1705 You("mime %s something%s%s.", ing_suffix(bp), suf ? " " : "",
1706 suf ? suf : "");
1707 }
1708 return (allownone ? (struct obj *) &zeroobj : (struct obj *) 0);
1709 }
1710 redo_menu:
1711 /* since gold is now kept in inventory, we need to do processing for
1712 select-from-invent before checking whether gold has been picked */
1713 if (ilet == '?' || ilet == '*') {
1714 char *allowed_choices = (ilet == '?') ? lets : (char *) 0;
1715 long ctmp = 0;
1716 char menuquery[QBUFSZ];
1717
1718 menuquery[0] = qbuf[0] = '\0';
1719 if (iflags.force_invmenu)
1720 Sprintf(menuquery, "What do you want to %s?", word);
1721 if (!strcmp(word, "grease"))
1722 Sprintf(qbuf, "your %s", fingers_or_gloves(FALSE));
1723 else if (!strcmp(word, "write with"))
1724 Sprintf(qbuf, "your %s", body_part(FINGERTIP));
1725 else if (!strcmp(word, "wield"))
1726 Sprintf(qbuf, "your %s %s%s", uarmg ? "gloved" : "bare",
1727 makeplural(body_part(HAND)),
1728 !uwep ? " (wielded)" : "");
1729 else if (!strcmp(word, "ready"))
1730 Sprintf(qbuf, "empty quiver%s",
1731 !uquiver ? " (nothing readied)" : "");
1732
1733 if (ilet == '?' && !*lets && *altlets)
1734 allowed_choices = altlets;
1735 ilet = display_pickinv(allowed_choices, *qbuf ? qbuf : (char *) 0,
1736 menuquery,
1737 TRUE, allowcnt ? &ctmp : (long *) 0);
1738 if (!ilet)
1739 continue;
1740 if (ilet == HANDS_SYM)
1741 return (struct obj *) &zeroobj; /* cast away 'const' */
1742 if (ilet == '\033') {
1743 if (flags.verbose)
1744 pline1(Never_mind);
1745 return (struct obj *) 0;
1746 }
1747 if (ilet == '*')
1748 goto redo_menu;
1749 if (allowcnt && ctmp >= 0) {
1750 cnt = ctmp;
1751 cntgiven = TRUE;
1752 }
1753 /* they typed a letter (not a space) at the prompt */
1754 }
1755 /* find the item which was picked */
1756 for (otmp = invent; otmp; otmp = otmp->nobj)
1757 if (otmp->invlet == ilet)
1758 break;
1759 /* some items have restrictions */
1760 if (ilet == def_oc_syms[COIN_CLASS].sym
1761 /* guard against the [hypothetical] chace of having more
1762 than one invent slot of gold and picking the non-'$' one */
1763 || (otmp && otmp->oclass == COIN_CLASS)) {
1764 if (!usegold) {
1765 You("cannot %s gold.", word);
1766 return (struct obj *) 0;
1767 }
1768 /* Historic note: early Nethack had a bug which was
1769 * first reported for Larn, where trying to drop 2^32-n
1770 * gold pieces was allowed, and did interesting things
1771 * to your money supply. The LRS is the tax bureau
1772 * from Larn.
1773 */
1774 if (cntgiven && cnt <= 0) {
1775 if (cnt < 0)
1776 pline_The(
1777 "LRS would be very interested to know you have that much.");
1778 return (struct obj *) 0;
1779 }
1780 }
1781 if (cntgiven && !strcmp(word, "throw")) {
1782 /* permit counts for throwing gold, but don't accept
1783 * counts for other things since the throw code will
1784 * split off a single item anyway */
1785 if (cnt == 0)
1786 return (struct obj *) 0;
1787 if (cnt > 1 && (ilet != def_oc_syms[COIN_CLASS].sym
1788 && !(otmp && otmp->oclass == COIN_CLASS))) {
1789 You("can only throw one item at a time.");
1790 continue;
1791 }
1792 }
1793 context.botl = 1; /* May have changed the amount of money */
1794 savech(ilet);
1795 /* [we used to set otmp (by finding ilet in invent) here, but
1796 that's been moved above so that otmp can be checked earlier] */
1797 /* verify the chosen object */
1798 if (!otmp) {
1799 You("don't have that object.");
1800 if (in_doagain)
1801 return (struct obj *) 0;
1802 continue;
1803 } else if (cnt < 0 || otmp->quan < cnt) {
1804 You("don't have that many! You have only %ld.", otmp->quan);
1805 if (in_doagain)
1806 return (struct obj *) 0;
1807 continue;
1808 }
1809 break;
1810 }
1811 if (!allowall && let && !index(let, otmp->oclass)
1812 && !(usegold && otmp->oclass == COIN_CLASS)) {
1813 silly_thing(word, otmp);
1814 return (struct obj *) 0;
1815 }
1816 if (cntgiven) {
1817 if (cnt == 0)
1818 return (struct obj *) 0;
1819 if (cnt != otmp->quan) {
1820 /* don't split a stack of cursed loadstones */
1821 if (splittable(otmp))
1822 otmp = splitobj(otmp, cnt);
1823 else if (otmp->otyp == LOADSTONE && otmp->cursed)
1824 /* kludge for canletgo()'s can't-drop-this message */
1825 otmp->corpsenm = (int) cnt;
1826 }
1827 }
1828 return otmp;
1829 }
1830
1831 void
silly_thing(word,otmp)1832 silly_thing(word, otmp)
1833 const char *word;
1834 struct obj *otmp;
1835 {
1836 #if 1 /* 'P','R' vs 'W','T' handling is obsolete */
1837 nhUse(otmp);
1838 #else
1839 const char *s1, *s2, *s3;
1840 int ocls = otmp->oclass, otyp = otmp->otyp;
1841
1842 s1 = s2 = s3 = 0;
1843 /* check for attempted use of accessory commands ('P','R') on armor
1844 and for corresponding armor commands ('W','T') on accessories */
1845 if (ocls == ARMOR_CLASS) {
1846 if (!strcmp(word, "put on"))
1847 s1 = "W", s2 = "wear", s3 = "";
1848 else if (!strcmp(word, "remove"))
1849 s1 = "T", s2 = "take", s3 = " off";
1850 } else if ((ocls == RING_CLASS || otyp == MEAT_RING)
1851 || ocls == AMULET_CLASS
1852 || (otyp == BLINDFOLD || otyp == TOWEL || otyp == LENSES)) {
1853 if (!strcmp(word, "wear"))
1854 s1 = "P", s2 = "put", s3 = " on";
1855 else if (!strcmp(word, "take off"))
1856 s1 = "R", s2 = "remove", s3 = "";
1857 }
1858 if (s1)
1859 pline("Use the '%s' command to %s %s%s.", s1, s2,
1860 !(is_plural(otmp) || pair_of(otmp)) ? "that" : "those", s3);
1861 else
1862 #endif
1863 pline(silly_thing_to, word);
1864 }
1865
1866 STATIC_PTR int
ckvalidcat(otmp)1867 ckvalidcat(otmp)
1868 struct obj *otmp;
1869 {
1870 /* use allow_category() from pickup.c */
1871 return (int) allow_category(otmp);
1872 }
1873
1874 STATIC_PTR int
ckunpaid(otmp)1875 ckunpaid(otmp)
1876 struct obj *otmp;
1877 {
1878 return (otmp->unpaid || (Has_contents(otmp) && count_unpaid(otmp->cobj)));
1879 }
1880
1881 boolean
wearing_armor()1882 wearing_armor()
1883 {
1884 return (boolean) (uarm || uarmc || uarmf || uarmg
1885 || uarmh || uarms || uarmu);
1886 }
1887
1888 boolean
is_worn(otmp)1889 is_worn(otmp)
1890 struct obj *otmp;
1891 {
1892 return (otmp->owornmask & (W_ARMOR | W_ACCESSORY | W_SADDLE | W_WEAPONS))
1893 ? TRUE
1894 : FALSE;
1895 }
1896
1897 /* extra xprname() input that askchain() can't pass through safe_qbuf() */
1898 STATIC_VAR struct xprnctx {
1899 char let;
1900 boolean dot;
1901 } safeq_xprn_ctx;
1902
1903 /* safe_qbuf() -> short_oname() callback */
1904 STATIC_PTR char *
safeq_xprname(obj)1905 safeq_xprname(obj)
1906 struct obj *obj;
1907 {
1908 return xprname(obj, (char *) 0, safeq_xprn_ctx.let, safeq_xprn_ctx.dot,
1909 0L, 0L);
1910 }
1911
1912 /* alternate safe_qbuf() -> short_oname() callback */
1913 STATIC_PTR char *
safeq_shortxprname(obj)1914 safeq_shortxprname(obj)
1915 struct obj *obj;
1916 {
1917 return xprname(obj, ansimpleoname(obj), safeq_xprn_ctx.let,
1918 safeq_xprn_ctx.dot, 0L, 0L);
1919 }
1920
1921 static NEARDATA const char removeables[] = { ARMOR_CLASS, WEAPON_CLASS,
1922 RING_CLASS, AMULET_CLASS,
1923 TOOL_CLASS, 0 };
1924
1925 /* Interactive version of getobj - used for Drop, Identify, and Takeoff (A).
1926 Return the number of times fn was called successfully.
1927 If combo is TRUE, we just use this to get a category list. */
1928 int
ggetobj(word,fn,mx,combo,resultflags)1929 ggetobj(word, fn, mx, combo, resultflags)
1930 const char *word;
1931 int FDECL((*fn), (OBJ_P)), mx;
1932 boolean combo; /* combination menu flag */
1933 unsigned *resultflags;
1934 {
1935 int FDECL((*ckfn), (OBJ_P)) = (int FDECL((*), (OBJ_P))) 0;
1936 boolean FDECL((*ofilter), (OBJ_P)) = (boolean FDECL((*), (OBJ_P))) 0;
1937 boolean takeoff, ident, allflag, m_seen;
1938 int itemcount;
1939 int oletct, iletct, unpaid, oc_of_sym;
1940 char sym, *ip, olets[MAXOCLASSES + 5], ilets[MAXOCLASSES + 10];
1941 char extra_removeables[3 + 1]; /* uwep,uswapwep,uquiver */
1942 char buf[BUFSZ] = DUMMY, qbuf[QBUFSZ];
1943
1944 if (!invent) {
1945 You("have nothing to %s.", word);
1946 if (resultflags)
1947 *resultflags = ALL_FINISHED;
1948 return 0;
1949 }
1950 if (resultflags)
1951 *resultflags = 0;
1952 takeoff = ident = allflag = m_seen = FALSE;
1953 add_valid_menu_class(0); /* reset */
1954 if (taking_off(word)) {
1955 takeoff = TRUE;
1956 ofilter = is_worn;
1957 } else if (!strcmp(word, "identify")) {
1958 ident = TRUE;
1959 ofilter = not_fully_identified;
1960 }
1961
1962 iletct = collect_obj_classes(ilets, invent, FALSE, ofilter, &itemcount);
1963 unpaid = count_unpaid(invent);
1964
1965 if (ident && !iletct) {
1966 return -1; /* no further identifications */
1967 } else if (invent) {
1968 ilets[iletct++] = ' ';
1969 if (unpaid)
1970 ilets[iletct++] = 'u';
1971 if (count_buc(invent, BUC_BLESSED, ofilter))
1972 ilets[iletct++] = 'B';
1973 if (count_buc(invent, BUC_UNCURSED, ofilter))
1974 ilets[iletct++] = 'U';
1975 if (count_buc(invent, BUC_CURSED, ofilter))
1976 ilets[iletct++] = 'C';
1977 if (count_buc(invent, BUC_UNKNOWN, ofilter))
1978 ilets[iletct++] = 'X';
1979 ilets[iletct++] = 'a';
1980 }
1981 ilets[iletct++] = 'i';
1982 if (!combo)
1983 ilets[iletct++] = 'm'; /* allow menu presentation on request */
1984 ilets[iletct] = '\0';
1985
1986 for (;;) {
1987 Sprintf(qbuf, "What kinds of thing do you want to %s? [%s]",
1988 word, ilets);
1989 getlin(qbuf, buf);
1990 if (buf[0] == '\033')
1991 return 0;
1992 if (index(buf, 'i')) {
1993 char ailets[1+26+26+1+5+1]; /* $ + a-z + A-Z + # + slop + \0 */
1994 struct obj *otmp;
1995
1996 /* applicable inventory letters; if empty, show entire invent */
1997 ailets[0] = '\0';
1998 if (ofilter)
1999 for (otmp = invent; otmp; otmp = otmp->nobj)
2000 /* index() check: limit overflow items to one '#' */
2001 if ((*ofilter)(otmp) && !index(ailets, otmp->invlet))
2002 (void) strkitten(ailets, otmp->invlet);
2003 if (display_inventory(ailets, TRUE) == '\033')
2004 return 0;
2005 } else
2006 break;
2007 }
2008
2009 extra_removeables[0] = '\0';
2010 if (takeoff) {
2011 /* arbitrary types of items can be placed in the weapon slots
2012 [any duplicate entries in extra_removeables[] won't matter] */
2013 if (uwep)
2014 (void) strkitten(extra_removeables, uwep->oclass);
2015 if (uswapwep)
2016 (void) strkitten(extra_removeables, uswapwep->oclass);
2017 if (uquiver)
2018 (void) strkitten(extra_removeables, uquiver->oclass);
2019 }
2020
2021 ip = buf;
2022 olets[oletct = 0] = '\0';
2023 while ((sym = *ip++) != '\0') {
2024 if (sym == ' ')
2025 continue;
2026 oc_of_sym = def_char_to_objclass(sym);
2027 if (takeoff && oc_of_sym != MAXOCLASSES) {
2028 if (index(extra_removeables, oc_of_sym)) {
2029 ; /* skip rest of takeoff checks */
2030 } else if (!index(removeables, oc_of_sym)) {
2031 pline("Not applicable.");
2032 return 0;
2033 } else if (oc_of_sym == ARMOR_CLASS && !wearing_armor()) {
2034 noarmor(FALSE);
2035 return 0;
2036 } else if (oc_of_sym == WEAPON_CLASS && !uwep && !uswapwep
2037 && !uquiver) {
2038 You("are not wielding anything.");
2039 return 0;
2040 } else if (oc_of_sym == RING_CLASS && !uright && !uleft) {
2041 You("are not wearing rings.");
2042 return 0;
2043 } else if (oc_of_sym == AMULET_CLASS && !uamul) {
2044 You("are not wearing an amulet.");
2045 return 0;
2046 } else if (oc_of_sym == TOOL_CLASS && !ublindf) {
2047 You("are not wearing a blindfold.");
2048 return 0;
2049 }
2050 }
2051
2052 if (oc_of_sym == COIN_CLASS && !combo) {
2053 context.botl = 1;
2054 } else if (sym == 'a') {
2055 allflag = TRUE;
2056 } else if (sym == 'A') {
2057 ; /* same as the default */
2058 } else if (sym == 'u') {
2059 add_valid_menu_class('u');
2060 ckfn = ckunpaid;
2061 } else if (index("BUCX", sym)) {
2062 add_valid_menu_class(sym); /* 'B','U','C',or 'X' */
2063 ckfn = ckvalidcat;
2064 } else if (sym == 'm') {
2065 m_seen = TRUE;
2066 } else if (oc_of_sym == MAXOCLASSES) {
2067 You("don't have any %c's.", sym);
2068 } else if (oc_of_sym != VENOM_CLASS) { /* suppress venom */
2069 if (!index(olets, oc_of_sym)) {
2070 add_valid_menu_class(oc_of_sym);
2071 olets[oletct++] = oc_of_sym;
2072 olets[oletct] = 0;
2073 }
2074 }
2075 }
2076
2077 if (m_seen) {
2078 return (allflag
2079 || (!oletct && ckfn != ckunpaid && ckfn != ckvalidcat))
2080 ? -2 : -3;
2081 } else if (flags.menu_style != MENU_TRADITIONAL && combo && !allflag) {
2082 return 0;
2083 #if 0
2084 /* !!!! test gold dropping */
2085 } else if (allowgold == 2 && !oletct) {
2086 return 1; /* you dropped gold (or at least tried to) */
2087 #endif
2088 } else {
2089 int cnt = askchain(&invent, olets, allflag, fn, ckfn, mx, word);
2090 /*
2091 * askchain() has already finished the job in this case
2092 * so set a special flag to convey that back to the caller
2093 * so that it won't continue processing.
2094 * Fix for bug C331-1 reported by Irina Rempt-Drijfhout.
2095 */
2096 if (combo && allflag && resultflags)
2097 *resultflags |= ALL_FINISHED;
2098 return cnt;
2099 }
2100 }
2101
2102 /*
2103 * Walk through the chain starting at objchn and ask for all objects
2104 * with olet in olets (if nonNULL) and satisfying ckfn (if nonnull)
2105 * whether the action in question (i.e., fn) has to be performed.
2106 * If allflag then no questions are asked. Mx gives the max number
2107 * of objects to be treated. Return the number of objects treated.
2108 */
2109 int
askchain(objchn,olets,allflag,fn,ckfn,mx,word)2110 askchain(objchn, olets, allflag, fn, ckfn, mx, word)
2111 struct obj **objchn; /* *objchn might change */
2112 int allflag, mx;
2113 const char *olets, *word; /* olets is an Obj Class char array */
2114 int FDECL((*fn), (OBJ_P)), FDECL((*ckfn), (OBJ_P));
2115 {
2116 struct obj *otmp, *otmpo;
2117 register char sym, ilet;
2118 int cnt = 0, dud = 0, tmp;
2119 boolean takeoff, nodot, ident, take_out, put_in, first, ininv, bycat;
2120 char qbuf[QBUFSZ], qpfx[QBUFSZ];
2121 Loot *sortedchn = 0;
2122
2123 takeoff = taking_off(word);
2124 ident = !strcmp(word, "identify");
2125 take_out = !strcmp(word, "take out");
2126 put_in = !strcmp(word, "put in");
2127 nodot = (!strcmp(word, "nodot") || !strcmp(word, "drop") || ident
2128 || takeoff || take_out || put_in);
2129 ininv = (*objchn == invent);
2130 bycat = (menu_class_present('u')
2131 || menu_class_present('B') || menu_class_present('U')
2132 || menu_class_present('C') || menu_class_present('X'));
2133
2134 /* someday maybe we'll sort by 'olets' too (temporarily replace
2135 flags.packorder and pass SORTLOOT_PACK), but not yet... */
2136 sortedchn = sortloot(objchn, SORTLOOT_INVLET, FALSE,
2137 (boolean FDECL((*), (OBJ_P))) 0);
2138
2139 first = TRUE;
2140 /*
2141 * Interrogate in the object class order specified.
2142 * For example, if a person specifies =/ then first all rings
2143 * will be asked about followed by all wands. -dgk
2144 */
2145 nextclass:
2146 ilet = 'a' - 1;
2147 if (*objchn && (*objchn)->oclass == COIN_CLASS)
2148 ilet--; /* extra iteration */
2149 /*
2150 * Multiple Drop can change the invent chain while it operates
2151 * (dropping a burning potion of oil while levitating creates
2152 * an explosion which can destroy inventory items), so simple
2153 * list traversal
2154 * for (otmp = *objchn; otmp; otmp = otmp2) {
2155 * otmp2 = otmp->nobj;
2156 * ...
2157 * }
2158 * is inadequate here. Use each object's bypass bit to keep
2159 * track of which list elements have already been processed.
2160 */
2161 bypass_objlist(*objchn, FALSE); /* clear chain's bypass bits */
2162 while ((otmp = nxt_unbypassed_loot(sortedchn, *objchn)) != 0) {
2163 if (ilet == 'z')
2164 ilet = 'A';
2165 else if (ilet == 'Z')
2166 ilet = NOINVSYM; /* '#' */
2167 else
2168 ilet++;
2169 if (olets && *olets && otmp->oclass != *olets)
2170 continue;
2171 if (takeoff && !is_worn(otmp))
2172 continue;
2173 if (ident && !not_fully_identified(otmp))
2174 continue;
2175 if (ckfn && !(*ckfn)(otmp))
2176 continue;
2177 if (bycat && !ckvalidcat(otmp))
2178 continue;
2179 if (!allflag) {
2180 safeq_xprn_ctx.let = ilet;
2181 safeq_xprn_ctx.dot = !nodot;
2182 *qpfx = '\0';
2183 if (first) {
2184 /* traditional_loot() skips prompting when only one
2185 class of objects is involved, so prefix the first
2186 object being queried here with an explanation why */
2187 if (take_out || put_in)
2188 Sprintf(qpfx, "%s: ", word), *qpfx = highc(*qpfx);
2189 first = FALSE;
2190 }
2191 (void) safe_qbuf(qbuf, qpfx, "?", otmp,
2192 ininv ? safeq_xprname : doname,
2193 ininv ? safeq_shortxprname : ansimpleoname,
2194 "item");
2195 sym = (takeoff || ident || otmp->quan < 2L) ? nyaq(qbuf)
2196 : nyNaq(qbuf);
2197 } else
2198 sym = 'y';
2199
2200 otmpo = otmp;
2201 if (sym == '#') {
2202 /* Number was entered; split the object unless it corresponds
2203 to 'none' or 'all'. 2 special cases: cursed loadstones and
2204 welded weapons (eg, multiple daggers) will remain as merged
2205 unit; done to avoid splitting an object that won't be
2206 droppable (even if we're picking up rather than dropping). */
2207 if (!yn_number) {
2208 sym = 'n';
2209 } else {
2210 sym = 'y';
2211 if (yn_number < otmp->quan && splittable(otmp))
2212 otmp = splitobj(otmp, yn_number);
2213 }
2214 }
2215 switch (sym) {
2216 case 'a':
2217 allflag = 1;
2218 /*FALLTHRU*/
2219 case 'y':
2220 tmp = (*fn)(otmp);
2221 if (tmp < 0) {
2222 if (container_gone(fn)) {
2223 /* otmp caused magic bag to explode;
2224 both are now gone */
2225 otmp = 0; /* and return */
2226 } else if (otmp && otmp != otmpo) {
2227 /* split occurred, merge again */
2228 (void) merged(&otmpo, &otmp);
2229 }
2230 goto ret;
2231 }
2232 cnt += tmp;
2233 if (--mx == 0)
2234 goto ret;
2235 /*FALLTHRU*/
2236 case 'n':
2237 if (nodot)
2238 dud++;
2239 default:
2240 break;
2241 case 'q':
2242 /* special case for seffects() */
2243 if (ident)
2244 cnt = -1;
2245 goto ret;
2246 }
2247 }
2248 if (olets && *olets && *++olets)
2249 goto nextclass;
2250
2251 if (!takeoff && (dud || cnt))
2252 pline("That was all.");
2253 else if (!dud && !cnt)
2254 pline("No applicable objects.");
2255 ret:
2256 unsortloot(&sortedchn);
2257 bypass_objlist(*objchn, FALSE);
2258 return cnt;
2259 }
2260
2261 /*
2262 * Object identification routines:
2263 */
2264
2265 /* make an object actually be identified; no display updating */
2266 void
fully_identify_obj(otmp)2267 fully_identify_obj(otmp)
2268 struct obj *otmp;
2269 {
2270 makeknown(otmp->otyp);
2271 if (otmp->oartifact)
2272 discover_artifact((xchar) otmp->oartifact);
2273 otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1;
2274 if (Is_container(otmp) || otmp->otyp == STATUE)
2275 otmp->cknown = otmp->lknown = 1;
2276 if (otmp->otyp == EGG && otmp->corpsenm != NON_PM)
2277 learn_egg_type(otmp->corpsenm);
2278 }
2279
2280 /* ggetobj callback routine; identify an object and give immediate feedback */
2281 int
identify(otmp)2282 identify(otmp)
2283 struct obj *otmp;
2284 {
2285 fully_identify_obj(otmp);
2286 prinv((char *) 0, otmp, 0L);
2287 return 1;
2288 }
2289
2290 /* menu of unidentified objects; select and identify up to id_limit of them */
2291 STATIC_OVL void
menu_identify(id_limit)2292 menu_identify(id_limit)
2293 int id_limit;
2294 {
2295 menu_item *pick_list;
2296 int n, i, first = 1, tryct = 5;
2297 char buf[BUFSZ];
2298 /* assumptions: id_limit > 0 and at least one unID'd item is present */
2299
2300 while (id_limit) {
2301 Sprintf(buf, "What would you like to identify %s?",
2302 first ? "first" : "next");
2303 n = query_objlist(buf, &invent, (SIGNAL_NOMENU | SIGNAL_ESCAPE
2304 | USE_INVLET | INVORDER_SORT),
2305 &pick_list, PICK_ANY, not_fully_identified);
2306
2307 if (n > 0) {
2308 if (n > id_limit)
2309 n = id_limit;
2310 for (i = 0; i < n; i++, id_limit--)
2311 (void) identify(pick_list[i].item.a_obj);
2312 free((genericptr_t) pick_list);
2313 mark_synch(); /* Before we loop to pop open another menu */
2314 first = 0;
2315 } else if (n == -2) { /* player used ESC to quit menu */
2316 break;
2317 } else if (n == -1) { /* no eligible items found */
2318 pline("That was all.");
2319 break;
2320 } else if (!--tryct) { /* stop re-prompting */
2321 pline1(thats_enough_tries);
2322 break;
2323 } else { /* try again */
2324 pline("Choose an item; use ESC to decline.");
2325 }
2326 }
2327 }
2328 /* count the unidentified items */
2329 int
count_unidentified(objchn)2330 count_unidentified(objchn)
2331 struct obj *objchn;
2332 {
2333 int unid_cnt = 0;
2334 struct obj *obj;
2335
2336 for (obj = objchn; obj; obj = obj->nobj)
2337 if (not_fully_identified(obj))
2338 ++unid_cnt;
2339 return unid_cnt;
2340 }
2341
2342 /* dialog with user to identify a given number of items; 0 means all */
2343 void
identify_pack(id_limit,learning_id)2344 identify_pack(id_limit, learning_id)
2345 int id_limit;
2346 boolean learning_id; /* true if we just read unknown identify scroll */
2347 {
2348 struct obj *obj;
2349 int n, unid_cnt = count_unidentified(invent);
2350
2351 if (!unid_cnt) {
2352 You("have already identified all %sof your possessions.",
2353 learning_id ? "the rest " : "");
2354 } else if (!id_limit || id_limit >= unid_cnt) {
2355 /* identify everything */
2356 /* TODO: use fully_identify_obj and cornline/menu/whatever here */
2357 for (obj = invent; obj; obj = obj->nobj) {
2358 if (not_fully_identified(obj)) {
2359 (void) identify(obj);
2360 if (unid_cnt == 1)
2361 break;
2362 }
2363 }
2364 } else {
2365 /* identify up to `id_limit' items */
2366 n = 0;
2367 if (flags.menu_style == MENU_TRADITIONAL)
2368 do {
2369 n = ggetobj("identify", identify, id_limit, FALSE,
2370 (unsigned *) 0);
2371 if (n < 0)
2372 break; /* quit or no eligible items */
2373 } while ((id_limit -= n) > 0);
2374 if (n == 0 || n < -1)
2375 menu_identify(id_limit);
2376 }
2377 update_inventory();
2378 }
2379
2380 /* called when regaining sight; mark inventory objects which were picked
2381 up while blind as now having been seen */
2382 void
learn_unseen_invent()2383 learn_unseen_invent()
2384 {
2385 struct obj *otmp;
2386
2387 if (Blind)
2388 return; /* sanity check */
2389
2390 for (otmp = invent; otmp; otmp = otmp->nobj) {
2391 if (otmp->dknown)
2392 continue; /* already seen */
2393 /* set dknown, perhaps bknown (for priest[ess]) */
2394 (void) xname(otmp);
2395 /*
2396 * If object->eknown gets implemented (see learnwand(zap.c)),
2397 * handle deferred discovery here.
2398 */
2399 }
2400 update_inventory();
2401 }
2402
2403 /* persistent inventory window is maintained by interface code;
2404 'update_inventory' used to be a macro for
2405 (*windowprocs.win_update_inventory) but the restore hackery
2406 was getting out of hand; this is now a central call point */
2407 void
update_inventory()2408 update_inventory()
2409 {
2410 if (restoring)
2411 return;
2412
2413 /*
2414 * Ought to check (windowprocs.wincap2 & WC2_PERM_INVENT) here....
2415 *
2416 * We currently don't skip this call when iflags.perm_invent is False
2417 * because curses uses that to disable a previous perm_invent window
2418 * (after toggle via 'O'; perhaps the options code should handle that).
2419 */
2420 (*windowprocs.win_update_inventory)();
2421 }
2422
2423 /* should of course only be called for things in invent */
2424 STATIC_OVL char
obj_to_let(obj)2425 obj_to_let(obj)
2426 struct obj *obj;
2427 {
2428 if (!flags.invlet_constant) {
2429 obj->invlet = NOINVSYM;
2430 reassign();
2431 }
2432 return obj->invlet;
2433 }
2434
2435 /*
2436 * Print the indicated quantity of the given object. If quan == 0L then use
2437 * the current quantity.
2438 */
2439 void
prinv(prefix,obj,quan)2440 prinv(prefix, obj, quan)
2441 const char *prefix;
2442 struct obj *obj;
2443 long quan;
2444 {
2445 if (!prefix)
2446 prefix = "";
2447 pline("%s%s%s", prefix, *prefix ? " " : "",
2448 xprname(obj, (char *) 0, obj_to_let(obj), TRUE, 0L, quan));
2449 }
2450
2451 char *
xprname(obj,txt,let,dot,cost,quan)2452 xprname(obj, txt, let, dot, cost, quan)
2453 struct obj *obj;
2454 const char *txt; /* text to print instead of obj */
2455 char let; /* inventory letter */
2456 boolean dot; /* append period; (dot && cost => Iu) */
2457 long cost; /* cost (for inventory of unpaid or expended items) */
2458 long quan; /* if non-0, print this quantity, not obj->quan */
2459 {
2460 #ifdef LINT /* handle static char li[BUFSZ]; */
2461 char li[BUFSZ];
2462 #else
2463 static char li[BUFSZ];
2464 #endif
2465 boolean use_invlet = (flags.invlet_constant
2466 && let != CONTAINED_SYM && let != HANDS_SYM);
2467 long savequan = 0;
2468
2469 if (quan && obj) {
2470 savequan = obj->quan;
2471 obj->quan = quan;
2472 }
2473 /*
2474 * If let is:
2475 * - Then obj == null and 'txt' refers to hands or fingers.
2476 * * Then obj == null and we are printing a total amount.
2477 * > Then the object is contained and doesn't have an inventory letter.
2478 */
2479 if (cost != 0 || let == '*') {
2480 /* if dot is true, we're doing Iu, otherwise Ix */
2481 Sprintf(li,
2482 iflags.menu_tab_sep ? "%c - %s\t%6ld %s"
2483 : "%c - %-45s %6ld %s",
2484 (dot && use_invlet ? obj->invlet : let),
2485 (txt ? txt : doname(obj)), cost, currency(cost));
2486 } else {
2487 /* ordinary inventory display or pickup message */
2488 Sprintf(li, "%c - %s%s", (use_invlet ? obj->invlet : let),
2489 (txt ? txt : doname(obj)), (dot ? "." : ""));
2490 }
2491 if (savequan)
2492 obj->quan = savequan;
2493
2494 return li;
2495 }
2496
2497 /* the 'i' command */
2498 int
ddoinv()2499 ddoinv()
2500 {
2501 (void) display_inventory((char *) 0, FALSE);
2502 return 0;
2503 }
2504
2505 /*
2506 * find_unpaid()
2507 *
2508 * Scan the given list of objects. If last_found is NULL, return the first
2509 * unpaid object found. If last_found is not NULL, then skip over unpaid
2510 * objects until last_found is reached, then set last_found to NULL so the
2511 * next unpaid object is returned. This routine recursively follows
2512 * containers.
2513 */
2514 STATIC_OVL struct obj *
find_unpaid(list,last_found)2515 find_unpaid(list, last_found)
2516 struct obj *list, **last_found;
2517 {
2518 struct obj *obj;
2519
2520 while (list) {
2521 if (list->unpaid) {
2522 if (*last_found) {
2523 /* still looking for previous unpaid object */
2524 if (list == *last_found)
2525 *last_found = (struct obj *) 0;
2526 } else
2527 return ((*last_found = list));
2528 }
2529 if (Has_contents(list)) {
2530 if ((obj = find_unpaid(list->cobj, last_found)) != 0)
2531 return obj;
2532 }
2533 list = list->nobj;
2534 }
2535 return (struct obj *) 0;
2536 }
2537
2538 /* for perm_invent when operating on a partial inventory display, so that
2539 the persistent one doesn't get shrunk during filtering for item selection
2540 then regrown to full inventory, possibly being resized in the process */
2541 static winid cached_pickinv_win = WIN_ERR;
2542
2543 void
free_pickinv_cache()2544 free_pickinv_cache()
2545 {
2546 if (cached_pickinv_win != WIN_ERR) {
2547 destroy_nhwindow(cached_pickinv_win);
2548 cached_pickinv_win = WIN_ERR;
2549 }
2550 }
2551
2552 /*
2553 * Internal function used by display_inventory and getobj that can display
2554 * inventory and return a count as well as a letter. If out_cnt is not null,
2555 * any count returned from the menu selection is placed here.
2556 */
2557 STATIC_OVL char
display_pickinv(lets,xtra_choice,query,want_reply,out_cnt)2558 display_pickinv(lets, xtra_choice, query, want_reply, out_cnt)
2559 register const char *lets;
2560 const char *xtra_choice; /* "fingers", pick hands rather than an object */
2561 const char *query;
2562 boolean want_reply;
2563 long *out_cnt;
2564 {
2565 static const char not_carrying_anything[] = "Not carrying anything";
2566 struct obj *otmp, wizid_fakeobj;
2567 char ilet, ret;
2568 char *invlet = flags.inv_order;
2569 int n, classcount;
2570 winid win; /* windows being used */
2571 anything any;
2572 menu_item *selected;
2573 unsigned sortflags;
2574 Loot *sortedinvent, *srtinv;
2575 boolean wizid = (wizard && iflags.override_ID), gotsomething = FALSE;
2576
2577 if (lets && !*lets)
2578 lets = 0; /* simplify tests: (lets) instead of (lets && *lets) */
2579
2580 if (iflags.perm_invent && (lets || xtra_choice || wizid)) {
2581 /* partial inventory in perm_invent setting; don't operate on
2582 full inventory window, use an alternate one instead; create
2583 the first time needed and keep it for re-use as needed later */
2584 if (cached_pickinv_win == WIN_ERR)
2585 cached_pickinv_win = create_nhwindow(NHW_MENU);
2586 win = cached_pickinv_win;
2587 } else
2588 win = WIN_INVEN;
2589
2590 /*
2591 * Exit early if no inventory -- but keep going if we are doing
2592 * a permanent inventory update. We need to keep going so the
2593 * permanent inventory window updates itself to remove the last
2594 * item(s) dropped. One down side: the addition of the exception
2595 * for permanent inventory window updates _can_ pop the window
2596 * up when it's not displayed -- even if it's empty -- because we
2597 * don't know at this level if its up or not. This may not be
2598 * an issue if empty checks are done before hand and the call
2599 * to here is short circuited away.
2600 *
2601 * 2: our count here is only to distinguish between 0 and 1 and
2602 * more than 1; for the last one, we don't need a precise number.
2603 * For perm_invent update we force 'more than 1'.
2604 */
2605 n = (iflags.perm_invent && !lets && !want_reply) ? 2
2606 : lets ? (int) strlen(lets)
2607 : !invent ? 0 : !invent->nobj ? 1 : 2;
2608 /* for xtra_choice, there's another 'item' not included in initial 'n';
2609 for !lets (full invent) and for override_ID (wizard mode identify),
2610 skip message_menu handling of single item even if item count was 1 */
2611 if (xtra_choice || (n == 1 && (!lets || iflags.override_ID)))
2612 ++n;
2613
2614 if (n == 0) {
2615 pline("%s.", not_carrying_anything);
2616 return 0;
2617 }
2618
2619 /* oxymoron? temporarily assign permanent inventory letters */
2620 if (!flags.invlet_constant)
2621 reassign();
2622
2623 if (n == 1 && !iflags.force_invmenu) {
2624 /* when only one item of interest, use pline instead of menus;
2625 we actually use a fake message-line menu in order to allow
2626 the user to perform selection at the --More-- prompt for tty */
2627 ret = '\0';
2628 if (xtra_choice) {
2629 /* xtra_choice is "bare hands" (wield), "fingertip" (Engrave),
2630 "nothing" (ready Quiver), or "fingers" (apply grease) */
2631 ret = message_menu(HANDS_SYM, PICK_ONE,
2632 xprname((struct obj *) 0, xtra_choice,
2633 HANDS_SYM, TRUE, 0L, 0L)); /* '-' */
2634 } else {
2635 for (otmp = invent; otmp; otmp = otmp->nobj)
2636 if (!lets || otmp->invlet == lets[0])
2637 break;
2638 if (otmp)
2639 ret = message_menu(otmp->invlet,
2640 want_reply ? PICK_ONE : PICK_NONE,
2641 xprname(otmp, (char *) 0, lets[0],
2642 TRUE, 0L, 0L));
2643 }
2644 if (out_cnt)
2645 *out_cnt = -1L; /* select all */
2646 return ret;
2647 }
2648
2649 sortflags = (flags.sortloot == 'f') ? SORTLOOT_LOOT : SORTLOOT_INVLET;
2650 if (flags.sortpack)
2651 sortflags |= SORTLOOT_PACK;
2652 sortedinvent = sortloot(&invent, sortflags, FALSE,
2653 (boolean FDECL((*), (OBJ_P))) 0);
2654
2655 start_menu(win);
2656 any = zeroany;
2657 if (wizard && iflags.override_ID) {
2658 int unid_cnt;
2659 char prompt[QBUFSZ];
2660
2661 unid_cnt = count_unidentified(invent);
2662 Sprintf(prompt, "Debug Identify"); /* 'title' rather than 'prompt' */
2663 if (unid_cnt)
2664 Sprintf(eos(prompt),
2665 " -- unidentified or partially identified item%s",
2666 plur(unid_cnt));
2667 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, prompt, MENU_UNSELECTED);
2668 if (!unid_cnt) {
2669 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
2670 "(all items are permanently identified already)",
2671 MENU_UNSELECTED);
2672 gotsomething = TRUE;
2673 } else {
2674 any.a_obj = &wizid_fakeobj;
2675 Sprintf(prompt, "select %s to permanently identify",
2676 (unid_cnt == 1) ? "it": "any or all of them");
2677 /* wiz_identify stuffed the wiz_identify command character (^I)
2678 into iflags.override_ID for our use as an accelerator;
2679 it could be ambiguous if player has assigned a letter to
2680 the #wizidentify command, so include it as a group accelator
2681 but use '_' as the primary selector */
2682 if (unid_cnt > 1)
2683 Sprintf(eos(prompt), " (%s for all)",
2684 visctrl(iflags.override_ID));
2685 add_menu(win, NO_GLYPH, &any, '_', iflags.override_ID, ATR_NONE,
2686 prompt, MENU_UNSELECTED);
2687 gotsomething = TRUE;
2688 }
2689 } else if (xtra_choice) {
2690 /* wizard override ID and xtra_choice are mutually exclusive */
2691 if (flags.sortpack)
2692 add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2693 "Miscellaneous", MENU_UNSELECTED);
2694 any.a_char = HANDS_SYM; /* '-' */
2695 add_menu(win, NO_GLYPH, &any, HANDS_SYM, 0, ATR_NONE,
2696 xtra_choice, MENU_UNSELECTED);
2697 gotsomething = TRUE;
2698 }
2699 nextclass:
2700 classcount = 0;
2701 for (srtinv = sortedinvent; (otmp = srtinv->obj) != 0; ++srtinv) {
2702 if (lets && !index(lets, otmp->invlet))
2703 continue;
2704 if (!flags.sortpack || otmp->oclass == *invlet) {
2705 if (wizid && !not_fully_identified(otmp))
2706 continue;
2707 any = zeroany; /* all bits zero */
2708 ilet = otmp->invlet;
2709 if (flags.sortpack && !classcount) {
2710 add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2711 let_to_name(*invlet, FALSE,
2712 (want_reply && iflags.menu_head_objsym)),
2713 MENU_UNSELECTED);
2714 classcount++;
2715 }
2716 if (wizid)
2717 any.a_obj = otmp;
2718 else
2719 any.a_char = ilet;
2720 add_menu(win, obj_to_glyph(otmp, rn2_on_display_rng), &any, ilet,
2721 wizid ? def_oc_syms[(int) otmp->oclass].sym : 0,
2722 ATR_NONE, doname(otmp), MENU_UNSELECTED);
2723 gotsomething = TRUE;
2724 }
2725 }
2726 if (flags.sortpack) {
2727 if (*++invlet)
2728 goto nextclass;
2729 if (--invlet != venom_inv) {
2730 invlet = venom_inv;
2731 goto nextclass;
2732 }
2733 }
2734 if (iflags.force_invmenu && lets && want_reply) {
2735 any = zeroany;
2736 add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2737 "Special", MENU_UNSELECTED);
2738 any.a_char = '*';
2739 add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE,
2740 "(list everything)", MENU_UNSELECTED);
2741 gotsomething = TRUE;
2742 }
2743 unsortloot(&sortedinvent);
2744 /* for permanent inventory where we intend to show everything but
2745 nothing has been listed (because there isn't anyhing to list;
2746 the n==0 case above gets skipped for perm_invent), put something
2747 into the menu */
2748 if (iflags.perm_invent && !lets && !gotsomething) {
2749 any = zeroany;
2750 add_menu(win, NO_GLYPH, &any, 0, 0, 0,
2751 not_carrying_anything, MENU_UNSELECTED);
2752 want_reply = FALSE;
2753 }
2754 end_menu(win, query && *query ? query : (char *) 0);
2755
2756 n = select_menu(win,
2757 wizid ? PICK_ANY : want_reply ? PICK_ONE : PICK_NONE,
2758 &selected);
2759 if (n > 0) {
2760 if (wizid) {
2761 int i;
2762
2763 /* identifying items will update perm_invent, calling this
2764 routine recursively, and we don't want the nested call
2765 to filter on unID'd items */
2766 iflags.override_ID = 0;
2767 ret = '\0';
2768 for (i = 0; i < n; ++i) {
2769 otmp = selected[i].item.a_obj;
2770 if (otmp == &wizid_fakeobj) {
2771 identify_pack(0, FALSE);
2772 } else {
2773 if (not_fully_identified(otmp))
2774 (void) identify(otmp);
2775 }
2776 }
2777 } else {
2778 ret = selected[0].item.a_char;
2779 if (out_cnt)
2780 *out_cnt = selected[0].count;
2781 }
2782 free((genericptr_t) selected);
2783 } else
2784 ret = !n ? '\0' : '\033'; /* cancelled */
2785
2786 return ret;
2787 }
2788
2789 /*
2790 * If lets == NULL or "", list all objects in the inventory. Otherwise,
2791 * list all objects with object classes that match the order in lets.
2792 *
2793 * Returns the letter identifier of a selected item, or 0 if nothing
2794 * was selected.
2795 */
2796 char
display_inventory(lets,want_reply)2797 display_inventory(lets, want_reply)
2798 const char *lets;
2799 boolean want_reply;
2800 {
2801 return display_pickinv(lets, (char *) 0, (char *) 0,
2802 want_reply, (long *) 0);
2803 }
2804
2805 /*
2806 * Show what is current using inventory letters.
2807 *
2808 */
2809 STATIC_OVL char
display_used_invlets(avoidlet)2810 display_used_invlets(avoidlet)
2811 char avoidlet;
2812 {
2813 struct obj *otmp;
2814 char ilet, ret = 0;
2815 char *invlet = flags.inv_order;
2816 int n, classcount, invdone = 0;
2817 winid win;
2818 anything any;
2819 menu_item *selected;
2820
2821 if (invent) {
2822 win = create_nhwindow(NHW_MENU);
2823 start_menu(win);
2824 while (!invdone) {
2825 any = zeroany; /* set all bits to zero */
2826 classcount = 0;
2827 for (otmp = invent; otmp; otmp = otmp->nobj) {
2828 ilet = otmp->invlet;
2829 if (ilet == avoidlet)
2830 continue;
2831 if (!flags.sortpack || otmp->oclass == *invlet) {
2832 if (flags.sortpack && !classcount) {
2833 any = zeroany; /* zero */
2834 add_menu(win, NO_GLYPH, &any, 0, 0,
2835 iflags.menu_headings,
2836 let_to_name(*invlet, FALSE, FALSE),
2837 MENU_UNSELECTED);
2838 classcount++;
2839 }
2840 any.a_char = ilet;
2841 add_menu(win, obj_to_glyph(otmp, rn2_on_display_rng),
2842 &any, ilet, 0, ATR_NONE,
2843 doname(otmp), MENU_UNSELECTED);
2844 }
2845 }
2846 if (flags.sortpack && *++invlet)
2847 continue;
2848 invdone = 1;
2849 }
2850 end_menu(win, "Inventory letters used:");
2851
2852 n = select_menu(win, PICK_ONE, &selected);
2853 if (n > 0) {
2854 ret = selected[0].item.a_char;
2855 free((genericptr_t) selected);
2856 } else
2857 ret = !n ? '\0' : '\033'; /* cancelled */
2858 destroy_nhwindow(win);
2859 }
2860 return ret;
2861 }
2862
2863 /*
2864 * Returns the number of unpaid items within the given list. This includes
2865 * contained objects.
2866 */
2867 int
count_unpaid(list)2868 count_unpaid(list)
2869 struct obj *list;
2870 {
2871 int count = 0;
2872
2873 while (list) {
2874 if (list->unpaid)
2875 count++;
2876 if (Has_contents(list))
2877 count += count_unpaid(list->cobj);
2878 list = list->nobj;
2879 }
2880 return count;
2881 }
2882
2883 /*
2884 * Returns the number of items with b/u/c/unknown within the given list.
2885 * This does NOT include contained objects.
2886 *
2887 * Assumes that the hero sees or touches or otherwise senses the objects
2888 * at some point: bknown is forced for priest[ess], like in xname().
2889 */
2890 int
count_buc(list,type,filterfunc)2891 count_buc(list, type, filterfunc)
2892 struct obj *list;
2893 int type;
2894 boolean FDECL((*filterfunc), (OBJ_P));
2895 {
2896 int count = 0;
2897
2898 for (; list; list = list->nobj) {
2899 /* priests always know bless/curse state */
2900 if (Role_if(PM_PRIEST))
2901 list->bknown = (list->oclass != COIN_CLASS);
2902 /* some actions exclude some or most items */
2903 if (filterfunc && !(*filterfunc)(list))
2904 continue;
2905
2906 /* coins are either uncursed or unknown based upon option setting */
2907 if (list->oclass == COIN_CLASS) {
2908 if (type == (iflags.goldX ? BUC_UNKNOWN : BUC_UNCURSED))
2909 ++count;
2910 continue;
2911 }
2912 /* check whether this object matches the requested type */
2913 if (!list->bknown
2914 ? (type == BUC_UNKNOWN)
2915 : list->blessed ? (type == BUC_BLESSED)
2916 : list->cursed ? (type == BUC_CURSED)
2917 : (type == BUC_UNCURSED))
2918 ++count;
2919 }
2920 return count;
2921 }
2922
2923 /* similar to count_buc(), but tallies all states at once
2924 rather than looking for a specific type */
2925 void
tally_BUCX(list,by_nexthere,bcp,ucp,ccp,xcp,ocp)2926 tally_BUCX(list, by_nexthere, bcp, ucp, ccp, xcp, ocp)
2927 struct obj *list;
2928 boolean by_nexthere;
2929 int *bcp, *ucp, *ccp, *xcp, *ocp;
2930 {
2931 /* Future extensions:
2932 * Skip current_container when list is invent, uchain when
2933 * first object of list is located on the floor. 'ocp' will then
2934 * have a function again (it was a counter for having skipped gold,
2935 * but that's not skipped anymore).
2936 */
2937 *bcp = *ucp = *ccp = *xcp = *ocp = 0;
2938 for ( ; list; list = (by_nexthere ? list->nexthere : list->nobj)) {
2939 /* priests always know bless/curse state */
2940 if (Role_if(PM_PRIEST))
2941 list->bknown = (list->oclass != COIN_CLASS);
2942 /* coins are either uncursed or unknown based upon option setting */
2943 if (list->oclass == COIN_CLASS) {
2944 if (iflags.goldX)
2945 ++(*xcp);
2946 else
2947 ++(*ucp);
2948 continue;
2949 }
2950 /* ordinary items */
2951 if (!list->bknown)
2952 ++(*xcp);
2953 else if (list->blessed)
2954 ++(*bcp);
2955 else if (list->cursed)
2956 ++(*ccp);
2957 else /* neither blessed nor cursed => uncursed */
2958 ++(*ucp);
2959 }
2960 }
2961
2962 /* count everything inside a container, or just shop-owned items inside */
2963 long
count_contents(container,nested,quantity,everything,newdrop)2964 count_contents(container, nested, quantity, everything, newdrop)
2965 struct obj *container;
2966 boolean nested, /* include contents of any nested containers */
2967 quantity, /* count all vs count separate stacks */
2968 everything, /* all objects vs only unpaid objects */
2969 newdrop; /* on floor, but hero-owned items haven't been marked
2970 * no_charge yet and shop-owned items are still marked
2971 * unpaid -- used when asking the player whether to sell */
2972 {
2973 struct obj *otmp, *topc;
2974 boolean shoppy = FALSE;
2975 long count = 0L;
2976
2977 if (!everything && !newdrop) {
2978 xchar x, y;
2979
2980 for (topc = container; topc->where == OBJ_CONTAINED;
2981 topc = topc->ocontainer)
2982 continue;
2983 if (topc->where == OBJ_FLOOR && get_obj_location(topc, &x, &y, 0))
2984 shoppy = costly_spot(x, y);
2985 }
2986 for (otmp = container->cobj; otmp; otmp = otmp->nobj) {
2987 if (nested && Has_contents(otmp))
2988 count += count_contents(otmp, nested, quantity, everything,
2989 newdrop);
2990 if (everything || otmp->unpaid || (shoppy && !otmp->no_charge))
2991 count += quantity ? otmp->quan : 1L;
2992 }
2993 return count;
2994 }
2995
2996 STATIC_OVL void
dounpaid()2997 dounpaid()
2998 {
2999 winid win;
3000 struct obj *otmp, *marker, *contnr;
3001 register char ilet;
3002 char *invlet = flags.inv_order;
3003 int classcount, count, num_so_far;
3004 long cost, totcost;
3005
3006 count = count_unpaid(invent);
3007 otmp = marker = contnr = (struct obj *) 0;
3008
3009 if (count == 1) {
3010 otmp = find_unpaid(invent, &marker);
3011 contnr = unknwn_contnr_contents(otmp);
3012 }
3013 if (otmp && !contnr) {
3014 /* 1 item; use pline instead of popup menu */
3015 cost = unpaid_cost(otmp, FALSE);
3016 iflags.suppress_price++; /* suppress "(unpaid)" suffix */
3017 pline1(xprname(otmp, distant_name(otmp, doname),
3018 carried(otmp) ? otmp->invlet : CONTAINED_SYM,
3019 TRUE, cost, 0L));
3020 iflags.suppress_price--;
3021 return;
3022 }
3023
3024 win = create_nhwindow(NHW_MENU);
3025 cost = totcost = 0;
3026 num_so_far = 0; /* count of # printed so far */
3027 if (!flags.invlet_constant)
3028 reassign();
3029
3030 do {
3031 classcount = 0;
3032 for (otmp = invent; otmp; otmp = otmp->nobj) {
3033 ilet = otmp->invlet;
3034 if (otmp->unpaid) {
3035 if (!flags.sortpack || otmp->oclass == *invlet) {
3036 if (flags.sortpack && !classcount) {
3037 putstr(win, 0, let_to_name(*invlet, TRUE, FALSE));
3038 classcount++;
3039 }
3040
3041 totcost += cost = unpaid_cost(otmp, FALSE);
3042 iflags.suppress_price++; /* suppress "(unpaid)" suffix */
3043 putstr(win, 0, xprname(otmp, distant_name(otmp, doname),
3044 ilet, TRUE, cost, 0L));
3045 iflags.suppress_price--;
3046 num_so_far++;
3047 }
3048 }
3049 }
3050 } while (flags.sortpack && (*++invlet));
3051
3052 if (count > num_so_far) {
3053 /* something unpaid is contained */
3054 if (flags.sortpack)
3055 putstr(win, 0, let_to_name(CONTAINED_SYM, TRUE, FALSE));
3056 /*
3057 * Search through the container objects in the inventory for
3058 * unpaid items. The top level inventory items have already
3059 * been listed.
3060 */
3061 for (otmp = invent; otmp; otmp = otmp->nobj) {
3062 if (Has_contents(otmp)) {
3063 long contcost = 0L;
3064
3065 marker = (struct obj *) 0; /* haven't found any */
3066 while (find_unpaid(otmp->cobj, &marker)) {
3067 totcost += cost = unpaid_cost(marker, FALSE);
3068 contcost += cost;
3069 if (otmp->cknown) {
3070 iflags.suppress_price++; /* suppress "(unpaid)" sfx */
3071 putstr(win, 0,
3072 xprname(marker, distant_name(marker, doname),
3073 CONTAINED_SYM, TRUE, cost, 0L));
3074 iflags.suppress_price--;
3075 }
3076 }
3077 if (!otmp->cknown) {
3078 char contbuf[BUFSZ];
3079
3080 /* Shopkeeper knows what to charge for contents */
3081 Sprintf(contbuf, "%s contents", s_suffix(xname(otmp)));
3082 putstr(win, 0,
3083 xprname((struct obj *) 0, contbuf, CONTAINED_SYM,
3084 TRUE, contcost, 0L));
3085 }
3086 }
3087 }
3088 }
3089
3090 putstr(win, 0, "");
3091 putstr(win, 0,
3092 xprname((struct obj *) 0, "Total:", '*', FALSE, totcost, 0L));
3093 display_nhwindow(win, FALSE);
3094 destroy_nhwindow(win);
3095 }
3096
3097 /* query objlist callback: return TRUE if obj type matches "this_type" */
3098 static int this_type;
3099
3100 STATIC_OVL boolean
this_type_only(obj)3101 this_type_only(obj)
3102 struct obj *obj;
3103 {
3104 boolean res = (obj->oclass == this_type);
3105
3106 if (obj->oclass == COIN_CLASS) {
3107 /* if filtering by bless/curse state, gold is classified as
3108 either unknown or uncursed based on user option setting */
3109 if (this_type && index("BUCX", this_type))
3110 res = (this_type == (iflags.goldX ? 'X' : 'U'));
3111 } else {
3112 switch (this_type) {
3113 case 'B':
3114 res = (obj->bknown && obj->blessed);
3115 break;
3116 case 'U':
3117 res = (obj->bknown && !(obj->blessed || obj->cursed));
3118 break;
3119 case 'C':
3120 res = (obj->bknown && obj->cursed);
3121 break;
3122 case 'X':
3123 res = !obj->bknown;
3124 break;
3125 default:
3126 break; /* use 'res' as-is */
3127 }
3128 }
3129 return res;
3130 }
3131
3132 /* the 'I' command */
3133 int
dotypeinv()3134 dotypeinv()
3135 {
3136 char c = '\0';
3137 int n, i = 0;
3138 char *extra_types, types[BUFSZ];
3139 int class_count, oclass, unpaid_count, itemcount;
3140 int bcnt, ccnt, ucnt, xcnt, ocnt;
3141 boolean billx = *u.ushops && doinvbill(0);
3142 menu_item *pick_list;
3143 boolean traditional = TRUE;
3144 const char *prompt = "What type of object do you want an inventory of?";
3145
3146 if (!invent && !billx) {
3147 You("aren't carrying anything.");
3148 return 0;
3149 }
3150 unpaid_count = count_unpaid(invent);
3151 tally_BUCX(invent, FALSE, &bcnt, &ucnt, &ccnt, &xcnt, &ocnt);
3152
3153 if (flags.menu_style != MENU_TRADITIONAL) {
3154 if (flags.menu_style == MENU_FULL
3155 || flags.menu_style == MENU_PARTIAL) {
3156 traditional = FALSE;
3157 i = UNPAID_TYPES;
3158 if (billx)
3159 i |= BILLED_TYPES;
3160 if (bcnt)
3161 i |= BUC_BLESSED;
3162 if (ucnt)
3163 i |= BUC_UNCURSED;
3164 if (ccnt)
3165 i |= BUC_CURSED;
3166 if (xcnt)
3167 i |= BUC_UNKNOWN;
3168 n = query_category(prompt, invent, i, &pick_list, PICK_ONE);
3169 if (!n)
3170 return 0;
3171 this_type = c = pick_list[0].item.a_int;
3172 free((genericptr_t) pick_list);
3173 }
3174 }
3175 if (traditional) {
3176 /* collect a list of classes of objects carried, for use as a prompt
3177 */
3178 types[0] = 0;
3179 class_count = collect_obj_classes(types, invent, FALSE,
3180 (boolean FDECL((*), (OBJ_P))) 0,
3181 &itemcount);
3182 if (unpaid_count || billx || (bcnt + ccnt + ucnt + xcnt) != 0)
3183 types[class_count++] = ' ';
3184 if (unpaid_count)
3185 types[class_count++] = 'u';
3186 if (billx)
3187 types[class_count++] = 'x';
3188 if (bcnt)
3189 types[class_count++] = 'B';
3190 if (ucnt)
3191 types[class_count++] = 'U';
3192 if (ccnt)
3193 types[class_count++] = 'C';
3194 if (xcnt)
3195 types[class_count++] = 'X';
3196 types[class_count] = '\0';
3197 /* add everything not already included; user won't see these */
3198 extra_types = eos(types);
3199 *extra_types++ = '\033';
3200 if (!unpaid_count)
3201 *extra_types++ = 'u';
3202 if (!billx)
3203 *extra_types++ = 'x';
3204 if (!bcnt)
3205 *extra_types++ = 'B';
3206 if (!ucnt)
3207 *extra_types++ = 'U';
3208 if (!ccnt)
3209 *extra_types++ = 'C';
3210 if (!xcnt)
3211 *extra_types++ = 'X';
3212 *extra_types = '\0'; /* for index() */
3213 for (i = 0; i < MAXOCLASSES; i++)
3214 if (!index(types, def_oc_syms[i].sym)) {
3215 *extra_types++ = def_oc_syms[i].sym;
3216 *extra_types = '\0';
3217 }
3218
3219 if (class_count > 1) {
3220 c = yn_function(prompt, types, '\0');
3221 savech(c);
3222 if (c == '\0') {
3223 clear_nhwindow(WIN_MESSAGE);
3224 return 0;
3225 }
3226 } else {
3227 /* only one thing to itemize */
3228 if (unpaid_count)
3229 c = 'u';
3230 else if (billx)
3231 c = 'x';
3232 else
3233 c = types[0];
3234 }
3235 }
3236 if (c == 'x' || (c == 'X' && billx && !xcnt)) {
3237 if (billx)
3238 (void) doinvbill(1);
3239 else
3240 pline("No used-up objects%s.",
3241 unpaid_count ? " on your shopping bill" : "");
3242 return 0;
3243 }
3244 if (c == 'u' || (c == 'U' && unpaid_count && !ucnt)) {
3245 if (unpaid_count)
3246 dounpaid();
3247 else
3248 You("are not carrying any unpaid objects.");
3249 return 0;
3250 }
3251 if (traditional) {
3252 if (index("BUCX", c))
3253 oclass = c; /* not a class but understood by this_type_only() */
3254 else
3255 oclass = def_char_to_objclass(c); /* change to object class */
3256
3257 if (oclass == COIN_CLASS)
3258 return doprgold();
3259 if (index(types, c) > index(types, '\033')) {
3260 /* '> ESC' => hidden choice, something known not to be carried */
3261 const char *before = "", *after = "";
3262
3263 switch (c) {
3264 case 'B':
3265 before = "known to be blessed ";
3266 break;
3267 case 'U':
3268 before = "known to be uncursed ";
3269 break;
3270 case 'C':
3271 before = "known to be cursed ";
3272 break;
3273 case 'X':
3274 after = " whose blessed/uncursed/cursed status is unknown";
3275 break; /* better phrasing is desirable */
3276 default:
3277 /* 'c' is an object class, because we've already handled
3278 all the non-class letters which were put into 'types[]';
3279 could/should move object class names[] array from below
3280 to somewhere above so that we can access it here (via
3281 lcase(strcpy(classnamebuf, names[(int) c]))), but the
3282 game-play value of doing so is low... */
3283 before = "such ";
3284 break;
3285 }
3286 You("have no %sobjects%s.", before, after);
3287 return 0;
3288 }
3289 this_type = oclass;
3290 }
3291 if (query_objlist((char *) 0, &invent,
3292 ((flags.invlet_constant ? USE_INVLET : 0)
3293 | INVORDER_SORT),
3294 &pick_list, PICK_NONE, this_type_only) > 0)
3295 free((genericptr_t) pick_list);
3296 return 0;
3297 }
3298
3299 /* return a string describing the dungeon feature at <x,y> if there
3300 is one worth mentioning at that location; otherwise null */
3301 const char *
dfeature_at(x,y,buf)3302 dfeature_at(x, y, buf)
3303 int x, y;
3304 char *buf;
3305 {
3306 struct rm *lev = &levl[x][y];
3307 int ltyp = lev->typ, cmap = -1;
3308 const char *dfeature = 0;
3309 static char altbuf[BUFSZ];
3310
3311 if (IS_DOOR(ltyp)) {
3312 switch (lev->doormask) {
3313 case D_NODOOR:
3314 cmap = S_ndoor;
3315 break; /* "doorway" */
3316 case D_ISOPEN:
3317 cmap = S_vodoor;
3318 break; /* "open door" */
3319 case D_BROKEN:
3320 dfeature = "broken door";
3321 break;
3322 default:
3323 cmap = S_vcdoor;
3324 break; /* "closed door" */
3325 }
3326 /* override door description for open drawbridge */
3327 if (is_drawbridge_wall(x, y) >= 0)
3328 dfeature = "open drawbridge portcullis", cmap = -1;
3329 } else if (IS_FOUNTAIN(ltyp))
3330 cmap = S_fountain; /* "fountain" */
3331 else if (IS_THRONE(ltyp))
3332 cmap = S_throne; /* "opulent throne" */
3333 else if (is_lava(x, y))
3334 cmap = S_lava; /* "molten lava" */
3335 else if (is_ice(x, y))
3336 cmap = S_ice; /* "ice" */
3337 else if (is_pool(x, y))
3338 dfeature = "pool of water";
3339 else if (IS_SINK(ltyp))
3340 cmap = S_sink; /* "sink" */
3341 else if (IS_ALTAR(ltyp)) {
3342 Sprintf(altbuf, "%saltar to %s (%s)",
3343 ((lev->altarmask & AM_SHRINE)
3344 && (Is_astralevel(&u.uz) || Is_sanctum(&u.uz)))
3345 ? "high "
3346 : "",
3347 a_gname(),
3348 align_str(Amask2align(lev->altarmask & ~AM_SHRINE)));
3349 dfeature = altbuf;
3350 } else if ((x == xupstair && y == yupstair)
3351 || (x == sstairs.sx && y == sstairs.sy && sstairs.up))
3352 cmap = S_upstair; /* "staircase up" */
3353 else if ((x == xdnstair && y == ydnstair)
3354 || (x == sstairs.sx && y == sstairs.sy && !sstairs.up))
3355 cmap = S_dnstair; /* "staircase down" */
3356 else if (x == xupladder && y == yupladder)
3357 cmap = S_upladder; /* "ladder up" */
3358 else if (x == xdnladder && y == ydnladder)
3359 cmap = S_dnladder; /* "ladder down" */
3360 else if (ltyp == DRAWBRIDGE_DOWN)
3361 cmap = S_vodbridge; /* "lowered drawbridge" */
3362 else if (ltyp == DBWALL)
3363 cmap = S_vcdbridge; /* "raised drawbridge" */
3364 else if (IS_GRAVE(ltyp))
3365 cmap = S_grave; /* "grave" */
3366 else if (ltyp == TREE)
3367 cmap = S_tree; /* "tree" */
3368 else if (ltyp == IRONBARS)
3369 dfeature = "set of iron bars";
3370
3371 if (cmap >= 0)
3372 dfeature = defsyms[cmap].explanation;
3373 if (dfeature)
3374 Strcpy(buf, dfeature);
3375 return dfeature;
3376 }
3377
3378 /* look at what is here; if there are many objects (pile_limit or more),
3379 don't show them unless obj_cnt is 0 */
3380 int
look_here(obj_cnt,picked_some)3381 look_here(obj_cnt, picked_some)
3382 int obj_cnt; /* obj_cnt > 0 implies that autopickup is in progress */
3383 boolean picked_some;
3384 {
3385 struct obj *otmp;
3386 struct trap *trap;
3387 const char *verb = Blind ? "feel" : "see";
3388 const char *dfeature = (char *) 0;
3389 char fbuf[BUFSZ], fbuf2[BUFSZ];
3390 winid tmpwin;
3391 boolean skip_objects, felt_cockatrice = FALSE;
3392
3393 /* default pile_limit is 5; a value of 0 means "never skip"
3394 (and 1 effectively forces "always skip") */
3395 skip_objects = (flags.pile_limit > 0 && obj_cnt >= flags.pile_limit);
3396 if (u.uswallow && u.ustuck) {
3397 struct monst *mtmp = u.ustuck;
3398
3399 /*
3400 * FIXME?
3401 * Engulfer's inventory can include worn items (specific case is
3402 * Juiblex being created with an amulet as random defensive item)
3403 * which will be flagged as "(being worn)". This code includes
3404 * such a worn item under the header "Contents of <mon>'s stomach",
3405 * a nifty trick for how/where to wear stuff. The situation is
3406 * rare enough to turn a blind eye.
3407 *
3408 * 3.6.3: Pickup has been changed to decline to pick up a worn
3409 * item from inside an engulfer, but if player tries, it just
3410 * says "you can't" without giving a reason why (which would be
3411 * something along the lines of "because it's worn on the outside
3412 * so is unreachable from in here...").
3413 */
3414 Sprintf(fbuf, "Contents of %s %s", s_suffix(mon_nam(mtmp)),
3415 mbodypart(mtmp, STOMACH));
3416 /* Skip "Contents of " by using fbuf index 12 */
3417 You("%s to %s what is lying in %s.", Blind ? "try" : "look around",
3418 verb, &fbuf[12]);
3419 otmp = mtmp->minvent;
3420 if (otmp) {
3421 for (; otmp; otmp = otmp->nobj) {
3422 /* If swallower is an animal, it should have become stone
3423 * but... */
3424 if (otmp->otyp == CORPSE)
3425 feel_cockatrice(otmp, FALSE);
3426 }
3427 if (Blind)
3428 Strcpy(fbuf, "You feel");
3429 Strcat(fbuf, ":");
3430 (void) display_minventory(mtmp, MINV_ALL | PICK_NONE, fbuf);
3431 } else {
3432 You("%s no objects here.", verb);
3433 }
3434 return !!Blind;
3435 }
3436 if (!skip_objects && (trap = t_at(u.ux, u.uy)) && trap->tseen)
3437 There("is %s here.",
3438 an(defsyms[trap_to_defsym(trap->ttyp)].explanation));
3439
3440 otmp = level.objects[u.ux][u.uy];
3441 dfeature = dfeature_at(u.ux, u.uy, fbuf2);
3442 if (dfeature && !strcmp(dfeature, "pool of water") && Underwater)
3443 dfeature = 0;
3444
3445 if (Blind) {
3446 boolean drift = Is_airlevel(&u.uz) || Is_waterlevel(&u.uz);
3447
3448 if (dfeature && !strncmp(dfeature, "altar ", 6)) {
3449 /* don't say "altar" twice, dfeature has more info */
3450 You("try to feel what is here.");
3451 } else {
3452 const char *where = (Blind && !can_reach_floor(TRUE))
3453 ? "lying beneath you"
3454 : "lying here on the ",
3455 *onwhat = (Blind && !can_reach_floor(TRUE))
3456 ? ""
3457 : surface(u.ux, u.uy);
3458
3459 You("try to feel what is %s%s.", drift ? "floating here" : where,
3460 drift ? "" : onwhat);
3461 }
3462 if (dfeature && !drift && !strcmp(dfeature, surface(u.ux, u.uy)))
3463 dfeature = 0; /* ice already identified */
3464 if (!can_reach_floor(TRUE)) {
3465 pline("But you can't reach it!");
3466 return 0;
3467 }
3468 }
3469
3470 if (dfeature)
3471 Sprintf(fbuf, "There is %s here.", an(dfeature));
3472
3473 if (!otmp || is_lava(u.ux, u.uy)
3474 || (is_pool(u.ux, u.uy) && !Underwater)) {
3475 if (dfeature)
3476 pline1(fbuf);
3477 read_engr_at(u.ux, u.uy); /* Eric Backus */
3478 if (!skip_objects && (Blind || !dfeature))
3479 You("%s no objects here.", verb);
3480 return !!Blind;
3481 }
3482 /* we know there is something here */
3483
3484 if (skip_objects) {
3485 if (dfeature)
3486 pline1(fbuf);
3487 read_engr_at(u.ux, u.uy); /* Eric Backus */
3488 if (obj_cnt == 1 && otmp->quan == 1L)
3489 There("is %s object here.", picked_some ? "another" : "an");
3490 else
3491 There("are %s%s objects here.",
3492 (obj_cnt < 5)
3493 ? "a few"
3494 : (obj_cnt < 10)
3495 ? "several"
3496 : "many",
3497 picked_some ? " more" : "");
3498 for (; otmp; otmp = otmp->nexthere)
3499 if (otmp->otyp == CORPSE && will_feel_cockatrice(otmp, FALSE)) {
3500 pline("%s %s%s.",
3501 (obj_cnt > 1)
3502 ? "Including"
3503 : (otmp->quan > 1L)
3504 ? "They're"
3505 : "It's",
3506 corpse_xname(otmp, (const char *) 0, CXN_ARTICLE),
3507 poly_when_stoned(youmonst.data)
3508 ? ""
3509 : ", unfortunately");
3510 feel_cockatrice(otmp, FALSE);
3511 break;
3512 }
3513 } else if (!otmp->nexthere) {
3514 /* only one object */
3515 if (dfeature)
3516 pline1(fbuf);
3517 read_engr_at(u.ux, u.uy); /* Eric Backus */
3518 You("%s here %s.", verb, doname_with_price(otmp));
3519 iflags.last_msg = PLNMSG_ONE_ITEM_HERE;
3520 if (otmp->otyp == CORPSE)
3521 feel_cockatrice(otmp, FALSE);
3522 } else {
3523 char buf[BUFSZ];
3524
3525 display_nhwindow(WIN_MESSAGE, FALSE);
3526 tmpwin = create_nhwindow(NHW_MENU);
3527 if (dfeature) {
3528 putstr(tmpwin, 0, fbuf);
3529 putstr(tmpwin, 0, "");
3530 }
3531 Sprintf(buf, "%s that %s here:",
3532 picked_some ? "Other things" : "Things",
3533 Blind ? "you feel" : "are");
3534 putstr(tmpwin, 0, buf);
3535 for (; otmp; otmp = otmp->nexthere) {
3536 if (otmp->otyp == CORPSE && will_feel_cockatrice(otmp, FALSE)) {
3537 felt_cockatrice = TRUE;
3538 Sprintf(buf, "%s...", doname(otmp));
3539 putstr(tmpwin, 0, buf);
3540 break;
3541 }
3542 putstr(tmpwin, 0, doname_with_price(otmp));
3543 }
3544 display_nhwindow(tmpwin, TRUE);
3545 destroy_nhwindow(tmpwin);
3546 if (felt_cockatrice)
3547 feel_cockatrice(otmp, FALSE);
3548 read_engr_at(u.ux, u.uy); /* Eric Backus */
3549 }
3550 return !!Blind;
3551 }
3552
3553 /* the ':' command - explicitly look at what is here, including all objects */
3554 int
dolook()3555 dolook()
3556 {
3557 int res;
3558
3559 /* don't let
3560 MSGTYPE={norep,noshow} "You see here"
3561 interfere with feedback from the look-here command */
3562 hide_unhide_msgtypes(TRUE, MSGTYP_MASK_REP_SHOW);
3563 res = look_here(0, FALSE);
3564 /* restore normal msgtype handling */
3565 hide_unhide_msgtypes(FALSE, MSGTYP_MASK_REP_SHOW);
3566 return res;
3567 }
3568
3569 boolean
will_feel_cockatrice(otmp,force_touch)3570 will_feel_cockatrice(otmp, force_touch)
3571 struct obj *otmp;
3572 boolean force_touch;
3573 {
3574 if ((Blind || force_touch) && !uarmg && !Stone_resistance
3575 && (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm])))
3576 return TRUE;
3577 return FALSE;
3578 }
3579
3580 void
feel_cockatrice(otmp,force_touch)3581 feel_cockatrice(otmp, force_touch)
3582 struct obj *otmp;
3583 boolean force_touch;
3584 {
3585 char kbuf[BUFSZ];
3586
3587 if (will_feel_cockatrice(otmp, force_touch)) {
3588 /* "the <cockatrice> corpse" */
3589 Strcpy(kbuf, corpse_xname(otmp, (const char *) 0, CXN_PFX_THE));
3590
3591 if (poly_when_stoned(youmonst.data))
3592 You("touched %s with your bare %s.", kbuf,
3593 makeplural(body_part(HAND)));
3594 else
3595 pline("Touching %s is a fatal mistake...", kbuf);
3596 /* normalize body shape here; hand, not body_part(HAND) */
3597 Sprintf(kbuf, "touching %s bare-handed", killer_xname(otmp));
3598 /* will call polymon() for the poly_when_stoned() case */
3599 instapetrify(kbuf);
3600 }
3601 }
3602
3603 void
stackobj(obj)3604 stackobj(obj)
3605 struct obj *obj;
3606 {
3607 struct obj *otmp;
3608
3609 for (otmp = level.objects[obj->ox][obj->oy]; otmp; otmp = otmp->nexthere)
3610 if (otmp != obj && merged(&obj, &otmp))
3611 break;
3612 return;
3613 }
3614
3615 /* returns TRUE if obj & otmp can be merged; used in invent.c and mkobj.c */
3616 boolean
mergable(otmp,obj)3617 mergable(otmp, obj)
3618 register struct obj *otmp, *obj;
3619 {
3620 int objnamelth = 0, otmpnamelth = 0;
3621
3622 /* fail if already the same object, if different types, if either is
3623 explicitly marked to prevent merge, or if not mergable in general */
3624 if (obj == otmp || obj->otyp != otmp->otyp
3625 || obj->nomerge || otmp->nomerge || !objects[obj->otyp].oc_merge)
3626 return FALSE;
3627
3628 /* coins of the same kind will always merge */
3629 if (obj->oclass == COIN_CLASS)
3630 return TRUE;
3631
3632 if (obj->bypass != otmp->bypass
3633 || obj->cursed != otmp->cursed || obj->blessed != otmp->blessed)
3634 return FALSE;
3635
3636 if (obj->globby)
3637 return TRUE;
3638 /* Checks beyond this point either aren't applicable to globs
3639 * or don't inhibit their merger.
3640 */
3641
3642 if (obj->unpaid != otmp->unpaid || obj->spe != otmp->spe
3643 || obj->no_charge != otmp->no_charge || obj->obroken != otmp->obroken
3644 || obj->otrapped != otmp->otrapped || obj->lamplit != otmp->lamplit)
3645 return FALSE;
3646
3647 if (obj->oclass == FOOD_CLASS
3648 && (obj->oeaten != otmp->oeaten || obj->orotten != otmp->orotten))
3649 return FALSE;
3650
3651 if (obj->dknown != otmp->dknown
3652 || (obj->bknown != otmp->bknown && !Role_if(PM_PRIEST))
3653 || obj->oeroded != otmp->oeroded || obj->oeroded2 != otmp->oeroded2
3654 || obj->greased != otmp->greased)
3655 return FALSE;
3656
3657 if ((obj->oclass == WEAPON_CLASS || obj->oclass == ARMOR_CLASS)
3658 && (obj->oerodeproof != otmp->oerodeproof
3659 || obj->rknown != otmp->rknown))
3660 return FALSE;
3661
3662 if (obj->otyp == CORPSE || obj->otyp == EGG || obj->otyp == TIN) {
3663 if (obj->corpsenm != otmp->corpsenm)
3664 return FALSE;
3665 }
3666
3667 /* hatching eggs don't merge; ditto for revivable corpses */
3668 if ((obj->otyp == EGG && (obj->timed || otmp->timed))
3669 || (obj->otyp == CORPSE && otmp->corpsenm >= LOW_PM
3670 && is_reviver(&mons[otmp->corpsenm])))
3671 return FALSE;
3672
3673 /* allow candle merging only if their ages are close */
3674 /* see begin_burn() for a reference for the magic "25" */
3675 if (Is_candle(obj) && obj->age / 25 != otmp->age / 25)
3676 return FALSE;
3677
3678 /* burning potions of oil never merge */
3679 if (obj->otyp == POT_OIL && obj->lamplit)
3680 return FALSE;
3681
3682 /* don't merge surcharged item with base-cost item */
3683 if (obj->unpaid && !same_price(obj, otmp))
3684 return FALSE;
3685
3686 /* if they have names, make sure they're the same */
3687 objnamelth = strlen(safe_oname(obj));
3688 otmpnamelth = strlen(safe_oname(otmp));
3689 if ((objnamelth != otmpnamelth
3690 && ((objnamelth && otmpnamelth) || obj->otyp == CORPSE))
3691 || (objnamelth && otmpnamelth
3692 && strncmp(ONAME(obj), ONAME(otmp), objnamelth)))
3693 return FALSE;
3694
3695 /* for the moment, any additional information is incompatible */
3696 if (has_omonst(obj) || has_omid(obj) || has_olong(obj) || has_omonst(otmp)
3697 || has_omid(otmp) || has_olong(otmp))
3698 return FALSE;
3699
3700 if (obj->oartifact != otmp->oartifact)
3701 return FALSE;
3702
3703 if (obj->known == otmp->known || !objects[otmp->otyp].oc_uses_known) {
3704 return (boolean) objects[obj->otyp].oc_merge;
3705 } else
3706 return FALSE;
3707 }
3708
3709 /* the '$' command */
3710 int
doprgold()3711 doprgold()
3712 {
3713 /* the messages used to refer to "carrying gold", but that didn't
3714 take containers into account */
3715 long umoney = money_cnt(invent);
3716
3717 if (!umoney)
3718 Your("wallet is empty.");
3719 else
3720 Your("wallet contains %ld %s.", umoney, currency(umoney));
3721 shopper_financial_report();
3722 return 0;
3723 }
3724
3725 /* the ')' command */
3726 int
doprwep()3727 doprwep()
3728 {
3729 if (!uwep) {
3730 You("are empty %s.", body_part(HANDED));
3731 } else {
3732 prinv((char *) 0, uwep, 0L);
3733 if (u.twoweap)
3734 prinv((char *) 0, uswapwep, 0L);
3735 }
3736 return 0;
3737 }
3738
3739 /* caller is responsible for checking !wearing_armor() */
3740 STATIC_OVL void
noarmor(report_uskin)3741 noarmor(report_uskin)
3742 boolean report_uskin;
3743 {
3744 if (!uskin || !report_uskin) {
3745 You("are not wearing any armor.");
3746 } else {
3747 char *p, *uskinname, buf[BUFSZ];
3748
3749 uskinname = strcpy(buf, simpleonames(uskin));
3750 /* shorten "set of <color> dragon scales" to "<color> scales"
3751 and "<color> dragon scale mail" to "<color> scale mail" */
3752 if (!strncmpi(uskinname, "set of ", 7))
3753 uskinname += 7;
3754 if ((p = strstri(uskinname, " dragon ")) != 0)
3755 while ((p[1] = p[8]) != '\0')
3756 ++p;
3757
3758 You("are not wearing armor but have %s embedded in your skin.",
3759 uskinname);
3760 }
3761 }
3762
3763 /* the '[' command */
3764 int
doprarm()3765 doprarm()
3766 {
3767 char lets[8];
3768 register int ct = 0;
3769 /*
3770 * Note: players sometimes get here by pressing a function key which
3771 * transmits ''ESC [ <something>'' rather than by pressing '[';
3772 * there's nothing we can--or should-do about that here.
3773 */
3774
3775 if (!wearing_armor()) {
3776 noarmor(TRUE);
3777 } else {
3778 if (uarmu)
3779 lets[ct++] = obj_to_let(uarmu);
3780 if (uarm)
3781 lets[ct++] = obj_to_let(uarm);
3782 if (uarmc)
3783 lets[ct++] = obj_to_let(uarmc);
3784 if (uarmh)
3785 lets[ct++] = obj_to_let(uarmh);
3786 if (uarms)
3787 lets[ct++] = obj_to_let(uarms);
3788 if (uarmg)
3789 lets[ct++] = obj_to_let(uarmg);
3790 if (uarmf)
3791 lets[ct++] = obj_to_let(uarmf);
3792 lets[ct] = 0;
3793 (void) display_inventory(lets, FALSE);
3794 }
3795 return 0;
3796 }
3797
3798 /* the '=' command */
3799 int
doprring()3800 doprring()
3801 {
3802 if (!uleft && !uright)
3803 You("are not wearing any rings.");
3804 else {
3805 char lets[3];
3806 register int ct = 0;
3807
3808 if (uleft)
3809 lets[ct++] = obj_to_let(uleft);
3810 if (uright)
3811 lets[ct++] = obj_to_let(uright);
3812 lets[ct] = 0;
3813 (void) display_inventory(lets, FALSE);
3814 }
3815 return 0;
3816 }
3817
3818 /* the '"' command */
3819 int
dopramulet()3820 dopramulet()
3821 {
3822 if (!uamul)
3823 You("are not wearing an amulet.");
3824 else
3825 prinv((char *) 0, uamul, 0L);
3826 return 0;
3827 }
3828
3829 STATIC_OVL boolean
tool_in_use(obj)3830 tool_in_use(obj)
3831 struct obj *obj;
3832 {
3833 if ((obj->owornmask & (W_TOOL | W_SADDLE)) != 0L)
3834 return TRUE;
3835 if (obj->oclass != TOOL_CLASS)
3836 return FALSE;
3837 return (boolean) (obj == uwep || obj->lamplit
3838 || (obj->otyp == LEASH && obj->leashmon));
3839 }
3840
3841 /* the '(' command */
3842 int
doprtool()3843 doprtool()
3844 {
3845 struct obj *otmp;
3846 int ct = 0;
3847 char lets[52 + 1];
3848
3849 for (otmp = invent; otmp; otmp = otmp->nobj)
3850 if (tool_in_use(otmp))
3851 lets[ct++] = obj_to_let(otmp);
3852 lets[ct] = '\0';
3853 if (!ct)
3854 You("are not using any tools.");
3855 else
3856 (void) display_inventory(lets, FALSE);
3857 return 0;
3858 }
3859
3860 /* '*' command; combines the ')' + '[' + '=' + '"' + '(' commands;
3861 show inventory of all currently wielded, worn, or used objects */
3862 int
doprinuse()3863 doprinuse()
3864 {
3865 struct obj *otmp;
3866 int ct = 0;
3867 char lets[52 + 1];
3868
3869 for (otmp = invent; otmp; otmp = otmp->nobj)
3870 if (is_worn(otmp) || tool_in_use(otmp))
3871 lets[ct++] = obj_to_let(otmp);
3872 lets[ct] = '\0';
3873 if (!ct)
3874 You("are not wearing or wielding anything.");
3875 else
3876 (void) display_inventory(lets, FALSE);
3877 return 0;
3878 }
3879
3880 /*
3881 * uses up an object that's on the floor, charging for it as necessary
3882 */
3883 void
useupf(obj,numused)3884 useupf(obj, numused)
3885 register struct obj *obj;
3886 long numused;
3887 {
3888 register struct obj *otmp;
3889 boolean at_u = (obj->ox == u.ux && obj->oy == u.uy);
3890
3891 /* burn_floor_objects() keeps an object pointer that it tries to
3892 * useupf() multiple times, so obj must survive if plural */
3893 if (obj->quan > numused)
3894 otmp = splitobj(obj, numused);
3895 else
3896 otmp = obj;
3897 if (costly_spot(otmp->ox, otmp->oy)) {
3898 if (index(u.urooms, *in_rooms(otmp->ox, otmp->oy, 0)))
3899 addtobill(otmp, FALSE, FALSE, FALSE);
3900 else
3901 (void) stolen_value(otmp, otmp->ox, otmp->oy, FALSE, FALSE);
3902 }
3903 delobj(otmp);
3904 if (at_u && u.uundetected && hides_under(youmonst.data))
3905 (void) hideunder(&youmonst);
3906 }
3907
3908 /*
3909 * Conversion from a class to a string for printing.
3910 * This must match the object class order.
3911 */
3912 STATIC_VAR NEARDATA const char *names[] = {
3913 0, "Illegal objects", "Weapons", "Armor", "Rings", "Amulets", "Tools",
3914 "Comestibles", "Potions", "Scrolls", "Spellbooks", "Wands", "Coins",
3915 "Gems/Stones", "Boulders/Statues", "Iron balls", "Chains", "Venoms"
3916 };
3917 STATIC_VAR NEARDATA const char oth_symbols[] = { CONTAINED_SYM, '\0' };
3918 STATIC_VAR NEARDATA const char *oth_names[] = { "Bagged/Boxed items" };
3919
3920 STATIC_VAR NEARDATA char *invbuf = (char *) 0;
3921 STATIC_VAR NEARDATA unsigned invbufsiz = 0;
3922
3923 char *
let_to_name(let,unpaid,showsym)3924 let_to_name(let, unpaid, showsym)
3925 char let;
3926 boolean unpaid, showsym;
3927 {
3928 const char *ocsymfmt = " ('%c')";
3929 const int invbuf_sympadding = 8; /* arbitrary */
3930 const char *class_name;
3931 const char *pos;
3932 int oclass = (let >= 1 && let < MAXOCLASSES) ? let : 0;
3933 unsigned len;
3934
3935 if (oclass)
3936 class_name = names[oclass];
3937 else if ((pos = index(oth_symbols, let)) != 0)
3938 class_name = oth_names[pos - oth_symbols];
3939 else
3940 class_name = names[0];
3941
3942 len = strlen(class_name) + (unpaid ? sizeof "unpaid_" : sizeof "")
3943 + (oclass ? (strlen(ocsymfmt) + invbuf_sympadding) : 0);
3944 if (len > invbufsiz) {
3945 if (invbuf)
3946 free((genericptr_t) invbuf);
3947 invbufsiz = len + 10; /* add slop to reduce incremental realloc */
3948 invbuf = (char *) alloc(invbufsiz);
3949 }
3950 if (unpaid)
3951 Strcat(strcpy(invbuf, "Unpaid "), class_name);
3952 else
3953 Strcpy(invbuf, class_name);
3954 if ((oclass != 0) && showsym) {
3955 char *bp = eos(invbuf);
3956 int mlen = invbuf_sympadding - strlen(class_name);
3957 while (--mlen > 0) {
3958 *bp = ' ';
3959 bp++;
3960 }
3961 *bp = '\0';
3962 Sprintf(eos(invbuf), ocsymfmt, def_oc_syms[oclass].sym);
3963 }
3964 return invbuf;
3965 }
3966
3967 /* release the static buffer used by let_to_name() */
3968 void
free_invbuf()3969 free_invbuf()
3970 {
3971 if (invbuf)
3972 free((genericptr_t) invbuf), invbuf = (char *) 0;
3973 invbufsiz = 0;
3974 }
3975
3976 /* give consecutive letters to every item in inventory (for !fixinv mode);
3977 gold is always forced to '$' slot at head of list */
3978 void
reassign()3979 reassign()
3980 {
3981 int i;
3982 struct obj *obj, *prevobj, *goldobj;
3983
3984 /* first, remove [first instance of] gold from invent, if present */
3985 prevobj = goldobj = 0;
3986 for (obj = invent; obj; prevobj = obj, obj = obj->nobj)
3987 if (obj->oclass == COIN_CLASS) {
3988 goldobj = obj;
3989 if (prevobj)
3990 prevobj->nobj = goldobj->nobj;
3991 else
3992 invent = goldobj->nobj;
3993 break;
3994 }
3995 /* second, re-letter the rest of the list */
3996 for (obj = invent, i = 0; obj; obj = obj->nobj, i++)
3997 obj->invlet =
3998 (i < 26) ? ('a' + i) : (i < 52) ? ('A' + i - 26) : NOINVSYM;
3999 /* third, assign gold the "letter" '$' and re-insert it at head */
4000 if (goldobj) {
4001 goldobj->invlet = GOLD_SYM;
4002 goldobj->nobj = invent;
4003 invent = goldobj;
4004 }
4005 if (i >= 52)
4006 i = 52 - 1;
4007 lastinvnr = i;
4008 }
4009
4010 /* #adjust command
4011 *
4012 * User specifies a 'from' slot for inventory stack to move,
4013 * then a 'to' slot for its destination. Open slots and those
4014 * filled by compatible stacks are listed as likely candidates
4015 * but user can pick any inventory letter (including 'from').
4016 *
4017 * to == from, 'from' has a name
4018 * All compatible items (same name or no name) are gathered
4019 * into the 'from' stack. No count is allowed.
4020 * to == from, 'from' does not have a name
4021 * All compatible items without a name are gathered into the
4022 * 'from' stack. No count is allowed. Compatible stacks with
4023 * names are left as-is.
4024 * to != from, no count
4025 * Move 'from' to 'to'. If 'to' is not empty, merge 'from'
4026 * into it if possible, otherwise swap it with the 'from' slot.
4027 * to != from, count given
4028 * If the user specifies a count when choosing the 'from' slot,
4029 * and that count is less than the full size of the stack,
4030 * then the stack will be split. The 'count' portion is moved
4031 * to the destination, and the only candidate for merging with
4032 * it is the stack already at the 'to' slot, if any. When the
4033 * destination is non-empty but won't merge, whatever is there
4034 * will be moved to an open slot; if there isn't any open slot
4035 * available, the adjustment attempt fails.
4036 *
4037 * To minimize merging for 'from == to', unnamed stacks will
4038 * merge with named 'from' but named ones won't merge with
4039 * unnamed 'from'. Otherwise attempting to collect all unnamed
4040 * stacks would lump the first compatible named stack with them
4041 * and give them its name.
4042 *
4043 * To maximize merging for 'from != to', compatible stacks will
4044 * merge when either lacks a name (or they already have the same
4045 * name). When no count is given and one stack has a name and
4046 * the other doesn't, the merged result will have that name.
4047 * However, when splitting results in a merger, the name of the
4048 * destination overrides that of the source, even if destination
4049 * is unnamed and source is named.
4050 */
4051 int
doorganize()4052 doorganize() /* inventory organizer by Del Lamb */
4053 {
4054 struct obj *obj, *otmp, *splitting, *bumped;
4055 int ix, cur, trycnt, goldstacks;
4056 char let;
4057 #define GOLD_INDX 0
4058 #define GOLD_OFFSET 1
4059 #define OVRFLW_INDX (GOLD_OFFSET + 52) /* past gold and 2*26 letters */
4060 char lets[1 + 52 + 1 + 1]; /* room for '$a-zA-Z#\0' */
4061 char qbuf[QBUFSZ];
4062 char allowall[4]; /* { ALLOW_COUNT, ALL_CLASSES, 0, 0 } */
4063 char *objname, *otmpname;
4064 const char *adj_type;
4065 boolean ever_mind = FALSE, collect;
4066
4067 /* when no invent, or just gold in '$' slot, there's nothing to adjust */
4068 if (!invent || (invent->oclass == COIN_CLASS
4069 && invent->invlet == GOLD_SYM && !invent->nobj)) {
4070 You("aren't carrying anything %s.",
4071 !invent ? "to adjust" : "adjustable");
4072 return 0;
4073 }
4074
4075 if (!flags.invlet_constant)
4076 reassign();
4077 /* get object the user wants to organize (the 'from' slot) */
4078 allowall[0] = ALLOW_COUNT;
4079 allowall[1] = ALL_CLASSES;
4080 allowall[2] = '\0';
4081 for (goldstacks = 0, otmp = invent; otmp; otmp = otmp->nobj) {
4082 /* gold should never end up in a letter slot, nor should two '$'
4083 slots occur, but if they ever do, allow #adjust to handle them
4084 (in the past, things like this have happened, usually due to
4085 bknown being erroneously set on one stack, clear on another;
4086 object merger isn't fooled by that anymore) */
4087 if (otmp->oclass == COIN_CLASS
4088 && (otmp->invlet != GOLD_SYM || ++goldstacks > 1)) {
4089 allowall[1] = COIN_CLASS;
4090 allowall[2] = ALL_CLASSES;
4091 allowall[3] = '\0';
4092 break;
4093 }
4094 }
4095 if (!(obj = getobj(allowall, "adjust")))
4096 return 0;
4097
4098 /* figure out whether user gave a split count to getobj() */
4099 splitting = bumped = 0;
4100 for (otmp = invent; otmp; otmp = otmp->nobj)
4101 if (otmp->nobj == obj) { /* knowledge of splitobj() operation */
4102 if (otmp->invlet == obj->invlet)
4103 splitting = otmp;
4104 break;
4105 }
4106
4107 /* initialize the list with all lower and upper case letters */
4108 lets[GOLD_INDX] = (obj->oclass == COIN_CLASS) ? GOLD_SYM : ' ';
4109 for (ix = GOLD_OFFSET, let = 'a'; let <= 'z';)
4110 lets[ix++] = let++;
4111 for (let = 'A'; let <= 'Z';)
4112 lets[ix++] = let++;
4113 lets[OVRFLW_INDX] = ' ';
4114 lets[sizeof lets - 1] = '\0';
4115 /* for floating inv letters, truncate list after the first open slot */
4116 if (!flags.invlet_constant && (ix = inv_cnt(FALSE)) < 52)
4117 lets[ix + (splitting ? 0 : 1)] = '\0';
4118
4119 /* blank out all the letters currently in use in the inventory
4120 except those that will be merged with the selected object */
4121 for (otmp = invent; otmp; otmp = otmp->nobj)
4122 if (otmp != obj && !mergable(otmp, obj)) {
4123 let = otmp->invlet;
4124 if (let >= 'a' && let <= 'z')
4125 lets[GOLD_OFFSET + let - 'a'] = ' ';
4126 else if (let >= 'A' && let <= 'Z')
4127 lets[GOLD_OFFSET + let - 'A' + 26] = ' ';
4128 /* overflow defaults to off, but it we find a stack using that
4129 slot, switch to on -- the opposite of normal invlet handling */
4130 else if (let == NOINVSYM)
4131 lets[OVRFLW_INDX] = NOINVSYM;
4132 }
4133
4134 /* compact the list by removing all the blanks */
4135 for (ix = cur = 0; lets[ix]; ix++)
4136 if (lets[ix] != ' ' && cur++ < ix)
4137 lets[cur - 1] = lets[ix];
4138 lets[cur] = '\0';
4139 /* and by dashing runs of letters */
4140 if (cur > 5)
4141 compactify(lets);
4142
4143 /* get 'to' slot to use as destination */
4144 Sprintf(qbuf, "Adjust letter to what [%s]%s?", lets,
4145 invent ? " (? see used letters)" : "");
4146 for (trycnt = 1; ; ++trycnt) {
4147 let = yn_function(qbuf, (char *) 0, '\0');
4148 if (let == '?' || let == '*') {
4149 let = display_used_invlets(splitting ? obj->invlet : 0);
4150 if (!let)
4151 continue;
4152 if (let == '\033')
4153 goto noadjust;
4154 }
4155 if (index(quitchars, let)
4156 /* adjusting to same slot is meaningful since all
4157 compatible stacks get collected along the way,
4158 but splitting to same slot is not */
4159 || (splitting && let == obj->invlet)) {
4160 noadjust:
4161 if (splitting)
4162 (void) merged(&splitting, &obj);
4163 if (!ever_mind)
4164 pline1(Never_mind);
4165 return 0;
4166 } else if (let == GOLD_SYM && obj->oclass != COIN_CLASS) {
4167 pline("Only gold coins may be moved into the '%c' slot.",
4168 GOLD_SYM);
4169 ever_mind = TRUE;
4170 goto noadjust;
4171 }
4172 /* letter() classifies '@' as one; compactify() can put '-' in lets;
4173 the only thing of interest that index() might find is '$' or '#'
4174 since letter() catches everything else that we put into lets[] */
4175 if ((letter(let) && let != '@') || (index(lets, let) && let != '-'))
4176 break; /* got one */
4177 if (trycnt == 5)
4178 goto noadjust;
4179 pline("Select an inventory slot letter."); /* else try again */
4180 }
4181
4182 collect = (let == obj->invlet);
4183 /* change the inventory and print the resulting item */
4184 adj_type = collect ? "Collecting" : !splitting ? "Moving:" : "Splitting:";
4185
4186 /*
4187 * don't use freeinv/addinv to avoid double-touching artifacts,
4188 * dousing lamps, losing luck, cursing loadstone, etc.
4189 */
4190 extract_nobj(obj, &invent);
4191
4192 for (otmp = invent; otmp;) {
4193 /* it's tempting to pull this outside the loop, but merged() could
4194 free ONAME(obj) [via obfree()] and replace it with ONAME(otmp) */
4195 objname = has_oname(obj) ? ONAME(obj) : (char *) 0;
4196
4197 if (collect) {
4198 /* Collecting: #adjust an inventory stack into its same slot;
4199 keep it there and merge other compatible stacks into it.
4200 Traditional inventory behavior is to merge unnamed stacks
4201 with compatible named ones; we only want that if it is
4202 the 'from' stack (obj) with a name and candidate (otmp)
4203 without one, not unnamed 'from' with named candidate. */
4204 otmpname = has_oname(otmp) ? ONAME(otmp) : (char *) 0;
4205 if ((!otmpname || (objname && !strcmp(objname, otmpname)))
4206 && merged(&otmp, &obj)) {
4207 adj_type = "Merging:";
4208 obj = otmp;
4209 otmp = otmp->nobj;
4210 extract_nobj(obj, &invent);
4211 continue; /* otmp has already been updated */
4212 }
4213 } else if (otmp->invlet == let) {
4214 /* Moving or splitting: don't merge extra compatible stacks.
4215 Found 'otmp' in destination slot; merge if compatible,
4216 otherwise bump whatever is there to an open slot. */
4217 if (!splitting) {
4218 adj_type = "Swapping:";
4219 otmp->invlet = obj->invlet;
4220 } else {
4221 /* strip 'from' name if it has one */
4222 if (objname && !obj->oartifact)
4223 ONAME(obj) = (char *) 0;
4224 if (!mergable(otmp, obj)) {
4225 /* won't merge; put 'from' name back */
4226 if (objname)
4227 ONAME(obj) = objname;
4228 } else {
4229 /* will merge; discard 'from' name */
4230 if (objname)
4231 free((genericptr_t) objname), objname = 0;
4232 }
4233
4234 if (merged(&otmp, &obj)) {
4235 adj_type = "Splitting and merging:";
4236 obj = otmp;
4237 extract_nobj(obj, &invent);
4238 } else if (inv_cnt(FALSE) >= 52) {
4239 (void) merged(&splitting, &obj); /* undo split */
4240 /* "knapsack cannot accommodate any more items" */
4241 Your("pack is too full.");
4242 return 0;
4243 } else {
4244 bumped = otmp;
4245 extract_nobj(bumped, &invent);
4246 }
4247 } /* moving vs splitting */
4248 break; /* not collecting and found 'to' slot */
4249 } /* collect */
4250 otmp = otmp->nobj;
4251 }
4252
4253 /* inline addinv; insert loose object at beginning of inventory */
4254 obj->invlet = let;
4255 obj->nobj = invent;
4256 obj->where = OBJ_INVENT;
4257 invent = obj;
4258 reorder_invent();
4259 if (bumped) {
4260 /* splitting the 'from' stack is causing an incompatible
4261 stack in the 'to' slot to be moved into an open one;
4262 we need to do another inline insertion to inventory */
4263 assigninvlet(bumped);
4264 bumped->nobj = invent;
4265 bumped->where = OBJ_INVENT;
4266 invent = bumped;
4267 reorder_invent();
4268 }
4269
4270 /* messages deferred until inventory has been fully reestablished */
4271 prinv(adj_type, obj, 0L);
4272 if (bumped)
4273 prinv("Moving:", bumped, 0L);
4274 if (splitting)
4275 clear_splitobjs(); /* reset splitobj context */
4276 update_inventory();
4277 return 0;
4278 }
4279
4280 /* common to display_minventory and display_cinventory */
4281 STATIC_OVL void
invdisp_nothing(hdr,txt)4282 invdisp_nothing(hdr, txt)
4283 const char *hdr, *txt;
4284 {
4285 winid win;
4286 anything any;
4287 menu_item *selected;
4288
4289 any = zeroany;
4290 win = create_nhwindow(NHW_MENU);
4291 start_menu(win);
4292 add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings, hdr,
4293 MENU_UNSELECTED);
4294 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
4295 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, txt, MENU_UNSELECTED);
4296 end_menu(win, (char *) 0);
4297 if (select_menu(win, PICK_NONE, &selected) > 0)
4298 free((genericptr_t) selected);
4299 destroy_nhwindow(win);
4300 return;
4301 }
4302
4303 /* query_objlist callback: return things that are worn or wielded */
4304 STATIC_OVL boolean
worn_wield_only(obj)4305 worn_wield_only(obj)
4306 struct obj *obj;
4307 {
4308 #if 1
4309 /* check for things that *are* worn or wielded (only used for monsters,
4310 so we don't worry about excluding W_CHAIN, W_ARTI and the like) */
4311 return (boolean) (obj->owornmask != 0L);
4312 #else
4313 /* this used to check for things that *might* be worn or wielded,
4314 but that's not particularly interesting */
4315 if (is_weptool(obj) || is_wet_towel(obj) || obj->otyp == MEAT_RING)
4316 return TRUE;
4317 return (boolean) (obj->oclass == WEAPON_CLASS
4318 || obj->oclass == ARMOR_CLASS
4319 || obj->oclass == AMULET_CLASS
4320 || obj->oclass == RING_CLASS);
4321 #endif
4322 }
4323
4324 /*
4325 * Display a monster's inventory.
4326 * Returns a pointer to the object from the monster's inventory selected
4327 * or NULL if nothing was selected.
4328 *
4329 * By default, only worn and wielded items are displayed. The caller
4330 * can pick one. Modifier flags are:
4331 *
4332 * PICK_NONE, PICK_ONE - standard menu control
4333 * PICK_ANY - allowed, but we only return a single object
4334 * MINV_NOLET - nothing selectable
4335 * MINV_ALL - display all inventory
4336 */
4337 struct obj *
display_minventory(mon,dflags,title)4338 display_minventory(mon, dflags, title)
4339 register struct monst *mon;
4340 int dflags;
4341 char *title;
4342 {
4343 struct obj *ret;
4344 char tmp[QBUFSZ];
4345 int n;
4346 menu_item *selected = 0;
4347 int do_all = (dflags & MINV_ALL) != 0,
4348 incl_hero = (do_all && u.uswallow && mon == u.ustuck),
4349 have_inv = (mon->minvent != 0), have_any = (have_inv || incl_hero),
4350 pickings = (dflags & MINV_PICKMASK);
4351
4352 Sprintf(tmp, "%s %s:", s_suffix(noit_Monnam(mon)),
4353 do_all ? "possessions" : "armament");
4354
4355 if (do_all ? have_any : (mon->misc_worn_check || MON_WEP(mon))) {
4356 /* Fool the 'weapon in hand' routine into
4357 * displaying 'weapon in claw', etc. properly.
4358 */
4359 youmonst.data = mon->data;
4360 /* in case inside a shop, don't append "for sale" prices */
4361 iflags.suppress_price++;
4362
4363 n = query_objlist(title ? title : tmp, &(mon->minvent),
4364 (INVORDER_SORT | (incl_hero ? INCLUDE_HERO : 0)),
4365 &selected, pickings,
4366 do_all ? allow_all : worn_wield_only);
4367
4368 iflags.suppress_price--;
4369 /* was 'set_uasmon();' but that potentially has side-effects */
4370 youmonst.data = &mons[u.umonnum]; /* most basic part of set_uasmon */
4371 } else {
4372 invdisp_nothing(title ? title : tmp, "(none)");
4373 n = 0;
4374 }
4375
4376 if (n > 0) {
4377 ret = selected[0].item.a_obj;
4378 free((genericptr_t) selected);
4379 } else
4380 ret = (struct obj *) 0;
4381 return ret;
4382 }
4383
4384 /*
4385 * Display the contents of a container in inventory style.
4386 * Currently, this is only used for statues, via wand of probing.
4387 */
4388 struct obj *
display_cinventory(obj)4389 display_cinventory(obj)
4390 register struct obj *obj;
4391 {
4392 struct obj *ret;
4393 char qbuf[QBUFSZ];
4394 int n;
4395 menu_item *selected = 0;
4396
4397 (void) safe_qbuf(qbuf, "Contents of ", ":", obj, doname, ansimpleoname,
4398 "that");
4399
4400 if (obj->cobj) {
4401 n = query_objlist(qbuf, &(obj->cobj), INVORDER_SORT,
4402 &selected, PICK_NONE, allow_all);
4403 } else {
4404 invdisp_nothing(qbuf, "(empty)");
4405 n = 0;
4406 }
4407 if (n > 0) {
4408 ret = selected[0].item.a_obj;
4409 free((genericptr_t) selected);
4410 } else
4411 ret = (struct obj *) 0;
4412 obj->cknown = 1;
4413 return ret;
4414 }
4415
4416 /* query objlist callback: return TRUE if obj is at given location */
4417 static coord only;
4418
4419 STATIC_OVL boolean
only_here(obj)4420 only_here(obj)
4421 struct obj *obj;
4422 {
4423 return (obj->ox == only.x && obj->oy == only.y);
4424 }
4425
4426 /*
4427 * Display a list of buried items in inventory style. Return a non-zero
4428 * value if there were items at that spot.
4429 *
4430 * Currently, this is only used with a wand of probing zapped downwards.
4431 */
4432 int
display_binventory(x,y,as_if_seen)4433 display_binventory(x, y, as_if_seen)
4434 int x, y;
4435 boolean as_if_seen;
4436 {
4437 struct obj *obj;
4438 menu_item *selected = 0;
4439 int n;
4440
4441 /* count # of objects here */
4442 for (n = 0, obj = level.buriedobjlist; obj; obj = obj->nobj)
4443 if (obj->ox == x && obj->oy == y) {
4444 if (as_if_seen)
4445 obj->dknown = 1;
4446 n++;
4447 }
4448
4449 if (n) {
4450 only.x = x;
4451 only.y = y;
4452 if (query_objlist("Things that are buried here:",
4453 &level.buriedobjlist, INVORDER_SORT,
4454 &selected, PICK_NONE, only_here) > 0)
4455 free((genericptr_t) selected);
4456 only.x = only.y = 0;
4457 }
4458 return n;
4459 }
4460
4461 /*invent.c*/
4462