make rank() static again
[NetHack.git] / src / invent.c
blobaa1e0de675ea181e53dc2c4d5cee86fdef285777
1 /* NetHack 3.7 invent.c $NHDT-Date: 1737384766 2025/01/20 06:52:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.531 $ */
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. */
6 #include "hack.h"
8 /* fake inventory letters, not 'a'..'z' or 'A'..'Z' */
9 #define NOINVSYM '#' /* overflow because all 52 letters are in use */
10 #define CONTAINED_SYM '>' /* designator for inside a container */
11 #define HANDS_SYM '-' /* hands|fingers|self depending on context */
13 staticfn void inuse_classify(Loot *, struct obj *);
14 staticfn char *loot_xname(struct obj *);
15 staticfn int invletter_value(char);
16 staticfn int QSORTCALLBACK sortloot_cmp(const genericptr, const genericptr);
17 staticfn void reorder_invent(void);
18 staticfn struct obj *addinv_core0(struct obj *, struct obj *,
19 boolean) NONNULLARG1;
20 staticfn void noarmor(boolean);
21 staticfn void invdisp_nothing(const char *, const char *);
22 staticfn boolean worn_wield_only(struct obj *);
23 staticfn char *cinv_doname(struct obj *);
24 staticfn char *cinv_ansimpleoname(struct obj *);
25 staticfn boolean only_here(struct obj *);
26 staticfn void compactify(char *);
27 staticfn boolean taking_off(const char *);
28 staticfn void mime_action(const char *);
29 staticfn char *getobj_hands_txt(const char *, char *);
30 staticfn int ckvalidcat(struct obj *);
31 staticfn int ckunpaid(struct obj *);
32 staticfn char *safeq_xprname(struct obj *);
33 staticfn char *safeq_shortxprname(struct obj *);
34 staticfn char display_pickinv(const char *, const char *, const char *,
35 boolean, boolean, long *);
36 staticfn char display_used_invlets(char);
37 staticfn boolean this_type_only(struct obj *);
38 staticfn void dounpaid(int, int, int);
39 staticfn struct obj *find_unpaid(struct obj *, struct obj **);
40 staticfn void menu_identify(int);
41 staticfn boolean tool_being_used(struct obj *);
42 staticfn int adjust_ok(struct obj *);
43 staticfn int adjust_gold_ok(struct obj *);
44 staticfn int doorganize_core(struct obj *);
45 staticfn char obj_to_let(struct obj *);
46 staticfn boolean item_naming_classification(struct obj *, char *, char *);
47 staticfn int item_reading_classification(struct obj *, char *);
48 staticfn void ia_addmenu(winid, int, char, const char *);
49 staticfn void itemactions_pushkeys(struct obj *, int);
50 staticfn int itemactions(struct obj *);
51 staticfn int dispinv_with_action(char *, boolean, const char *);
53 /* enum and structs are defined in wintype.h */
54 static win_request_info wri_info;
55 static int perminv_flags = InvOptNone;
56 static boolean in_perm_invent_toggled;
58 /* wizards can wish for venom, which will become an invisible inventory
59 * item without this. putting it in inv_order would mean venom would
60 * suddenly become a choice for all the inventory-class commands, which
61 * would probably cause mass confusion. the test for inventory venom
62 * is only WIZARD and not wizard because the wizard can leave venom lying
63 * around on a bones level for normal players to find. [Note to the
64 * confused: 'WIZARD' used to be a compile-time conditional so this was
65 * guarded by #ifdef WIZARD/.../#endif.]
67 static const char venom_inv[] = { VENOM_CLASS, 0 }; /* (constant) */
69 /* menu heading lines used instead of object classes when sorting by in-use;
70 pointers aren't const because dispinv_with_action() might temporarily
71 change "Accessories" to "Rings" or "Amulet", then back again */
72 static const char *inuse_headers[] = { /* [4] shown first, [1] last */
73 "", "Miscellaneous", "Worn Armor",
74 "Wielded/Readied Weapons", "Accessories",
77 /* sortloot() classification for in-use sort;
78 called at most once [per sort] for each object */
79 staticfn void
80 inuse_classify(Loot *sort_item, struct obj *obj)
82 long w_mask = (obj->owornmask & (W_ACCESSORY | W_WEAPONS | W_ARMOR));
83 int rating = 0, altclass = 0;
85 #define USE_RATING(test) \
86 do { \
87 /* 'rating' advances for each USE_RATING() call */ \
88 ++rating; \
89 if ((test) != 0) \
90 goto assign_rating; \
91 } while (0)
94 * In order of importance, least to most, somewhat arbitrarily.
96 * For instance, all accessories are grouped together even
97 * though they're usually less important than other stuff, so
98 * that they appear earlier within displayed list of used items.
99 * Amulet is rated as most important primarily because the
100 * default 'packorder' puts amulets first (possibly because one
101 * might be The Amulet). Non-wielded alternate weapon and
102 * quiver are grouped with primary weapon. Weapons are rated
103 * above armor because of default 'packorder'.
105 * These ratings don't match either subclasses or 'packorder'.
107 * USE_RATING() sets up 'rating' then jumps to 'assign_rating'
108 * if 'obj' warrants that.
110 /* "Miscellaneous" */
111 ++altclass; /* 1 */
112 /* lamp and leash might be used doubly, as a tool and also wielded
113 or readied-in-quiver; these tests for used-as-tool only pass
114 when owornmask is 0 so that used-as-weapon takes precedence */
115 USE_RATING(!w_mask && obj->otyp == LEASH && obj->leashmon);
116 USE_RATING(!w_mask && obj->oclass == TOOL_CLASS && obj->lamplit);
117 /* "Armor" */
118 ++altclass; /* 2 */
119 USE_RATING(w_mask & WORN_SHIRT);
120 USE_RATING(w_mask & WORN_BOOTS);
121 USE_RATING(w_mask & WORN_GLOVES);
122 USE_RATING(w_mask & WORN_HELMET);
123 USE_RATING(w_mask & WORN_SHIELD);
124 USE_RATING(w_mask & WORN_CLOAK);
125 USE_RATING(w_mask & WORN_ARMOR);
126 /* "Weapons" */
127 ++altclass; /* 3 */
128 /* could get more complicated: if uswapwep is just alternate weapon
129 rather than wielded secondary, swap order with quiver (unless
130 quiver is ammo for uswapwep without also being ammo for uwep) */
131 USE_RATING(w_mask & W_QUIVER);
132 USE_RATING(w_mask & W_SWAPWEP);
133 USE_RATING(w_mask & W_WEP);
134 /* "Accessories" */
135 ++altclass; /* 4 */
136 USE_RATING(w_mask & WORN_BLINDF);
137 USE_RATING(w_mask & (ULEFTY ? RIGHT_RING : LEFT_RING)); /* off hand */
138 USE_RATING(w_mask & (URIGHTY ? RIGHT_RING : LEFT_RING)); /* main hand */
139 USE_RATING(w_mask & WORN_AMUL);
141 /* if we get here, the USE_RATING() checks failed to find a match */
142 rating = 0;
143 altclass = -1; /* 'orderclass' must end up non-zero */
145 assign_rating:
146 sort_item->inuse = rating;
147 sort_item->orderclass = altclass; /* used for alternate headings */
149 /* not applicable for in-use */
150 sort_item->subclass = 0;
151 sort_item->disco = 0;
153 #undef USE_RATING
156 /* sortloot() classification; called at most once [per sort] for each object;
157 also called by '\' command if discoveries use sortloot order */
158 void
159 loot_classify(Loot *sort_item, struct obj *obj)
161 /* we may eventually make this a settable option to always use
162 with sortloot instead of only when the 'sortpack' option isn't
163 set; it is similar to sortpack's inv_order but items most
164 likely to be picked up are moved to the front */
165 static char def_srt_order[MAXOCLASSES] = {
166 COIN_CLASS, AMULET_CLASS, RING_CLASS, WAND_CLASS, POTION_CLASS,
167 SCROLL_CLASS, SPBOOK_CLASS, GEM_CLASS, FOOD_CLASS, TOOL_CLASS,
168 WEAPON_CLASS, ARMOR_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0,
170 static char armcat[8];
171 const char *classorder;
172 char *p;
173 int k, otyp = obj->otyp, oclass = obj->oclass;
174 boolean seen, discovered = objects[otyp].oc_name_known ? TRUE : FALSE;
177 * For the value types assigned by this classification, sortloot()
178 * will put lower valued ones before higher valued ones.
180 if (!Blind)
181 obj->dknown = 1; /* xname(obj) does this; we want it sooner */
182 seen = obj->dknown ? TRUE : FALSE,
183 /* class order */
184 classorder = flags.sortpack ? flags.inv_order : def_srt_order;
185 p = strchr(classorder, oclass);
186 if (p)
187 k = 1 + (int) (p - classorder);
188 else
189 k = 1 + (int) strlen(classorder) + (oclass != VENOM_CLASS);
190 sort_item->orderclass = k;
191 /* subclass designation; only a few classes have subclasses
192 and the non-armor ones we use are fairly arbitrary */
193 switch (oclass) {
194 case ARMOR_CLASS:
195 if (!armcat[7]) {
196 /* one-time init; we use a different order than the subclass
197 values defined by objclass.h */
198 armcat[ARM_HELM] = 1; /* [2] */
199 armcat[ARM_GLOVES] = 2; /* [3] */
200 armcat[ARM_BOOTS] = 3; /* [4] */
201 armcat[ARM_SHIELD] = 4; /* [1] */
202 armcat[ARM_CLOAK] = 5; /* [5] */
203 armcat[ARM_SHIRT] = 6; /* [6] */
204 armcat[ARM_SUIT] = 7; /* [0] */
205 armcat[7] = 8; /* sanity protection */
207 k = objects[otyp].oc_armcat;
208 /* oc_armcat overloads oc_subtyp which is an 'schar' so guard
209 against somebody assigning something unexpected to it */
210 if (k < 0 || k >= 7)
211 k = 7;
212 k = armcat[k];
213 break;
214 case WEAPON_CLASS:
215 /* for weapons, group by ammo (arrows, bolts), launcher (bows),
216 missile (darts, boomerangs), stackable (daggers, knives, spears),
217 'other' (swords, axes, &c), polearms */
218 k = objects[otyp].oc_skill;
219 k = (k < 0) ? ((k >= -P_CROSSBOW && k <= -P_BOW) ? 1 : 3)
220 : ((k >= P_BOW && k <= P_CROSSBOW) ? 2
221 : (k == P_SPEAR || k == P_DAGGER || k == P_KNIFE) ? 4
222 : !is_pole(obj) ? 5 : 6);
223 break;
224 case TOOL_CLASS:
225 if (seen && discovered
226 && (otyp == BAG_OF_TRICKS || otyp == HORN_OF_PLENTY))
227 k = 2; /* known pseudo-container */
228 else if (Is_container(obj))
229 k = 1; /* regular container or unknown bag of tricks */
230 else
231 switch (otyp) {
232 case WOODEN_FLUTE:
233 case MAGIC_FLUTE:
234 case TOOLED_HORN:
235 case FROST_HORN:
236 case FIRE_HORN:
237 case WOODEN_HARP:
238 case MAGIC_HARP:
239 case BUGLE:
240 case LEATHER_DRUM:
241 case DRUM_OF_EARTHQUAKE:
242 case HORN_OF_PLENTY: /* not a musical instrument */
243 k = 3; /* instrument or unknown horn of plenty */
244 break;
245 default:
246 k = 4; /* 'other' tool */
247 break;
249 break;
250 case FOOD_CLASS:
251 /* [what about separating "partly eaten" within each group?] */
252 switch (otyp) {
253 case SLIME_MOLD:
254 k = 1;
255 break;
256 default:
257 /* [maybe separate one-bite foods from rations and such?] */
258 k = obj->globby ? 6 : 2;
259 break;
260 case TIN:
261 k = 3;
262 break;
263 case EGG:
264 k = 4;
265 break;
266 case CORPSE:
267 k = 5;
268 break;
270 break;
271 case GEM_CLASS:
273 * Normally subclass takes priority over discovery status, but
274 * that would give away information for gems (assuming we'll
275 * group them as valuable gems, next glass, then gray stones,
276 * and finally rocks once they're all fully identified).
278 * Order:
279 * 1) unseen gems and glass ("gem")
280 * 2) seen but undiscovered gems and glass ("blue gem"),
281 * 3) discovered gems ("sapphire"),
282 * 4) discovered glass ("worthless pieced of blue glass"),
283 * 5) unseen gray stones and rocks ("stone"),
284 * 6) seen but undiscovered gray stones ("gray stone"),
285 * 7) discovered gray stones ("touchstone"),
286 * 8) seen rocks ("rock").
288 switch (objects[obj->otyp].oc_material) {
289 case GEMSTONE:
290 k = !seen ? 1 : !discovered ? 2 : 3;
291 break;
292 case GLASS:
293 k = !seen ? 1 : !discovered ? 2 : 4;
294 break;
295 default: /* MINERAL */
296 k = !seen ? 5 : (obj->otyp != ROCK) ? (!discovered ? 6 : 7) : 8;
297 break;
299 break;
300 default:
301 /* other classes don't have subclasses; we assign a nonzero
302 value because sortloot() uses 0 to mean 'not yet classified' */
303 k = 1; /* any non-zero would do */
304 break;
306 sort_item->subclass = k;
307 /* discovery status */
308 k = !seen ? 1 /* unseen */
309 : (discovered || !OBJ_DESCR(objects[otyp])) ? 4
310 : (objects[otyp].oc_uname) ? 3 /* named (partially discovered) */
311 : 2; /* undiscovered */
312 sort_item->disco = k;
313 /* not applicable */
314 sort_item->inuse = 0;
317 /* sortloot() formatting routine; for alphabetizing, not shown to user */
318 staticfn char *
319 loot_xname(struct obj *obj)
321 struct obj saveo;
322 boolean save_debug;
323 char *res, *save_oname;
326 * Deal with things that xname() includes as a prefix. We don't
327 * want such because they change alphabetical ordering. First,
328 * remember 'obj's current settings.
330 saveo.odiluted = obj->odiluted;
331 saveo.blessed = obj->blessed, saveo.cursed = obj->cursed;
332 saveo.spe = obj->spe;
333 saveo.owt = obj->owt;
334 save_oname = has_oname(obj) ? ONAME(obj) : 0;
335 save_debug = flags.debug;
336 /* suppress "diluted" for potions and "holy/unholy" for water;
337 sortloot() will deal with them using other criteria than name */
338 if (obj->oclass == POTION_CLASS) {
339 obj->odiluted = 0;
340 if (obj->otyp == POT_WATER)
341 obj->blessed = 0, obj->cursed = 0;
343 /* make "wet towel" and "moist towel" format as "towel" so that all
344 three group together */
345 if (obj->otyp == TOWEL)
346 obj->spe = 0;
347 /* group globs by monster type rather than by size: force all to
348 have the same size adjective hence same "small glob of " prefix */
349 if (obj->globby)
350 obj->owt = 20; /* weight of a fresh glob (one pudding's worth) */
351 /* suppress user-assigned name */
352 if (save_oname && !obj->oartifact)
353 ONAME(obj) = 0;
354 /* avoid wizard mode formatting variations */
355 if (wizard) { /* flags.debug */
356 /* paranoia: before toggling off wizard mode, guard against a
357 panic in xname() producing a normal mode panic save file */
358 program_state.something_worth_saving = 0;
359 flags.debug = FALSE;
362 res = cxname_singular(obj);
364 if (save_debug) {
365 flags.debug = TRUE;
366 program_state.something_worth_saving = 1;
368 /* restore the object */
369 if (obj->oclass == POTION_CLASS) {
370 obj->odiluted = saveo.odiluted;
371 if (obj->otyp == POT_WATER)
372 obj->blessed = saveo.blessed, obj->cursed = saveo.cursed;
374 if (obj->otyp == TOWEL) {
375 obj->spe = saveo.spe;
376 /* give "towel" a suffix that will force wet ones to come first,
377 moist ones next, and dry ones last regardless of whether
378 they've been flagged as having spe known */
379 Strcat(res, is_wet_towel(obj) ? ((obj->spe >= 3) ? "x" : "y") : "z");
381 if (obj->globby) {
382 obj->owt = saveo.owt;
383 /* we've suppressed the size prefix (above); there normally won't
384 be more than one of a given creature type because they coalesce,
385 but globs with different bless/curse state won't merge so it is
386 feasible to have multiple at the same location; add a suffix to
387 get such sorted by size (small first) */
388 Strcat(res, (obj->owt <= 100) ? "a"
389 : (obj->owt <= 300) ? "b"
390 : (obj->owt <= 500) ? "c"
391 : "d");
393 if (save_oname && !obj->oartifact)
394 ONAME(obj) = save_oname;
396 return res;
399 /* '$'==1, 'a'-'z'==2..27, 'A'-'Z'==28..53, '#'==54, catchall 55 */
400 staticfn int
401 invletter_value(char c)
403 return ('a' <= c && c <= 'z') ? (c - 'a' + 2)
404 : ('A' <= c && c <= 'Z') ? (c - 'A' + 2 + 26)
405 : (c == '$') ? 1
406 : (c == '#') ? 1 + invlet_basic + 1
407 : 1 + invlet_basic + 1 + 1; /* none of the above
408 * (shouldn't happen) */
411 /* qsort comparison routine for sortloot() */
412 staticfn int QSORTCALLBACK
413 sortloot_cmp(const genericptr vptr1, const genericptr vptr2)
415 struct sortloot_item *sli1 = (struct sortloot_item *) vptr1,
416 *sli2 = (struct sortloot_item *) vptr2;
417 struct obj *obj1 = sli1->obj,
418 *obj2 = sli2->obj;
419 char *nam1, *nam2, *tmpstr;
420 int val1, val2, namcmp;
422 /* in-use takes precedence over all others */
423 if ((gs.sortlootmode & SORTLOOT_INUSE) != 0) {
424 /* Classify each object at most once no matter how many
425 comparisons it is involved in. */
426 if (!sli1->orderclass)
427 inuse_classify(sli1, obj1);
428 if (!sli2->orderclass)
429 inuse_classify(sli2, obj2);
431 val1 = sli1->inuse;
432 val2 = sli2->inuse;
433 if (val1 != val2)
434 return val2 - val1; /* bigger value comes before smaller */
435 /* neither item in use (or both are lit lamps/candles or both are
436 attached leashes; items using owornmask don't produce ties) */
437 goto tiebreak;
440 /* order by object class unless we're doing by-invlet without sortpack */
441 if ((gs.sortlootmode & (SORTLOOT_PACK | SORTLOOT_INVLET))
442 != SORTLOOT_INVLET) {
443 /* Classify each object at most once no matter how many
444 comparisons it is involved in. */
445 if (!sli1->orderclass)
446 loot_classify(sli1, obj1);
447 if (!sli2->orderclass)
448 loot_classify(sli2, obj2);
450 /* Sort by class. */
451 val1 = sli1->orderclass;
452 val2 = sli2->orderclass;
453 if (val1 != val2)
454 return val1 - val2;
456 /* skip sub-classes when ordering by sortpack+invlet */
457 if ((gs.sortlootmode & SORTLOOT_INVLET) == 0) {
458 /* Class matches; sort by subclass. */
459 val1 = sli1->subclass;
460 val2 = sli2->subclass;
461 if (val1 != val2)
462 return val1 - val2;
464 /* Class and subclass match; sort by discovery status:
465 * first unseen, then seen but not named or discovered,
466 * then named, lastly discovered.
467 * 1) potion
468 * 2) pink potion
469 * 3) dark green potion called confusion
470 * 4) potion of healing
471 * Multiple entries within each group will be put into
472 * alphabetical order below.
474 val1 = sli1->disco;
475 val2 = sli2->disco;
476 if (val1 != val2)
477 return val1 - val2;
481 /* order by assigned inventory letter */
482 if ((gs.sortlootmode & SORTLOOT_INVLET) != 0) {
483 val1 = invletter_value(obj1->invlet);
484 val2 = invletter_value(obj2->invlet);
485 if (val1 != val2)
486 return val1 - val2;
489 if ((gs.sortlootmode & SORTLOOT_LOOT) == 0)
490 goto tiebreak;
493 * Sort object names in lexicographical order, ignoring quantity.
495 * Each obj gets formatted at most once (per sort) no matter how many
496 * comparisons it gets subjected to.
498 nam1 = sli1->str;
499 if (!nam1) {
500 tmpstr = loot_xname(obj1);
501 nam1 = sli1->str = dupstr(tmpstr);
502 maybereleaseobuf(tmpstr);
504 nam2 = sli2->str;
505 if (!nam2) {
506 tmpstr = loot_xname(obj2);
507 nam2 = sli2->str = dupstr(tmpstr);
508 maybereleaseobuf(tmpstr);
510 if ((namcmp = strcmpi(nam1, nam2)) != 0)
511 return namcmp;
513 /* Sort by BUCX. */
514 val1 = obj1->bknown ? (obj1->blessed ? 3 : !obj1->cursed ? 2 : 1) : 0;
515 val2 = obj2->bknown ? (obj2->blessed ? 3 : !obj2->cursed ? 2 : 1) : 0;
516 if (val1 != val2)
517 return val2 - val1; /* bigger is better */
519 /* Sort by greasing. This will put the objects in degreasing order. */
520 val1 = obj1->greased;
521 val2 = obj2->greased;
522 if (val1 != val2)
523 return val2 - val1; /* bigger is better */
525 /* Sort by erosion. The effective amount is what matters. */
526 val1 = greatest_erosion(obj1);
527 val2 = greatest_erosion(obj2);
528 if (val1 != val2)
529 return val1 - val2; /* bigger is WORSE */
531 /* Sort by erodeproofing. Map known-invulnerable to 1, and both
532 known-vulnerable and unknown-vulnerability to 0, because that's
533 how they're displayed. */
534 val1 = obj1->rknown && obj1->oerodeproof;
535 val2 = obj2->rknown && obj2->oerodeproof;
536 if (val1 != val2)
537 return val2 - val1; /* bigger is better */
539 /* Sort by enchantment. Map unknown to -1000, which is comfortably
540 below the range of obj->spe. oc_uses_known means that obj->known
541 matters, which usually indirectly means that obj->spe is relevant.
542 Lots of objects use obj->spe for some other purpose (see obj.h). */
543 if (objects[obj1->otyp].oc_uses_known
544 /* exclude eggs (laid by you) and tins (homemade, pureed, &c) */
545 && obj1->oclass != FOOD_CLASS) {
546 val1 = obj1->known ? obj1->spe : -1000;
547 val2 = obj2->known ? obj2->spe : -1000;
548 if (val1 != val2)
549 return val2 - val1; /* bigger is better */
552 tiebreak:
553 /* They're identical, as far as we're concerned. We want
554 to force a deterministic order, and do so by producing a
555 stable sort: maintain the original order of equal items. */
556 return (sli1->indx - sli2->indx);
560 * sortloot() - the story so far...
562 * The original implementation constructed and returned an array
563 * of pointers to objects in the requested order. Callers had to
564 * count the number of objects, allocate the array, pass one
565 * object at a time to the routine which populates it, traverse
566 * the objects via stepping through the array, then free the
567 * array. The ordering process used a basic insertion sort which
568 * is fine for short lists but inefficient for long ones.
570 * 3.6.0 (and continuing with 3.6.1) changed all that so that
571 * sortloot was self-contained as far as callers were concerned.
572 * It reordered the linked list into the requested order and then
573 * normal list traversal was used to process it. It also switched
574 * to qsort() on the assumption that the C library implementation
575 * put some effort into sorting efficiently. It also checked
576 * whether the list was already sorted as it got ready to do the
577 * sorting, so re-examining inventory or a pile of objects without
578 * having changed anything would gobble up less CPU than a full
579 * sort. But it had at least two problems (aside from the ordinary
580 * complement of bugs):
581 * 1) some players wanted to get the original order back when they
582 * changed the 'sortloot' option back to 'none', but the list
583 * reordering made that infeasible;
584 * 2) object identification giving the 'ID whole pack' result
585 * would call makeknown() on each newly ID'd object, that would
586 * call update_inventory() to update the persistent inventory
587 * window if one existed, the interface would call the inventory
588 * display routine which would call sortloot() which might change
589 * the order of the list being traversed by the identify code,
590 * possibly skipping the ID of some objects. That could have been
591 * avoided by suppressing 'perm_invent' during identification
592 * (fragile) or by avoiding sortloot() during inventory display
593 * (more robust).
595 * As of 3.6.2: revert to the temporary array of ordered obj pointers
596 * but have sortloot() do the counting and allocation. Callers
597 * need to use array traversal instead of linked list traversal
598 * and need to free the temporary array when done. And the
599 * array contains 'struct sortloot_item' (aka 'Loot') entries
600 * instead of simple 'struct obj *' entries.
602 Loot *
603 sortloot(
604 struct obj **olist, /* old version might have changed *olist, we don't */
605 unsigned mode, /* flags for sortloot_cmp() */
606 boolean by_nexthere, /* T: traverse via obj->nexthere, F: via obj->nobj */
607 boolean (*filterfunc)(struct obj *)) /* optional filter */
609 static Loot zerosli;
610 Loot *sliarray;
611 struct obj *o;
612 unsigned n, i;
613 boolean augment_filter;
615 for (n = 0, o = *olist; o; o = by_nexthere ? o->nexthere : o->nobj)
616 ++n;
617 /* note: if there is a filter function, this might overallocate */
618 sliarray = (Loot *) alloc((n + 1) * sizeof *sliarray);
620 /* the 'keep cockatrice corpses' flag is overloaded with sort mode */
621 augment_filter = (mode & SORTLOOT_PETRIFY) ? TRUE : FALSE;
622 mode &= ~SORTLOOT_PETRIFY; /* remove flag, leaving mode */
623 /* populate aliarray[0..n-1] */
624 for (i = 0, o = *olist; o; o = by_nexthere ? o->nexthere : o->nobj) {
625 if (filterfunc && !(*filterfunc)(o)
626 /* caller may be asking us to override filterfunc (in order
627 to do a cockatrice corpse touch check during pickup even
628 if/when the filter rejects food class) */
629 && (!augment_filter || o->otyp != CORPSE
630 || !touch_petrifies(&mons[o->corpsenm])))
631 continue;
632 sliarray[i] = zerosli;
633 sliarray[i].obj = o, sliarray[i].indx = (int) i;
634 ++i;
636 n = i;
637 /* add a terminator so that we don't have to pass 'n' back to caller */
638 sliarray[n] = zerosli;
639 sliarray[n].indx = -1;
641 /* do the sort; if no sorting is requested, we'll just return
642 a sortloot_item array reflecting the current ordering */
643 if (mode && n > 1) {
644 gs.sortlootmode = mode; /* extra input for sortloot_cmp() */
645 qsort((genericptr_t) sliarray, n, sizeof *sliarray, sortloot_cmp);
646 gs.sortlootmode = 0; /* reset static mode flags */
647 /* if sortloot_cmp formatted any objects, discard their strings now */
648 for (i = 0; i < n; ++i)
649 if (sliarray[i].str)
650 free((genericptr_t) sliarray[i].str), sliarray[i].str = 0;
652 return sliarray;
655 /* sortloot() callers should use this to free up memory it allocates */
656 void
657 unsortloot(Loot **loot_array_p)
659 if (*loot_array_p)
660 free((genericptr_t) *loot_array_p), *loot_array_p = (Loot *) 0;
663 #if 0 /* 3.6.0 'revamp' -- simpler than current, but ultimately too simple */
664 void
665 sortloot(
666 struct obj **olist,
667 unsigned mode, /* flags for sortloot_cmp() */
668 boolean by_nexthere) /* T: traverse via obj->nexthere, F: via obj->nobj */
670 struct sortloot_item *sliarray, osli, nsli;
671 struct obj *o, **nxt_p;
672 unsigned n, i;
673 boolean already_sorted = TRUE;
675 gs.sortlootmode = mode; /* extra input for sortloot_cmp() */
676 for (n = osli.indx = 0, osli.obj = *olist; (o = osli.obj) != 0;
677 osli = nsli) {
678 nsli.obj = by_nexthere ? o->nexthere : o->nobj;
679 nsli.indx = (int) ++n;
680 if (nsli.obj && already_sorted
681 && sortloot_cmp((genericptr_t) &osli, (genericptr_t) &nsli) > 0)
682 already_sorted = FALSE;
684 if (n > 1 && !already_sorted) {
685 sliarray = (struct sortloot_item *) alloc(n * sizeof *sliarray);
686 for (i = 0, o = *olist; o;
687 ++i, o = by_nexthere ? o->nexthere : o->nobj)
688 sliarray[i].obj = o, sliarray[i].indx = (int) i;
690 qsort((genericptr_t) sliarray, n, sizeof *sliarray, sortloot_cmp);
691 for (i = 0; i < n; ++i) {
692 o = sliarray[i].obj;
693 nxt_p = by_nexthere ? &(o->nexthere) : &(o->nobj);
694 *nxt_p = (i < n - 1) ? sliarray[i + 1].obj : (struct obj *) 0;
696 *olist = sliarray[0].obj;
697 free((genericptr_t) sliarray);
699 gs.sortlootmode = 0;
701 #endif /*0*/
703 void
704 assigninvlet(struct obj *otmp)
706 boolean inuse[invlet_basic];
707 int i;
708 struct obj *obj;
710 /* there should be at most one of these in inventory... */
711 if (otmp->oclass == COIN_CLASS) {
712 otmp->invlet = GOLD_SYM;
713 return;
716 for (i = 0; i < invlet_basic; i++)
717 inuse[i] = FALSE;
718 for (obj = gi.invent; obj; obj = obj->nobj)
719 if (obj != otmp) {
720 i = obj->invlet;
721 if ('a' <= i && i <= 'z')
722 inuse[i - 'a'] = TRUE;
723 else if ('A' <= i && i <= 'Z')
724 inuse[i - 'A' + 26] = TRUE;
725 if (i == otmp->invlet)
726 otmp->invlet = 0;
728 if ((i = otmp->invlet)
729 && (('a' <= i && i <= 'z') || ('A' <= i && i <= 'Z')))
730 return;
731 for (i = gl.lastinvnr + 1; i != gl.lastinvnr; i++) {
732 if (i == invlet_basic) {
733 i = -1;
734 continue;
736 if (!inuse[i])
737 break;
739 otmp->invlet =
740 (inuse[i] ? NOINVSYM : (i < 26) ? ('a' + i) : ('A' + i - 26));
741 gl.lastinvnr = i;
744 /* note: assumes ASCII; toggling a bit puts lowercase in front of uppercase */
745 #define inv_rank(o) ((o)->invlet ^ 040)
747 /* sort the inventory; used by addinv() and doorganize() */
748 staticfn void
749 reorder_invent(void)
751 struct obj *otmp, *prev, *next;
752 boolean need_more_sorting;
754 do {
756 * We expect at most one item to be out of order, so this
757 * isn't nearly as inefficient as it may first appear.
759 need_more_sorting = FALSE;
760 for (otmp = gi.invent, prev = 0; otmp; ) {
761 next = otmp->nobj;
762 if (next && inv_rank(next) < inv_rank(otmp)) {
763 need_more_sorting = TRUE;
764 if (prev)
765 prev->nobj = next;
766 else
767 gi.invent = next;
768 otmp->nobj = next->nobj;
769 next->nobj = otmp;
770 prev = next;
771 } else {
772 prev = otmp;
773 otmp = next;
776 } while (need_more_sorting);
779 #undef inv_rank
781 /* scan a list of objects to see whether another object will merge with
782 one of them; used in pickup.c when all 52 inventory slots are in use,
783 to figure out whether another object could still be picked up */
784 struct obj *
785 merge_choice(struct obj *objlist, struct obj *obj)
787 struct monst *shkp;
788 unsigned save_nocharge;
790 if (!objlist) /* might be checking 'obj' against empty inventory */
791 return (struct obj *) 0;
792 if (obj->otyp == SCR_SCARE_MONSTER) /* punt on these */
793 return (struct obj *) 0;
794 /* if this is an item on the shop floor, the attributes it will
795 have when carried are different from what they are now; prevent
796 that from eliciting an incorrect result from mergable() */
797 save_nocharge = obj->no_charge;
798 if (objlist == gi.invent && obj->where == OBJ_FLOOR
799 && (shkp = shop_keeper(inside_shop(obj->ox, obj->oy))) != 0) {
800 if (obj->no_charge)
801 obj->no_charge = 0;
802 /* A billable object won't have its `unpaid' bit set, so would
803 erroneously seem to be a candidate to merge with a similar
804 ordinary object. That's no good, because once it's really
805 picked up, it won't merge after all. It might merge with
806 another unpaid object, but we can't check that here (depends
807 too much upon shk's bill) and if it doesn't merge it would
808 end up in the '#' overflow inventory slot, so reject it now. */
809 else if (inhishop(shkp))
810 return (struct obj *) 0;
812 do {
813 /*assert(objlist != NULL);*/
814 if (mergable(objlist, obj))
815 break;
816 objlist = objlist->nobj;
817 } while (objlist);
818 obj->no_charge = save_nocharge;
819 return objlist;
822 /* merge obj with otmp and delete obj if types agree */
824 merged(struct obj **potmp, struct obj **pobj)
826 struct obj *otmp = *potmp, *obj = *pobj;
827 boolean discovered = FALSE;
829 if (mergable(otmp, obj)) {
830 /* Approximate age: we do it this way because if we were to
831 * do it "accurately" (merge only when ages are identical)
832 * we'd wind up never merging any corpses.
833 * otmp->age = otmp->age*(1-proportion) + obj->age*proportion;
835 * Don't do the age manipulation if lit. We would need
836 * to stop the burn on both items, then merge the age,
837 * then restart the burn. Glob ages are averaged in the
838 * absorb routine, which uses weight rather than quantity
839 * to adjust for proportion (glob quantity is always 1).
841 if (!obj->lamplit && !obj->globby)
842 otmp->age = ((otmp->age * otmp->quan) + (obj->age * obj->quan))
843 / (otmp->quan + obj->quan);
845 if (!otmp->globby)
846 otmp->quan += obj->quan;
847 /* temporary special case for gold objects!!!! */
848 if (otmp->oclass == COIN_CLASS)
849 otmp->owt = weight(otmp), otmp->bknown = 0;
850 /* and puddings!!!1!!one! */
851 else if (!Is_pudding(otmp))
852 otmp->owt = weight(otmp);
853 if (!has_oname(otmp) && has_oname(obj))
854 otmp = *potmp = oname(otmp, ONAME(obj), ONAME_SKIP_INVUPD);
855 obj_extract_self(obj);
857 if (obj->pickup_prev && otmp->where == OBJ_INVENT)
858 otmp->pickup_prev = 1;
860 /* really should merge the timeouts */
861 if (obj->lamplit)
862 obj_merge_light_sources(obj, otmp);
863 if (obj->timed)
864 obj_stop_timers(obj); /* follows lights */
866 /* objects can be identified by comparing them (unless Blind,
867 but that is handled in mergable()); the object becomes
868 identified in a particular dimension if either object was
869 previously identified in that dimension, and if the
870 identification states don't match, one of them must have
871 previously been identified */
872 if (obj->known != otmp->known) {
873 otmp->known = 1;
874 discovered = TRUE;
876 if (obj->rknown != otmp->rknown) {
877 otmp->rknown = 1;
878 if (otmp->oerodeproof)
879 discovered = TRUE;
881 if (obj->bknown != otmp->bknown) {
882 otmp->bknown = 1;
883 if (!Role_if(PM_CLERIC))
884 discovered = TRUE;
887 /* fixup for `#adjust' merging wielded darts, daggers, &c */
888 if (obj->owornmask && carried(otmp)) {
889 long wmask = otmp->owornmask | obj->owornmask;
891 /* Both the items might be worn in competing slots;
892 merger preference (regardless of which is which):
893 primary weapon + alternate weapon -> primary weapon;
894 primary weapon + quiver -> primary weapon;
895 alternate weapon + quiver -> alternate weapon.
896 (Prior to 3.3.0, it was not possible for the two
897 stacks to be worn in different slots and `obj'
898 didn't need to be unworn when merging.) */
899 if ((wmask & W_WEP) != 0L) {
900 wmask = W_WEP;
901 } else if ((wmask & W_SWAPWEP) != 0L) {
902 wmask = W_SWAPWEP;
903 } else if ((wmask & W_QUIVER) != 0L) {
904 wmask = W_QUIVER;
905 } else {
906 impossible("merging strangely worn items (%lx)", wmask);
907 wmask = otmp->owornmask;
909 if ((otmp->owornmask & ~wmask) != 0L)
910 setnotworn(otmp);
911 setworn(otmp, wmask);
912 setnotworn(obj);
913 #if 0
914 /* (this should not be necessary, since items
915 already in a monster's inventory don't ever get
916 merged into other objects [only vice versa]) */
917 } else if (obj->owornmask && mcarried(otmp)) {
918 if (obj == MON_WEP(otmp->ocarry)) {
919 MON_WEP(otmp->ocarry) = otmp;
920 otmp->owornmask = W_WEP;
922 #endif /*0*/
925 /* mergable() no longer requires 'bypass' to match; if 'obj' has
926 the bypass bit set, force the combined stack to have that too;
927 primarily in case this merge is occurring because stackobj()
928 is operating on an object just dropped by a monster that was
929 zapped with polymorph, we want bypass set in order to inhibit
930 the same zap from affecting the new combined stack when it hits
931 objects at the monster's spot (but also in case we're called by
932 code that's using obj->bypass to track 'already processed') */
933 if (obj->bypass)
934 otmp->bypass = 1;
936 /* handle puddings a bit differently; absorption will free the
937 other object automatically so we can just return out from here */
938 if (obj->globby) {
939 pudding_merge_message(otmp, obj);
940 obj_absorb(potmp, pobj);
941 return 1;
944 /* Print a message if item comparison discovers more
945 information about the items (with the exception of thrown
946 items, where this would be too spammy as such items get
947 unidentified by monsters very frequently). */
948 if (discovered && otmp->where == OBJ_INVENT
949 && obj->how_lost != LOST_THROWN
950 && otmp->how_lost != LOST_THROWN) {
951 pline("You learn more about your items by comparing them.");
954 obfree(obj, otmp); /* free(obj), bill->otmp */
955 return 1;
957 return 0;
961 * Adjust hero intrinsics as if this object was being added to the hero's
962 * inventory. Called _before_ the object has been added to the hero's
963 * inventory.
965 * This is called when adding objects to the hero's inventory normally (via
966 * addinv) or when an object in the hero's inventory has been polymorphed
967 * in-place.
969 * It may be valid to merge this code with addinv_core2().
971 void
972 addinv_core1(struct obj *obj)
974 if (obj->oclass == COIN_CLASS) {
975 disp.botl = TRUE;
976 } else if (obj->otyp == AMULET_OF_YENDOR) {
977 if (u.uhave.amulet)
978 impossible("already have amulet?");
979 u.uhave.amulet = 1;
980 record_achievement(ACH_AMUL);
981 } else if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
982 if (u.uhave.menorah)
983 impossible("already have candelabrum?");
984 u.uhave.menorah = 1;
985 record_achievement(ACH_CNDL);
986 } else if (obj->otyp == BELL_OF_OPENING) {
987 if (u.uhave.bell)
988 impossible("already have silver bell?");
989 u.uhave.bell = 1;
990 record_achievement(ACH_BELL);
991 } else if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
992 if (u.uhave.book)
993 impossible("already have the book?");
994 u.uhave.book = 1;
995 record_achievement(ACH_BOOK);
996 } else if (obj->oartifact) {
997 if (is_quest_artifact(obj)) {
998 if (u.uhave.questart)
999 impossible("already have quest artifact?");
1000 u.uhave.questart = 1;
1001 artitouch(obj);
1003 set_artifact_intrinsic(obj, 1, W_ART);
1006 /* "special achievements"; revealed in end of game disclosure and
1007 dumplog, originally just recorded in XLOGFILE */
1008 if (is_mines_prize(obj)) {
1009 record_achievement(ACH_MINE_PRIZE);
1010 svc.context.achieveo.mines_prize_oid = 0; /* done w/ luckstone o_id */
1011 obj->nomerge = 0; /* was set in create_object(sp_lev.c) */
1012 } else if (is_soko_prize(obj)) {
1013 record_achievement(ACH_SOKO_PRIZE);
1014 svc.context.achieveo.soko_prize_oid = 0; /* done w/ bag/amulet o_id */
1015 obj->nomerge = 0; /* (got set in sp_lev.c) */
1020 * Adjust hero intrinsics as if this object was being added to the hero's
1021 * inventory. Called _after_ the object has been added to the hero's
1022 * inventory.
1024 * This is called when adding objects to the hero's inventory normally (via
1025 * addinv) or when an object in the hero's inventory has been polymorphed
1026 * in-place.
1028 void
1029 addinv_core2(struct obj *obj)
1031 if (confers_luck(obj)) {
1032 /* new luckstone must be in inventory by this point
1033 * for correct calculation */
1034 set_moreluck();
1039 * Add obj to the hero's inventory. Make sure the object is "free".
1040 * Adjust hero attributes as necessary.
1042 staticfn struct obj *
1043 addinv_core0(struct obj *obj, struct obj *other_obj,
1044 boolean update_perm_invent)
1046 struct obj *otmp, *prev;
1047 int saved_otyp = (int) obj->otyp; /* for panic */
1048 boolean obj_was_thrown;
1050 if (obj->where != OBJ_FREE)
1051 panic("addinv: obj not free");
1052 /* normally addtobill() clears no_charge when items in a shop are
1053 picked up, but won't do so if the shop has become untended */
1054 obj->no_charge = 0; /* should not be set in hero's invent */
1055 if (Has_contents(obj))
1056 picked_container(obj); /* clear no_charge */
1057 obj_was_thrown = (obj->how_lost == LOST_THROWN);
1058 obj->how_lost = LOST_NONE;
1060 if (gl.loot_reset_justpicked) {
1061 gl.loot_reset_justpicked = FALSE;
1062 reset_justpicked(gi.invent);
1065 addinv_core1(obj); /* handle most side effects of carrying obj */
1067 /* for addinv_before(); if something has been removed and is now being
1068 reinserted, try to put it in the same place instead of merging or
1069 placing at end; for thrown-and-return weapon with !fixinv setting */
1070 if (other_obj) {
1071 for (otmp = gi.invent; otmp; otmp = otmp->nobj) {
1072 if (otmp->nobj == other_obj) {
1073 obj->nobj = other_obj;
1074 otmp->nobj = obj;
1075 obj->where = OBJ_INVENT;
1076 goto added;
1081 /* merge with quiver in preference to any other inventory slot
1082 in case quiver and wielded weapon are both eligible; adding
1083 extra to quivered stack is more useful than to wielded one */
1084 if (uquiver && merged(&uquiver, &obj)) {
1085 obj = uquiver;
1086 if (!obj)
1087 panic("addinv: null obj after quiver merge otyp=%d", saved_otyp);
1088 goto added;
1090 /* merge if possible; find end of chain in the process */
1091 for (prev = 0, otmp = gi.invent; otmp; prev = otmp, otmp = otmp->nobj)
1092 if (merged(&otmp, &obj)) {
1093 obj = otmp;
1094 if (!obj)
1095 panic("addinv: null obj after merge otyp=%d", saved_otyp);
1096 goto added;
1098 /* didn't merge, so insert into chain */
1099 assigninvlet(obj);
1100 if (flags.invlet_constant || !prev) {
1101 obj->nobj = gi.invent; /* insert at beginning */
1102 gi.invent = obj;
1103 if (flags.invlet_constant)
1104 reorder_invent();
1105 } else {
1106 prev->nobj = obj; /* insert at end */
1107 obj->nobj = 0;
1109 obj->where = OBJ_INVENT;
1111 /* fill empty quiver if obj was thrown */
1112 if (obj_was_thrown && flags.pickup_thrown && !uquiver
1113 /* if Mjollnir is thrown and fails to return, we want to
1114 auto-pick it when we move to its spot, but not into quiver
1115 because it needs to be wielded to be re-thrown;
1116 aklys likewise because player using 'f' to throw it might
1117 not notice that it isn't wielded until it fails to return
1118 several times; we never auto-wield, just omit from quiver
1119 so that player will be prompted for what to throw and
1120 possibly realize that re-wielding is necessary */
1121 && obj->oartifact != ART_MJOLLNIR && obj->otyp != AKLYS
1122 && (throwing_weapon(obj) || is_ammo(obj)))
1123 setuqwep(obj);
1124 added:
1125 obj->pickup_prev = 1;
1126 addinv_core2(obj); /* handle extrinsics conferred by carrying obj */
1127 carry_obj_effects(obj); /* carrying affects the obj */
1128 if (update_perm_invent)
1129 update_inventory();
1130 return obj;
1133 /* add obj to the hero's inventory in the default fashion */
1134 struct obj *
1135 addinv(struct obj *obj)
1137 return addinv_core0(obj, (struct obj *) 0, TRUE);
1140 /* add obj to the hero's inventory by inserting in front of a specific item;
1141 used for throw-and-return in case '!fixinv' is in effect */
1142 struct obj *
1143 addinv_before(struct obj *obj, struct obj *other_obj)
1145 /* if 'other_obj' is present this will implicitly be 'nomerge' */
1146 return addinv_core0(obj, other_obj, TRUE);
1149 /* return value will always be 'obj' */
1150 struct obj *
1151 addinv_nomerge(struct obj *obj)
1153 struct obj *result;
1154 unsigned save_nomerge = obj->nomerge;
1156 obj->nomerge = 1;
1157 result = addinv(obj);
1158 obj->nomerge = save_nomerge;
1159 return result;
1163 * Some objects are affected by being carried.
1164 * Make those adjustments here. Called _after_ the object
1165 * has been added to the hero's or monster's inventory,
1166 * and after hero's intrinsics have been updated.
1168 void
1169 carry_obj_effects(struct obj *obj)
1171 /* Cursed figurines can spontaneously transform when carried. */
1172 if (obj->otyp == FIGURINE) {
1173 if (obj->cursed && obj->corpsenm != NON_PM
1174 && !dead_species(obj->corpsenm, TRUE)) {
1175 attach_fig_transform_timeout(obj);
1180 DISABLE_WARNING_FORMAT_NONLITERAL
1182 /* Add an item to the inventory unless we're fumbling or it refuses to be
1183 * held (via touch_artifact), and give a message.
1184 * If there aren't any free inventory slots, we'll drop it instead.
1185 * If both success and failure messages are NULL, then we're just doing the
1186 * fumbling/slot-limit checking for a silent grab. In any case,
1187 * touch_artifact will print its own messages if they are warranted.
1189 struct obj *
1190 hold_another_object(
1191 struct obj *obj, /* object to be held */
1192 const char *drop_fmt, /* format string for message if it can't be held */
1193 const char *drop_arg, /* argument to use when formatting message */
1194 const char *hold_msg) /* message to display if successfully held */
1196 char buf[BUFSZ];
1198 if (!Blind)
1199 obj->dknown = 1; /* maximize mergeability */
1200 if (obj->oartifact) {
1201 /* place_object may change these */
1202 boolean crysknife = (obj->otyp == CRYSKNIFE);
1203 int oerode = obj->oerodeproof;
1204 boolean wasUpolyd = Upolyd;
1206 /* in case touching this object turns out to be fatal */
1207 place_object(obj, u.ux, u.uy);
1209 if (!touch_artifact(obj, &gy.youmonst)) {
1210 obj_extract_self(obj); /* remove it from the floor */
1211 dropy(obj); /* now put it back again :-) */
1212 return obj;
1213 } else if (wasUpolyd && !Upolyd) {
1214 /* lose your grip if you revert your form */
1215 if (drop_fmt)
1216 pline(drop_fmt, drop_arg);
1217 obj_extract_self(obj);
1218 dropy(obj);
1219 return obj;
1221 obj_extract_self(obj);
1222 if (crysknife) {
1223 obj->otyp = CRYSKNIFE;
1224 obj->oerodeproof = oerode;
1227 if (Fumbling) {
1228 obj->nomerge = 1;
1229 /* dropping expects obj to be in invent; since it's going to be
1230 dropped, avoid perminv update when temporarily adding it */
1231 obj = addinv_core0(obj, (struct obj *) 0, FALSE);
1232 goto drop_it;
1233 } else if (obj->otyp == CORPSE
1234 && !u_safe_from_fatal_corpse(obj, st_all)
1235 && obj->wishedfor) {
1236 obj->wishedfor = 0;
1237 obj = addinv_core0(obj, (struct obj *) 0, FALSE);
1238 goto drop_it;
1239 } else {
1240 long oquan = obj->quan;
1241 int prev_encumbr = near_capacity(); /* before addinv() */
1243 /* encumbrance limit is max( current_state, pickup_burden );
1244 this used to use hardcoded MOD_ENCUMBER (stressed) instead
1245 of the 'pickup_burden' option (which defaults to stressed) */
1246 if (prev_encumbr < flags.pickup_burden)
1247 prev_encumbr = flags.pickup_burden;
1248 /* addinv() may redraw the entire inventory, overwriting
1249 drop_arg when it is kept in an 'obuf' from doname();
1250 [should no longer be necessary now that perm_invent update is
1251 suppressed, but it's cheap to keep as a paranoid precaution] */
1252 if (drop_arg)
1253 drop_arg = strcpy(buf, drop_arg);
1255 obj = addinv_core0(obj, (struct obj *) 0, FALSE);
1256 if (inv_cnt(FALSE) > invlet_basic
1257 || ((obj->otyp != LOADSTONE || !obj->cursed)
1258 && near_capacity() > prev_encumbr)) {
1259 /* undo any merge which took place */
1260 if (obj->quan > oquan)
1261 obj = splitobj(obj, oquan);
1262 goto drop_it;
1263 } else {
1264 if (flags.autoquiver && !uquiver && !obj->owornmask
1265 && (is_missile(obj) || ammo_and_launcher(obj, uwep)
1266 || ammo_and_launcher(obj, uswapwep)))
1267 setuqwep(obj);
1268 if (hold_msg || drop_fmt)
1269 prinv(hold_msg, obj, oquan);
1270 /* obj made it into inventory and is staying there */
1271 update_inventory();
1272 (void) encumber_msg();
1275 return obj;
1277 drop_it:
1278 if (drop_fmt)
1279 pline(drop_fmt, drop_arg);
1280 obj->nomerge = 0;
1281 if (can_reach_floor(TRUE) || u.uswallow) {
1282 dropx(obj);
1283 } else {
1284 freeinv(obj);
1285 hitfloor(obj, FALSE);
1287 return (struct obj *) 0; /* might be gone */
1290 RESTORE_WARNING_FORMAT_NONLITERAL
1292 /* useup() all of an item regardless of its quantity */
1293 void
1294 useupall(struct obj *obj)
1296 setnotworn(obj);
1297 freeinv(obj);
1298 obfree(obj, (struct obj *) 0); /* deletes contents also */
1301 /* an item in inventory is going away after being used */
1302 void
1303 useup(struct obj *obj)
1305 /* Note: This works correctly for containers because they (containers)
1306 don't merge. */
1307 if (obj->quan > 1L) {
1308 obj->in_use = FALSE; /* no longer in use */
1309 obj->quan--;
1310 obj->owt = weight(obj);
1311 update_inventory();
1312 } else {
1313 useupall(obj);
1317 /* use one charge from an item and possibly incur shop debt for it */
1318 void
1319 consume_obj_charge(
1320 struct obj *obj,
1321 boolean maybe_unpaid) /* false if caller handles shop billing */
1323 if (maybe_unpaid)
1324 check_unpaid(obj);
1325 obj->spe -= 1;
1326 if (obj->known)
1327 update_inventory();
1331 * Adjust hero's attributes as if this object was being removed from the
1332 * hero's inventory. This should only be called from freeinv() and
1333 * where we are polymorphing an object already in the hero's inventory.
1335 * Should think of a better name...
1337 void
1338 freeinv_core(struct obj *obj)
1340 if (obj->oclass == COIN_CLASS) {
1341 disp.botl = TRUE;
1342 return;
1343 } else if (obj->otyp == AMULET_OF_YENDOR) {
1344 if (!u.uhave.amulet)
1345 impossible("don't have amulet?");
1346 u.uhave.amulet = 0;
1347 } else if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
1348 if (!u.uhave.menorah)
1349 impossible("don't have candelabrum?");
1350 u.uhave.menorah = 0;
1351 } else if (obj->otyp == BELL_OF_OPENING) {
1352 if (!u.uhave.bell)
1353 impossible("don't have silver bell?");
1354 u.uhave.bell = 0;
1355 } else if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
1356 if (!u.uhave.book)
1357 impossible("don't have the book?");
1358 u.uhave.book = 0;
1359 } else if (obj->oartifact) {
1360 if (is_quest_artifact(obj)) {
1361 if (!u.uhave.questart)
1362 impossible("don't have quest artifact?");
1363 u.uhave.questart = 0;
1365 set_artifact_intrinsic(obj, 0, W_ART);
1368 if (obj->otyp == LOADSTONE) {
1369 curse(obj);
1370 } else if (confers_luck(obj)) {
1371 set_moreluck();
1372 disp.botl = TRUE;
1373 } else if (obj->otyp == FIGURINE && obj->timed) {
1374 (void) stop_timer(FIG_TRANSFORM, obj_to_any(obj));
1377 if (obj == svc.context.tin.tin) {
1378 svc.context.tin.tin = (struct obj *) 0;
1379 svc.context.tin.o_id = 0;
1383 /* remove an object from the hero's inventory */
1384 void
1385 freeinv(struct obj *obj)
1387 extract_nobj(obj, &gi.invent);
1388 obj->pickup_prev = 0;
1389 freeinv_core(obj);
1390 update_inventory();
1393 /* drawbridge is destroying all objects at <x,y> */
1394 void
1395 delallobj(coordxy x, coordxy y)
1397 struct obj *otmp, *otmp2;
1399 for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp2) {
1400 if (otmp == uball)
1401 unpunish();
1402 /* after unpunish(), or might get deallocated chain */
1403 otmp2 = otmp->nexthere;
1404 if (otmp == uchain)
1405 continue;
1406 delobj(otmp);
1410 /* normal object deletion (if unpaid, it remains on the bill) */
1411 void
1412 delobj(struct obj *obj)
1414 delobj_core(obj, FALSE);
1417 /* destroy object; caller has control over whether to destroy something
1418 that ordinarily shouldn't be destroyed */
1419 void
1420 delobj_core(
1421 struct obj *obj,
1422 boolean force) /* 'force==TRUE' used when reviving Rider corpses */
1424 boolean update_map;
1426 /* obj_resists(obj,0,0) protects the Amulet, the invocation tools,
1427 and Rider corpses */
1428 if (!force && obj_resists(obj, 0, 0)) {
1429 /* player might be doing something stupid, but we
1430 * can't guarantee that. assume special artifacts
1431 * are indestructible via drawbridges, and exploding
1432 * chests, and golem creation, and ...
1434 obj->in_use = 0; /* in case caller has set this to 1 */
1435 return;
1437 update_map = (obj->where == OBJ_FLOOR);
1438 obj_extract_self(obj);
1439 if (update_map) { /* floor object's coordinates are always up to date */
1440 maybe_unhide_at(obj->ox, obj->oy);
1441 newsym(obj->ox, obj->oy);
1443 obfree(obj, (struct obj *) 0); /* frees contents also */
1446 /* try to find a particular type of object at designated map location */
1447 struct obj *
1448 sobj_at(int otyp, coordxy x, coordxy y)
1450 struct obj *otmp;
1452 for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere)
1453 if (otmp->otyp == otyp)
1454 break;
1456 return otmp;
1459 /* sobj_at(&c) traversal -- find next object of specified type */
1460 struct obj *
1461 nxtobj(struct obj *obj, int type, boolean by_nexthere)
1463 struct obj *otmp;
1465 otmp = obj; /* start with the object after this one */
1466 do {
1467 otmp = !by_nexthere ? otmp->nobj : otmp->nexthere;
1468 if (!otmp)
1469 break;
1470 } while (otmp->otyp != type);
1472 return otmp;
1475 /* return inventory object of type 'type' if hero has one, otherwise Null */
1476 struct obj *
1477 carrying(int type)
1479 struct obj *otmp;
1481 /* this could be replaced by 'return m_carrying(&gy.youmonst, type);' */
1482 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
1483 if (otmp->otyp == type)
1484 break;
1485 return otmp;
1488 /* return inventory object of type that will petrify on touch */
1489 struct obj *
1490 carrying_stoning_corpse(void)
1492 struct obj *otmp;
1494 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
1495 if (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm]))
1496 break;
1497 return otmp;
1500 /* Fictional and not-so-fictional currencies.
1501 * http://concord.wikia.com/wiki/List_of_Fictional_Currencies
1503 static const char *const currencies[] = {
1504 "Altarian Dollar", /* The Hitchhiker's Guide to the Galaxy */
1505 "Ankh-Morpork Dollar", /* Discworld */
1506 "auric", /* The Domination of Draka */
1507 "buckazoid", /* Space Quest */
1508 "cirbozoid", /* Starslip */
1509 "credit chit", /* Deus Ex */
1510 "cubit", /* Battlestar Galactica */
1511 "Flanian Pobble Bead", /* The Hitchhiker's Guide to the Galaxy */
1512 "fretzer", /* Jules Verne */
1513 "imperial credit", /* Star Wars */
1514 "Hong Kong Luna Dollar", /* The Moon is a Harsh Mistress */
1515 "kongbuck", /* Snow Crash */
1516 "nanite", /* System Shock 2 */
1517 "quatloo", /* Star Trek, Sim City */
1518 "simoleon", /* Sim City */
1519 "solari", /* Spaceballs */
1520 "spacebuck", /* Spaceballs */
1521 "sporebuck", /* Spore */
1522 "Triganic Pu", /* The Hitchhiker's Guide to the Galaxy */
1523 "woolong", /* Cowboy Bebop */
1524 "zorkmid", /* Zork, NetHack */
1527 const char *
1528 currency(long amount)
1530 const char *res;
1532 res = Hallucination ? ROLL_FROM(currencies) : "zorkmid";
1533 if (amount != 1L)
1534 res = makeplural(res);
1535 return res;
1538 struct obj *
1539 u_carried_gloves(void)
1541 struct obj *otmp, *gloves = (struct obj *) 0;
1543 if (uarmg) {
1544 gloves = uarmg;
1545 } else {
1546 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
1547 if (is_gloves(otmp)) {
1548 gloves = otmp;
1549 break;
1552 return gloves;
1556 /* 3.6 tribute */
1557 struct obj *
1558 u_have_novel(void)
1560 struct obj *otmp;
1562 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
1563 if (otmp->otyp == SPE_NOVEL)
1564 return otmp;
1565 return (struct obj *) 0;
1568 struct obj *
1569 o_on(unsigned int id, struct obj *objchn)
1571 struct obj *temp;
1573 while (objchn) {
1574 if (objchn->o_id == id)
1575 return objchn;
1576 if (Has_contents(objchn) && (temp = o_on(id, objchn->cobj)))
1577 return temp;
1578 objchn = objchn->nobj;
1580 return (struct obj *) 0;
1583 boolean
1584 obj_here(struct obj *obj, coordxy x, coordxy y)
1586 struct obj *otmp;
1588 for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere)
1589 if (obj == otmp)
1590 return TRUE;
1591 return FALSE;
1594 struct obj *
1595 g_at(coordxy x, coordxy y)
1597 struct obj *obj = svl.level.objects[x][y];
1599 while (obj) {
1600 if (obj->oclass == COIN_CLASS)
1601 return obj;
1602 obj = obj->nexthere;
1604 return (struct obj *) 0;
1607 /* compact a string of inventory letters by dashing runs of letters */
1608 staticfn void
1609 compactify(char *buf)
1611 int i1 = 1, i2 = 1;
1612 char ilet, ilet1, ilet2;
1614 ilet2 = buf[0];
1615 ilet1 = buf[1];
1616 buf[++i2] = buf[++i1];
1617 ilet = buf[i1];
1618 while (ilet) {
1619 if (ilet == ilet1 + 1) {
1620 if (ilet1 == ilet2 + 1)
1621 buf[i2 - 1] = ilet1 = '-';
1622 else if (ilet2 == '-') {
1623 buf[i2 - 1] = ++ilet1;
1624 buf[i2] = buf[++i1];
1625 ilet = buf[i1];
1626 continue;
1628 } else if (ilet == NOINVSYM) {
1629 /* compact three or more consecutive '#'
1630 characters into "#-#" */
1631 if (i2 >= 2 && buf[i2 - 2] == NOINVSYM && buf[i2 - 1] == NOINVSYM)
1632 buf[i2 - 1] = '-';
1633 else if (i2 >= 3 && buf[i2 - 3] == NOINVSYM && buf[i2 - 2] == '-'
1634 && buf[i2 - 1] == NOINVSYM)
1635 --i2;
1637 ilet2 = ilet1;
1638 ilet1 = ilet;
1639 buf[++i2] = buf[++i1];
1640 ilet = buf[i1];
1644 /* some objects shouldn't be split when count given to getobj or askchain */
1645 boolean
1646 splittable(struct obj *obj)
1648 return !((obj->otyp == LOADSTONE && obj->cursed)
1649 || (obj == uwep && welded(uwep)));
1652 /* match the prompt for either 'T' or 'R' command */
1653 staticfn boolean
1654 taking_off(const char *action)
1656 return !strcmp(action, "take off") || !strcmp(action, "remove");
1659 staticfn void
1660 mime_action(const char *word)
1662 char buf[BUFSZ];
1663 char *bp, *pfx, *sfx;
1665 Strcpy(buf, word);
1666 bp = pfx = sfx = (char *) 0;
1668 if ((bp = strstr(buf, " on the ")) != 0) {
1669 /* rub on the stone[s] */
1670 *bp = '\0';
1671 sfx = (bp + 1); /* "something <sfx>" */
1673 if ((!strncmp(buf, "rub the ", 8) && strstr(buf + 8, " on"))
1674 || (!strncmp(buf, "dip ", 4) && strstr(buf + 4, " into"))) {
1675 /* "rub the royal jelly on" -> "rubbing the royal jelly on", or
1676 "dip <foo> into" => "dipping <foo> into" */
1677 buf[3] = '\0';
1678 pfx = &buf[3 + 1]; /* "<pfx> something" */
1680 if ((bp = strstr(buf, " or ")) != 0) {
1681 *bp = '\0';
1682 bp = (rn2(2) ? buf : (bp + 4));
1683 } else
1684 bp = buf;
1686 You("mime %s%s%s something%s%s.", ing_suffix(bp),
1687 pfx ? " " : "", pfx ? pfx : "", sfx ? " " : "", sfx ? sfx : "");
1690 /* getobj callback that allows any object - but not hands. */
1692 any_obj_ok(struct obj *obj)
1694 if (obj)
1695 return GETOBJ_SUGGEST;
1696 return GETOBJ_EXCLUDE;
1699 /* return string describing your hands based on action. */
1700 staticfn char *
1701 getobj_hands_txt(const char *action, char *qbuf)
1703 if (!strcmp(action, "grease")) {
1704 Sprintf(qbuf, "your %s", fingers_or_gloves(FALSE));
1705 } else if (!strcmp(action, "write with")) {
1706 Sprintf(qbuf, "your %s", body_part(FINGERTIP));
1707 } else if (!strcmp(action, "wield")) {
1708 Sprintf(qbuf, "your %s %s%s", uarmg ? "gloved" : "bare",
1709 makeplural(body_part(HAND)),
1710 !uwep ? " (wielded)" : "");
1711 } else if (!strcmp(action, "ready")) {
1712 Sprintf(qbuf, "empty quiver%s",
1713 !uquiver ? " (nothing readied)" : "");
1714 } else {
1715 Sprintf(qbuf, "your %s", makeplural(body_part(HAND)));
1717 return qbuf;
1721 * getobj returns:
1722 * struct obj *xxx: object to do something with.
1723 * (struct obj *) 0 error return: no object.
1724 * &hands_obj explicitly no object (as in w-).
1725 * The obj_ok callback should not have side effects (apart from
1726 * abnormal-behavior things like impossible calls); it can be called multiple
1727 * times on the same object during the execution of this function.
1728 * Callbacks' argument is either a valid object pointer or a null pointer,
1729 * which represents the validity of doing that action on HANDS_SYM. getobj
1730 * won't call it with &hands_obj, so its behavior can be undefined in that
1731 * case.
1733 struct obj *
1734 getobj(
1735 const char *word, /* usually a direct verb such as "drop" */
1736 int (*obj_ok)(OBJ_P), /* callback to classify an object's suitability */
1737 unsigned int ctrlflags) /* some control to fine-tune the behavior */
1739 struct obj *otmp;
1740 char ilet = 0;
1741 char buf[BUFSZ], qbuf[QBUFSZ];
1742 char lets[BUFSZ], altlets[BUFSZ];
1743 int suggested = 0;
1744 char *bp = buf, *ap = altlets;
1745 boolean allowcnt = (ctrlflags & GETOBJ_ALLOWCNT),
1746 forceprompt = (ctrlflags & GETOBJ_PROMPT),
1747 allownone = FALSE;
1748 int inaccess = 0; /* counts GETOBJ_EXCLUDE_INACCESS items to decide
1749 * between "you don't have anything to <foo>"
1750 * versus "you don't have anything _else_ to <foo>"
1751 * (also used for GETOBJ_EXCLUDE_NONINVENT) */
1752 long cnt = 0L;
1753 boolean cntgiven = FALSE;
1754 boolean msggiven = FALSE;
1755 boolean oneloop = FALSE;
1756 Loot *sortedinvent, *srtinv;
1757 struct _cmd_queue cq, *cmdq;
1758 boolean need_more_cq = FALSE;
1760 need_more_cq:
1761 if ((cmdq = cmdq_pop()) != 0) {
1762 cq = *cmdq;
1763 free(cmdq);
1764 /* user-input means pick something interactively now, with more
1765 in the command queue for after that; if not user-input, it
1766 has to be a key here */
1767 if (cq.typ != CMDQ_USER_INPUT) {
1768 otmp = 0; /* in case of non-key or lookup failure */
1769 if (cq.typ == CMDQ_KEY) {
1770 int v;
1772 if (cq.key == HANDS_SYM) {
1773 /* check whether the hands/self choice is suitable */
1774 v = (*obj_ok)((struct obj *) 0);
1775 if (v == GETOBJ_SUGGEST || v == GETOBJ_DOWNPLAY)
1776 otmp = &hands_obj;
1777 } else {
1778 /* there could be more than one match if key is '#';
1779 take first one which passes the obj_ok callback */
1780 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
1781 if (otmp->invlet == cq.key) {
1782 v = (*obj_ok)(otmp);
1783 if (v == GETOBJ_SUGGEST || v == GETOBJ_DOWNPLAY)
1784 break;
1787 } else if (cq.typ == CMDQ_INT) {
1788 /* getting a partial stack */
1789 if (!cntgiven && allowcnt) {
1790 cnt = (long) cq.intval;
1791 cntgiven = TRUE;
1792 goto need_more_cq; /* now, get CMDQ_KEY */
1793 } else {
1794 cmdq_clear(CQ_CANNED);
1795 /* should maybe clear the CQ_REPEAT too? */
1796 return NULL;
1799 if (!otmp) { /* didn't find what we were looking for, */
1800 cmdq_clear(CQ_CANNED); /* so discard any other queued cmnds */
1801 } else if (cntgiven) {
1802 /* if stack is smaller than count, drop the whole stack */
1803 if (cnt < 1L || otmp->quan <= cnt)
1804 cntgiven = FALSE;
1805 goto split_otmp;
1807 return otmp;
1808 } /* !CMDQ_USER_INPUT */
1809 } else if (need_more_cq) {
1810 return NULL;
1813 /* is "hands"/"self" a valid thing to do this action on? */
1814 switch ((*obj_ok)((struct obj *) 0)) {
1815 case GETOBJ_SUGGEST: /* treat as likely candidate */
1816 allownone = TRUE;
1817 *bp++ = HANDS_SYM;
1818 *bp++ = ' '; /* put a space after the '-' in the prompt */
1819 break;
1820 case GETOBJ_DOWNPLAY: /* acceptable but not shown as likely choice */
1821 case GETOBJ_EXCLUDE_INACCESS: /* nothing currently gives this for '-'
1822 * but theoretically could if wearing
1823 * gloves */
1824 case GETOBJ_EXCLUDE_SELECTABLE: /* ditto, I think... */
1825 allownone = TRUE;
1826 *ap++ = HANDS_SYM;
1827 break;
1828 case GETOBJ_EXCLUDE_NONINVENT: /* player skipped some alternative that's
1829 * not in inventory, now the hands/self
1830 * possibility is telling us so */
1831 forceprompt = FALSE;
1832 inaccess++;
1833 break;
1834 default:
1835 break;
1838 if (!flags.invlet_constant)
1839 reassign();
1841 /* force invent to be in invlet order before collecting candidate
1842 inventory letters */
1843 sortedinvent = sortloot(&gi.invent, SORTLOOT_INVLET, FALSE,
1844 (boolean (*)(OBJ_P)) 0);
1846 for (srtinv = sortedinvent; (otmp = srtinv->obj) != 0; ++srtinv) {
1847 if (&bp[suggested] == &buf[sizeof buf - 1]
1848 || ap == &altlets[sizeof altlets - 1]) {
1849 /* we must have a huge number of noinvsym items somehow */
1850 impossible("getobj: inventory overflow");
1851 break;
1854 bp[suggested++] = otmp->invlet;
1855 switch ((*obj_ok)(otmp)) {
1856 case GETOBJ_EXCLUDE_INACCESS:
1857 /* remove inaccessible things */
1858 suggested--;
1859 inaccess++;
1860 break;
1861 case GETOBJ_EXCLUDE:
1862 case GETOBJ_EXCLUDE_SELECTABLE:
1863 /* remove more inappropriate things, but unlike the first it won't
1864 trigger an "else" in "you don't have anything else to ___" */
1865 suggested--;
1866 break;
1867 case GETOBJ_DOWNPLAY:
1868 /* acceptable but not listed as likely candidates in the prompt
1869 or in the inventory subset if player responds with '?' - thus,
1870 don't add it to lets with bp, but add it to altlets with ap */
1871 suggested--;
1872 forceprompt = TRUE;
1873 *ap++ = otmp->invlet;
1874 break;
1875 case GETOBJ_SUGGEST:
1876 break; /* adding otmp->invlet is all that's needed */
1877 case GETOBJ_EXCLUDE_NONINVENT: /* not applicable for invent items */
1878 default:
1879 impossible("bad return from getobj callback");
1882 unsortloot(&sortedinvent);
1884 bp[suggested] = '\0';
1885 /* If no objects were suggested but we added '- ' at the beginning for
1886 * hands, destroy the trailing space */
1887 if (suggested == 0 && bp > buf && bp[-1] == ' ')
1888 *--bp = '\0';
1889 Strcpy(lets, bp); /* necessary since we destroy buf */
1890 if (suggested > 5) /* compactify string */
1891 compactify(bp);
1892 *ap = '\0';
1894 if (suggested == 0 && !forceprompt && !allownone) {
1895 You("don't have anything %sto %s.", inaccess ? "else " : "", word);
1896 return (struct obj *) 0;
1898 for (;;) {
1899 cnt = 0L;
1900 cntgiven = FALSE;
1901 Sprintf(qbuf, "What do you want to %s?", word);
1902 if (gi.in_doagain) {
1903 ilet = readchar();
1904 } else if (iflags.force_invmenu) {
1905 /* don't overwrite a possible quitchars */
1906 if (!oneloop)
1907 ilet = (*lets || *altlets) ? '?' : '*';
1908 if (!msggiven)
1909 putmsghistory(qbuf, FALSE);
1910 msggiven = TRUE;
1911 oneloop = TRUE;
1912 } else {
1913 if (!buf[0])
1914 Strcat(qbuf, " [*]");
1915 else
1916 Sprintf(eos(qbuf), " [%s or ?*]", buf);
1917 ilet = yn_function(qbuf, (char *) 0, '\0', FALSE);
1919 if (digit(ilet)) {
1920 long tmpcnt = 0L;
1922 if (!allowcnt) {
1923 pline("No count allowed with this command.");
1924 continue;
1926 ilet = get_count(NULL, ilet, LARGEST_INT, &tmpcnt, GC_SAVEHIST);
1927 if (tmpcnt) {
1928 cnt = tmpcnt;
1929 cntgiven = TRUE;
1932 if (strchr(quitchars, ilet)) {
1933 if (flags.verbose)
1934 pline1(Never_mind);
1935 return (struct obj *) 0;
1937 if (ilet == HANDS_SYM) { /* '-' */
1938 if (!allownone)
1939 mime_action(word);
1940 return (allownone ? &hands_obj : (struct obj *) 0);
1942 redo_menu:
1943 /* since gold is now kept in inventory, we need to do processing for
1944 select-from-invent before checking whether gold has been picked */
1945 if (ilet == '?' || ilet == '*') {
1946 char *allowed_choices = (ilet == '?') ? lets : (char *) 0;
1947 long ctmp = 0L;
1948 char menuquery[QBUFSZ];
1949 char *handsbuf = (char *) 0;
1951 if (ilet == '?' && !*lets && *altlets)
1952 allowed_choices = altlets;
1954 menuquery[0] = qbuf[0] = '\0';
1955 if (iflags.force_invmenu)
1956 Snprintf(menuquery, sizeof menuquery,
1957 "What do you want to %s?", word);
1958 if (!allowed_choices || *allowed_choices == HANDS_SYM
1959 || *buf == HANDS_SYM)
1960 handsbuf = getobj_hands_txt(word, qbuf);
1961 ilet = display_pickinv(allowed_choices, handsbuf,
1962 menuquery, allownone, TRUE,
1963 allowcnt ? &ctmp : (long *) 0);
1964 if (!ilet) {
1965 if (oneloop)
1966 return (struct obj *) 0;
1967 continue;
1969 if (ilet == HANDS_SYM)
1970 return &hands_obj;
1971 if (ilet == '\033') {
1972 if (flags.verbose)
1973 pline1(Never_mind);
1974 return (struct obj *) 0;
1976 if (ilet == '*' || ilet == '?')
1977 goto redo_menu;
1978 if (allowcnt && ctmp >= 0L) {
1979 cnt = ctmp;
1980 cntgiven = TRUE;
1982 /* they typed a letter (not a space) at the prompt */
1984 /* find the item which was picked */
1985 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
1986 if (otmp->invlet == ilet)
1987 break;
1988 /* some items have restrictions */
1989 if (ilet == GOLD_SYM
1990 /* guard against the [hypothetical] chance of having more
1991 than one invent slot of gold and picking the non-'$' one */
1992 || (otmp && otmp->oclass == COIN_CLASS)) {
1993 if (otmp && obj_ok(otmp) <= GETOBJ_EXCLUDE) {
1994 You("cannot %s gold.", word);
1995 return (struct obj *) 0;
1998 * Historical note: early Nethack had a bug which was
1999 * first reported for Larn, where trying to drop 2^32-n
2000 * gold pieces was allowed, and did interesting things to
2001 * your money supply. The LRS is the tax bureau from Larn.
2003 if (cntgiven && cnt <= 0L) {
2004 if (cnt < 0L)
2005 pline_The("LRS would be very interested to know"
2006 " you have that much.");
2007 return (struct obj *) 0;
2010 if (cntgiven && !strcmp(word, "throw")) {
2011 static const char only_one[] = "can only throw one at a time";
2012 boolean coins;
2014 /* permit counts for throwing gold, but don't accept counts
2015 for other things since the throw code will split off a
2016 single item anyway; if populating quiver, 'word' will be
2017 "ready" or "fire" and this restriction doesn't apply */
2018 if (cnt == 0L || !otmp)
2019 return (struct obj *) 0;
2020 coins = (otmp->oclass == COIN_CLASS);
2021 if (cnt > 1L && (!coins || cnt > otmp->quan)) {
2022 if (cnt > otmp->quan)
2023 You("only have %ld%s%s.", otmp->quan,
2024 (!coins && otmp->quan > 1L) ? " and " : "",
2025 (!coins && otmp->quan > 1L) ? only_one : "");
2026 else
2027 You("%s.", only_one);
2028 continue;
2031 disp.botl = TRUE; /* May have changed the amount of money */
2032 if (otmp && !gi.in_doagain) {
2033 if (cntgiven && cnt > 0L)
2034 cmdq_add_int(CQ_REPEAT, cnt);
2035 cmdq_add_key(CQ_REPEAT, ilet);
2037 /* [we used to set otmp (by finding ilet in invent) here, but
2038 that's been moved above so that otmp can be checked earlier] */
2039 /* verify the chosen object */
2040 if (!otmp) {
2041 You("don't have that object.");
2042 if (gi.in_doagain)
2043 return (struct obj *) 0;
2044 continue;
2045 } else if (cnt < 0L || otmp->quan < cnt) {
2046 You("don't have that many! You have only %ld.", otmp->quan);
2047 if (gi.in_doagain)
2048 return (struct obj *) 0;
2049 continue;
2051 break;
2053 if (obj_ok(otmp) == GETOBJ_EXCLUDE) {
2054 silly_thing(word, otmp);
2055 return (struct obj *) 0;
2057 split_otmp:
2058 if (cntgiven) {
2059 if (cnt == 0L)
2060 return (struct obj *) 0;
2061 if (cnt != otmp->quan) {
2062 /* don't split a stack of cursed loadstones */
2063 if (splittable(otmp))
2064 otmp = splitobj(otmp, cnt);
2065 else if (otmp->otyp == LOADSTONE && otmp->cursed)
2066 /* kludge for canletgo()'s can't-drop-this message */
2067 otmp->corpsenm = (int) cnt;
2070 return otmp;
2073 DISABLE_WARNING_FORMAT_NONLITERAL
2075 void
2076 silly_thing(const char *word,
2077 struct obj *otmp)
2079 #ifdef OBSOLETE_HANDLING
2080 /* 'P','R' vs 'W','T' handling is obsolete */
2081 const char *s1, *s2, *s3;
2082 int ocls = otmp->oclass, otyp = otmp->otyp;
2084 s1 = s2 = s3 = 0;
2085 /* check for attempted use of accessory commands ('P','R') on armor
2086 and for corresponding armor commands ('W','T') on accessories */
2087 if (ocls == ARMOR_CLASS) {
2088 if (!strcmp(word, "put on"))
2089 s1 = "W", s2 = "wear", s3 = "";
2090 else if (!strcmp(word, "remove"))
2091 s1 = "T", s2 = "take", s3 = " off";
2092 } else if ((ocls == RING_CLASS || otyp == MEAT_RING)
2093 || ocls == AMULET_CLASS
2094 || (otyp == BLINDFOLD || otyp == TOWEL || otyp == LENSES)) {
2095 if (!strcmp(word, "wear"))
2096 s1 = "P", s2 = "put", s3 = " on";
2097 else if (!strcmp(word, "take off"))
2098 s1 = "R", s2 = "remove", s3 = "";
2100 if (s1)
2101 pline("Use the '%s' command to %s %s%s.", s1, s2,
2102 !(is_plural(otmp) || pair_of(otmp)) ? "that" : "those", s3);
2103 else
2104 #endif
2105 /* see comment about Amulet of Yendor in objtyp_is_callable(do_name.c);
2106 known fakes yield the silly thing feedback */
2107 if (!strcmp(word, "call")
2108 && (otmp->otyp == AMULET_OF_YENDOR
2109 || (otmp->otyp == FAKE_AMULET_OF_YENDOR && !otmp->known)))
2110 pline_The("Amulet doesn't like being called names.");
2111 else
2112 pline(silly_thing_to, word);
2115 RESTORE_WARNING_FORMAT_NONLITERAL
2117 staticfn int
2118 ckvalidcat(struct obj *otmp)
2120 /* use allow_category() from pickup.c */
2121 return (int) allow_category(otmp);
2124 staticfn int
2125 ckunpaid(struct obj *otmp)
2127 return (otmp->unpaid || (Has_contents(otmp) && count_unpaid(otmp->cobj)));
2130 boolean
2131 wearing_armor(void)
2133 return (boolean) (uarm || uarmc || uarmf || uarmg
2134 || uarmh || uarms || uarmu);
2137 boolean
2138 is_worn(struct obj *otmp)
2140 return (otmp->owornmask & (W_ARMOR | W_ACCESSORY | W_SADDLE | W_WEAPONS))
2141 ? TRUE
2142 : FALSE;
2145 /* is 'obj' being used by the hero? worn, wielded, active lamp or leash;
2146 not to be confused with obj->in_use, which finishes using up an item
2147 (destroys it) if restoring a save file finds that bit set */
2148 boolean
2149 is_inuse(struct obj *obj)
2151 return (carried(obj) && (is_worn(obj) || tool_being_used(obj)));
2154 /* extra xprname() input that askchain() can't pass through safe_qbuf() */
2155 static struct xprnctx {
2156 char let;
2157 boolean dot;
2158 } safeq_xprn_ctx;
2160 /* safe_qbuf() -> short_oname() callback */
2161 staticfn char *
2162 safeq_xprname(struct obj *obj)
2164 return xprname(obj, (char *) 0, safeq_xprn_ctx.let, safeq_xprn_ctx.dot,
2165 0L, 0L);
2168 /* alternate safe_qbuf() -> short_oname() callback */
2169 staticfn char *
2170 safeq_shortxprname(struct obj *obj)
2172 return xprname(obj, ansimpleoname(obj), safeq_xprn_ctx.let,
2173 safeq_xprn_ctx.dot, 0L, 0L);
2176 static NEARDATA const char removeables[] = { ARMOR_CLASS, WEAPON_CLASS,
2177 RING_CLASS, AMULET_CLASS,
2178 TOOL_CLASS, 0 };
2180 /* Interactive version of getobj - used for Drop, Identify, and Takeoff (A).
2181 Return the number of times fn was called successfully.
2182 If combo is TRUE, we just use this to get a category list. */
2184 ggetobj(const char *word, int (*fn)(OBJ_P), int mx,
2185 boolean combo, /* combination menu flag */
2186 unsigned *resultflags)
2188 int (*ckfn)(OBJ_P) = (int (*)(OBJ_P)) 0;
2189 boolean (*ofilter)(OBJ_P) = (boolean (*)(OBJ_P)) 0;
2190 boolean takeoff, ident, allflag, m_seen;
2191 int itemcount;
2192 int oletct, iletct, unpaid, oc_of_sym;
2193 char sym, *ip, olets[MAXOCLASSES + 6], ilets[MAXOCLASSES + 11];
2194 char extra_removeables[3 + 1]; /* uwep,uswapwep,uquiver */
2195 char buf[BUFSZ] = DUMMY, qbuf[QBUFSZ];
2197 if (!gi.invent) {
2198 You("have nothing to %s.", word);
2199 if (resultflags)
2200 *resultflags = ALL_FINISHED;
2201 return 0;
2203 if (resultflags)
2204 *resultflags = 0;
2205 takeoff = ident = allflag = m_seen = FALSE;
2206 add_valid_menu_class(0); /* reset */
2207 if (taking_off(word)) {
2208 takeoff = TRUE;
2209 ofilter = is_worn;
2210 } else if (!strcmp(word, "identify")) {
2211 ident = TRUE;
2212 ofilter = not_fully_identified;
2215 iletct = collect_obj_classes(ilets, gi.invent, FALSE, ofilter,
2216 &itemcount);
2217 unpaid = count_unpaid(gi.invent);
2219 if (ident && !iletct) {
2220 return -1; /* no further identifications */
2221 } else if (gi.invent) {
2222 ilets[iletct++] = ' ';
2223 if (unpaid)
2224 ilets[iletct++] = 'u';
2225 if (count_buc(gi.invent, BUC_BLESSED, ofilter))
2226 ilets[iletct++] = 'B';
2227 if (count_buc(gi.invent, BUC_UNCURSED, ofilter))
2228 ilets[iletct++] = 'U';
2229 if (count_buc(gi.invent, BUC_CURSED, ofilter))
2230 ilets[iletct++] = 'C';
2231 if (count_buc(gi.invent, BUC_UNKNOWN, ofilter))
2232 ilets[iletct++] = 'X';
2233 if (count_justpicked(gi.invent))
2234 ilets[iletct++] = 'P';
2235 ilets[iletct++] = 'a';
2237 ilets[iletct++] = 'i';
2238 if (!combo)
2239 ilets[iletct++] = 'm'; /* allow menu presentation on request */
2240 ilets[iletct] = '\0';
2242 for (;;) {
2243 Sprintf(qbuf, "What kinds of thing do you want to %s? [%s]",
2244 word, ilets);
2245 getlin(qbuf, buf);
2246 if (buf[0] == '\033')
2247 return 0;
2248 if (strchr(buf, 'i')) {
2249 char ailets[1+26+26+1+5+1]; /* $ + a-z + A-Z + # + slop + \0 */
2250 struct obj *otmp, *nextobj;
2252 /* applicable inventory letters; if empty, show entire invent */
2253 ailets[0] = '\0';
2254 if (ofilter)
2255 for (otmp = gi.invent; otmp; otmp = nextobj) {
2256 nextobj = otmp->nobj;
2257 /* strchr() check: limit overflow items to one '#' */
2258 if ((*ofilter)(otmp) && !strchr(ailets, otmp->invlet))
2259 (void) strkitten(ailets, otmp->invlet);
2261 if (display_inventory(ailets, TRUE) == '\033')
2262 return 0;
2263 } else
2264 break;
2267 extra_removeables[0] = '\0';
2268 if (takeoff) {
2269 /* arbitrary types of items can be placed in the weapon slots
2270 [any duplicate entries in extra_removeables[] won't matter] */
2271 if (uwep)
2272 (void) strkitten(extra_removeables, uwep->oclass);
2273 if (uswapwep)
2274 (void) strkitten(extra_removeables, uswapwep->oclass);
2275 if (uquiver)
2276 (void) strkitten(extra_removeables, uquiver->oclass);
2279 ip = buf;
2280 olets[oletct = 0] = '\0';
2281 while ((sym = *ip++) != '\0') {
2282 if (sym == ' ')
2283 continue;
2284 oc_of_sym = def_char_to_objclass(sym);
2285 if (takeoff && oc_of_sym != MAXOCLASSES) {
2286 if (strchr(extra_removeables, oc_of_sym)) {
2287 ; /* skip rest of takeoff checks */
2288 } else if (!strchr(removeables, oc_of_sym)) {
2289 pline("Not applicable.");
2290 return 0;
2291 } else if (oc_of_sym == ARMOR_CLASS && !wearing_armor()) {
2292 noarmor(FALSE);
2293 return 0;
2294 } else if (oc_of_sym == WEAPON_CLASS && !uwep && !uswapwep
2295 && !uquiver) {
2296 You("are not wielding anything.");
2297 return 0;
2298 } else if (oc_of_sym == RING_CLASS && !uright && !uleft) {
2299 You("are not wearing rings.");
2300 return 0;
2301 } else if (oc_of_sym == AMULET_CLASS && !uamul) {
2302 You("are not wearing an amulet.");
2303 return 0;
2304 } else if (oc_of_sym == TOOL_CLASS && !ublindf) {
2305 You("are not wearing a blindfold.");
2306 return 0;
2310 if (sym == 'a') {
2311 allflag = TRUE;
2312 } else if (sym == 'A') {
2313 ; /* same as the default */
2314 } else if (sym == 'u') {
2315 add_valid_menu_class('u');
2316 ckfn = ckunpaid;
2317 } else if (strchr("BUCXP", sym)) {
2318 add_valid_menu_class(sym); /* 'B','U','C','X', or 'P' */
2319 ckfn = ckvalidcat;
2320 } else if (sym == 'm') {
2321 m_seen = TRUE;
2322 } else if (oc_of_sym == MAXOCLASSES) {
2323 You("don't have any %c's.", sym);
2324 } else {
2325 if (!strchr(olets, oc_of_sym)) {
2326 add_valid_menu_class(oc_of_sym);
2327 olets[oletct++] = oc_of_sym;
2328 olets[oletct] = '\0';
2333 if (m_seen) {
2334 return (allflag
2335 || (!oletct && ckfn != ckunpaid && ckfn != ckvalidcat))
2336 ? -2 : -3;
2337 } else if (flags.menu_style != MENU_TRADITIONAL && combo && !allflag) {
2338 return 0;
2339 } else {
2340 int cnt = askchain(&gi.invent, olets, allflag, fn, ckfn, mx, word);
2342 * askchain() has already finished the job in this case
2343 * so set a special flag to convey that back to the caller
2344 * so that it won't continue processing.
2345 * Fix for bug C331-1 reported by Irina Rempt-Drijfhout.
2347 if (combo && allflag && resultflags)
2348 *resultflags |= ALL_FINISHED;
2349 return cnt;
2354 * Walk through the chain starting at objchn and ask for all objects
2355 * with olet in olets (if nonNULL) and satisfying ckfn (if nonnull)
2356 * whether the action in question (i.e., fn) has to be performed.
2359 askchain(
2360 struct obj **objchn, /* *objchn might change */
2361 const char *olets, /* olets is an Obj Class char array */
2362 int allflag, /* bypass prompting about individual items */
2363 int (*fn)(OBJ_P), /* action to perform on selected items */
2364 int (*ckfn)(OBJ_P), /* callback to decided if an item is selectable */
2365 int mx, /* if non-0, maximum number of objects to process */
2366 const char *word) /* name of the action */
2368 struct obj *otmp, *otmpo;
2369 char sym, ilet;
2370 int cnt = 0, dud = 0, tmp;
2371 boolean takeoff, nodot, ident, take_out, put_in, first, ininv, bycat;
2372 char qbuf[QBUFSZ], qpfx[QBUFSZ];
2373 Loot *sortedchn = 0;
2375 takeoff = taking_off(word);
2376 ident = !strcmp(word, "identify");
2377 take_out = !strcmp(word, "take out");
2378 put_in = !strcmp(word, "put in");
2379 nodot = (!strcmp(word, "nodot") || !strcmp(word, "drop") || ident
2380 || takeoff || take_out || put_in);
2381 ininv = (*objchn == gi.invent);
2382 bycat = (menu_class_present('u')
2383 || menu_class_present('B') || menu_class_present('U')
2384 || menu_class_present('C') || menu_class_present('X')
2385 || menu_class_present('P'));
2387 /* someday maybe we'll sort by 'olets' too (temporarily replace
2388 flags.packorder and pass SORTLOOT_PACK), but not yet... */
2389 sortedchn = sortloot(objchn, SORTLOOT_INVLET, FALSE,
2390 (boolean (*)(OBJ_P)) 0);
2392 first = TRUE;
2394 * Interrogate in the object class order specified.
2395 * For example, if a person specifies =/ then first all rings
2396 * will be asked about followed by all wands. -dgk
2398 nextclass:
2399 ilet = 'a' - 1;
2400 if (*objchn && (*objchn)->oclass == COIN_CLASS)
2401 ilet--; /* extra iteration */
2403 * Multiple Drop can change the gi.invent chain while it operates
2404 * (dropping a burning potion of oil while levitating creates
2405 * an explosion which can destroy inventory items), so simple
2406 * list traversal
2407 * for (otmp = *objchn; otmp; otmp = otmp2) {
2408 * otmp2 = otmp->nobj;
2409 * ...
2411 * is inadequate here. Use each object's bypass bit to keep
2412 * track of which list elements have already been processed.
2414 bypass_objlist(*objchn, FALSE); /* clear chain's bypass bits */
2415 while ((otmp = nxt_unbypassed_loot(sortedchn, *objchn)) != 0) {
2416 if (ilet == 'z')
2417 ilet = 'A';
2418 else if (ilet == 'Z')
2419 ilet = NOINVSYM; /* '#' */
2420 else
2421 ilet++;
2422 if (olets && *olets && otmp->oclass != *olets)
2423 continue;
2424 if (takeoff && !is_worn(otmp))
2425 continue;
2426 if (ident && !not_fully_identified(otmp))
2427 continue;
2428 if (ckfn && !(*ckfn)(otmp))
2429 continue;
2430 if (bycat && !ckvalidcat(otmp))
2431 continue;
2432 if (!allflag) {
2433 safeq_xprn_ctx.let = ilet;
2434 safeq_xprn_ctx.dot = !nodot;
2435 *qpfx = '\0';
2436 if (first) {
2437 /* traditional_loot() skips prompting when only one
2438 class of objects is involved, so prefix the first
2439 object being queried here with an explanation why */
2440 if (take_out || put_in)
2441 Sprintf(qpfx, "%s: ", word), *qpfx = highc(*qpfx);
2442 first = FALSE;
2444 (void) safe_qbuf(qbuf, qpfx, "?", otmp,
2445 ininv ? safeq_xprname : doname,
2446 ininv ? safeq_shortxprname : ansimpleoname,
2447 "item");
2448 sym = (takeoff || ident || otmp->quan < 2L) ? nyaq(qbuf)
2449 : nyNaq(qbuf);
2450 } else
2451 sym = 'y';
2453 otmpo = otmp;
2454 if (sym == '#') {
2455 /* Number was entered; split the object unless it corresponds
2456 to 'none' or 'all'. 2 special cases: cursed loadstones and
2457 welded weapons (eg, multiple daggers) will remain as merged
2458 unit; done to avoid splitting an object that won't be
2459 droppable (even if we're picking up rather than dropping). */
2460 if (!yn_number) {
2461 sym = 'n';
2462 } else {
2463 sym = 'y';
2464 if (yn_number < otmp->quan && splittable(otmp))
2465 otmp = splitobj(otmp, yn_number);
2468 switch (sym) {
2469 case 'a':
2470 allflag = 1;
2471 FALLTHROUGH;
2472 /*FALLTHRU*/
2473 case 'y':
2474 tmp = (*fn)(otmp);
2475 if (tmp <= 0) {
2476 if (container_gone(fn)) {
2477 /* otmp caused magic bag to explode;
2478 both are now gone */
2479 otmp = 0; /* and return */
2480 } else if (otmp && otmp != otmpo) {
2481 /* split occurred, merge again */
2482 (void) unsplitobj(otmp);
2484 if (tmp < 0)
2485 goto ret;
2487 cnt += tmp;
2488 if (--mx == 0)
2489 goto ret;
2490 FALLTHROUGH;
2491 /*FALLTHRU*/
2492 case 'n':
2493 if (nodot)
2494 dud++;
2495 FALLTHROUGH;
2496 /*FALLTHRU*/
2497 default:
2498 break;
2499 case 'q':
2500 /* special case for seffects() */
2501 if (ident)
2502 cnt = -1;
2503 goto ret;
2506 if (olets && *olets && *++olets)
2507 goto nextclass;
2509 if (!takeoff && (dud || cnt))
2510 pline("That was all.");
2511 else if (!dud && !cnt)
2512 pline("No applicable objects.");
2513 ret:
2514 unsortloot(&sortedchn);
2515 /* can't just clear bypass bit of items in objchn because the action
2516 applied to selected ones might move them to a different chain */
2517 /*bypass_objlist(*objchn, FALSE);*/
2518 clear_bypasses();
2519 return cnt;
2523 * Object identification routines:
2526 /* set the cknown and lknown flags on an object if they're applicable */
2527 void
2528 set_cknown_lknown(struct obj *obj)
2530 if (Is_container(obj) || obj->otyp == STATUE)
2531 obj->cknown = obj->lknown = 1;
2532 else if (obj->otyp == TIN)
2533 obj->cknown = 1;
2534 /* TODO? cknown might be extended to candy bar, where it would mean that
2535 wrapper's text was known which in turn indicates candy bar's content */
2536 return;
2539 /* make an object actually be identified; no display updating */
2540 void
2541 fully_identify_obj(struct obj *otmp)
2543 makeknown(otmp->otyp);
2544 if (otmp->oartifact)
2545 discover_artifact((xint16) otmp->oartifact);
2546 otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1;
2547 set_cknown_lknown(otmp); /* set otmp->{cknown,lknown} if applicable */
2548 if (otmp->otyp == EGG && otmp->corpsenm != NON_PM)
2549 learn_egg_type(otmp->corpsenm);
2552 /* ggetobj callback routine; identify an object and give immediate feedback */
2554 identify(struct obj *otmp)
2556 fully_identify_obj(otmp);
2557 prinv((char *) 0, otmp, 0L);
2558 return 1;
2561 /* menu of unidentified objects; select and identify up to id_limit of them */
2562 staticfn void
2563 menu_identify(int id_limit)
2565 menu_item *pick_list;
2566 int n, i, first = 1, tryct = 5;
2567 char buf[BUFSZ];
2568 /* assumptions: id_limit > 0 and at least one unID'd item is present */
2570 while (id_limit) {
2571 Sprintf(buf, "What would you like to identify %s?",
2572 first ? "first" : "next");
2573 n = query_objlist(buf, &gi.invent, (SIGNAL_NOMENU | SIGNAL_ESCAPE
2574 | USE_INVLET | INVORDER_SORT),
2575 &pick_list, PICK_ANY, not_fully_identified);
2577 if (n > 0) {
2578 if (n > id_limit)
2579 n = id_limit;
2580 for (i = 0; i < n; i++, id_limit--)
2581 (void) identify(pick_list[i].item.a_obj);
2582 free((genericptr_t) pick_list);
2583 if (id_limit)
2584 wait_synch(); /* Before we loop to pop open another menu */
2585 first = 0;
2586 } else if (n == -2) { /* player used ESC to quit menu */
2587 break;
2588 } else if (n == -1) { /* no eligible items found */
2589 pline("That was all.");
2590 break;
2591 } else if (!--tryct) { /* stop re-prompting */
2592 pline1(thats_enough_tries);
2593 break;
2594 } else { /* try again */
2595 pline("Choose an item; use ESC to decline.");
2599 /* count the unidentified items */
2601 count_unidentified(struct obj *objchn)
2603 int unid_cnt = 0;
2604 struct obj *obj;
2606 for (obj = objchn; obj; obj = obj->nobj)
2607 if (not_fully_identified(obj))
2608 ++unid_cnt;
2609 return unid_cnt;
2612 /* dialog with user to identify a given number of items; 0 means all */
2613 void
2614 identify_pack(
2615 int id_limit,
2616 boolean learning_id) /* T: just read unknown identify scroll */
2618 struct obj *obj;
2619 int n, unid_cnt = count_unidentified(gi.invent);
2621 if (!unid_cnt) {
2622 You("have already identified %s of your possessions.",
2623 !learning_id ? "all" : "the rest");
2624 } else if (!id_limit || id_limit >= unid_cnt) {
2625 /* identify everything */
2626 /* TODO: use fully_identify_obj and cornline/menu/whatever here */
2627 for (obj = gi.invent; obj; obj = obj->nobj) {
2628 if (not_fully_identified(obj)) {
2629 (void) identify(obj);
2630 if (--unid_cnt < 1)
2631 break;
2634 } else {
2635 /* identify up to `id_limit' items */
2636 n = 0;
2637 if (flags.menu_style == MENU_TRADITIONAL)
2638 do {
2639 n = ggetobj("identify", identify, id_limit, FALSE,
2640 (unsigned *) 0);
2641 if (n < 0)
2642 break; /* quit or no eligible items */
2643 } while ((id_limit -= n) > 0);
2644 if (n == 0 || n < -1)
2645 menu_identify(id_limit);
2647 update_inventory();
2650 /* called when regaining sight; mark inventory objects which were picked
2651 up while blind as now having been seen */
2652 void
2653 learn_unseen_invent(void)
2655 struct obj *otmp;
2656 boolean invupdated = FALSE;
2658 if (Blind)
2659 return; /* sanity check */
2661 for (otmp = gi.invent; otmp; otmp = otmp->nobj) {
2662 if (otmp->dknown && (otmp->bknown || !Role_if(PM_CLERIC)))
2663 continue; /* already seen */
2664 invupdated = TRUE;
2665 /* xname() will set dknown, perhaps bknown (for priest[ess]);
2666 result from xname() is immediately released for re-use */
2667 maybereleaseobuf(xname(otmp));
2669 * If object->eknown gets implemented (see learnwand(zap.c)),
2670 * handle deferred discovery here.
2673 if (invupdated)
2674 update_inventory();
2677 /* persistent inventory window is maintained by interface code;
2678 'update_inventory' used to be a macro for
2679 (*windowprocs.win_update_inventory) but the restore hackery to suppress
2680 screen updates was getting out of hand; this is now a central call point */
2681 void
2682 update_inventory(void)
2684 int save_suppress_price;
2686 if (!program_state.in_moveloop) /* not covered by suppress_map_output */
2687 return;
2688 if (suppress_map_output()) /* despite name, used for perm_invent too */
2689 return;
2692 * Ought to check (windowprocs.wincap & WC_PERM_INVENT) here....
2694 * We currently don't skip this call when iflags.perm_invent is False
2695 * because curses uses that to disable a previous perm_invent window
2696 * (after toggle via 'O'; perhaps the options code should handle that).
2698 * perm_invent might get updated while some code is avoiding price
2699 * feedback during obj name formatting for messages. Temporarily
2700 * force 'normal' formatting during the perm_invent update. (Cited
2701 * example was an update triggered by change in invent gold when
2702 * transferring some to shk during itemized billing. A previous fix
2703 * attempt in the shop code handled it for unpaid items but not for
2704 * paying for used-up shop items; that follows a different code path.)
2706 save_suppress_price = iflags.suppress_price;
2707 iflags.suppress_price = 0;
2708 (*windowprocs.win_update_inventory)(0);
2709 iflags.suppress_price = save_suppress_price;
2712 /* the #perminv command - call interface's persistent inventory routine */
2714 doperminv(void)
2717 * If persistent inventory window is enabled, interact with it.
2719 * Depending on interface, might accept and execute one scrolling
2720 * request (MENU_{FIRST,NEXT,PREVIOUS,LAST}_PAGE) then return,
2721 * or might stay and handle multiple requests until user finishes
2722 * (typically by typing <return> or <esc> but that's up to interface).
2725 #if 0
2726 /* [currently this would redraw the persistent inventory window
2727 whether that's needed or not, so also reset any previous
2728 scrolling; we don't want that if the interface only accepts
2729 one scroll command at a time] */
2730 update_inventory(); /* make sure that it's up to date */
2731 #endif
2733 if ((windowprocs.wincap & WC_PERM_INVENT) == 0) {
2734 /* [TODO? perhaps omit "by <interface>" if all the window ports
2735 compiled into this binary lack support for perm_invent...] */
2736 pline("Persistent inventory display is not supported by '%s'.",
2737 windowprocs.name);
2739 } else if (!iflags.perm_invent) {
2740 pline(
2741 "Persistent inventory ('perm_invent' option) is not presently enabled.");
2743 } else if (!gi.invent) {
2744 /* [should this be left for the interface to decide?] */
2745 pline("Persistent inventory display is empty.");
2747 } else {
2748 /* note: we used to request a scrolling key here and pass that to
2749 (*win_update_inventory)(key), but that limited the functionality
2750 and also cluttered message history with prompt and response so
2751 just send non-zero and have the interface be responsible for it */
2752 (*windowprocs.win_update_inventory)(1);
2754 } /* iflags.perm_invent */
2756 return ECMD_OK;
2759 /* should of course only be called for things in invent */
2760 staticfn char
2761 obj_to_let(struct obj *obj)
2763 if (!flags.invlet_constant) {
2764 obj->invlet = NOINVSYM;
2765 reassign();
2767 return obj->invlet;
2771 * Print the indicated quantity of the given object. If quan == 0L then use
2772 * the current quantity.
2774 void
2775 prinv(const char *prefix, struct obj *obj, long quan)
2777 boolean total_of = (quan && (quan < obj->quan));
2778 char totalbuf[QBUFSZ];
2780 if (!prefix)
2781 prefix = "";
2783 totalbuf[0] = '\0';
2784 if (total_of)
2785 Snprintf(totalbuf, sizeof totalbuf,
2786 " (%ld in total).", obj->quan);
2787 pline("%s%s%s%s", prefix, *prefix ? " " : "",
2788 xprname(obj, (char *) 0, obj_to_let(obj), !total_of, 0L, quan),
2789 flags.verbose ? totalbuf : "");
2792 DISABLE_WARNING_FORMAT_NONLITERAL
2794 char *
2795 xprname(
2796 struct obj *obj,
2797 const char *txt, /* text to print instead of obj */
2798 char let, /* inventory letter */
2799 boolean dot, /* append period; (dot && cost => Iu) */
2800 long cost, /* cost (for inventory of unpaid or expended items) */
2801 long quan) /* if non-0, print this quantity, not obj->quan */
2803 static char li[BUFSZ];
2804 char suffix[80]; /* plenty of room for count and hallucinatory currency */
2805 int sfxlen, txtlen; /* signed int for %*s formatting */
2806 const char *fmt;
2807 boolean use_invlet = (flags.invlet_constant && obj != NULL
2808 && let != CONTAINED_SYM && let != HANDS_SYM);
2809 long savequan = 0L;
2811 if (quan && obj) {
2812 savequan = obj->quan;
2813 obj->quan = quan;
2816 * If let is:
2817 * - Then obj == null and 'txt' refers to hands or fingers.
2818 * * Then obj == null and we are printing a total amount.
2819 * > Then the object is contained and doesn't have an inventory letter.
2821 fmt = "%c - %.*s%s";
2822 if (!txt) {
2823 assert(obj != NULL);
2824 txt = doname(obj);
2826 txtlen = (int) strlen(txt);
2828 if (cost != 0L || let == '*') {
2829 /* if dot is true, we're doing Iu, otherwise Ix */
2830 if (dot && use_invlet)
2831 let = obj->invlet;
2832 Sprintf(suffix, "%c%6ld %.50s", iflags.menu_tab_sep ? '\t' : ' ',
2833 cost, currency(cost));
2834 if (!iflags.menu_tab_sep) {
2835 fmt = "%c - %-45.*s%s";
2836 if (txtlen < 45)
2837 txtlen = 45;
2839 } else {
2840 /* ordinary inventory display or pickup message */
2841 if (use_invlet)
2842 let = obj->invlet;
2843 Strcpy(suffix, dot ? "." : "");
2845 sfxlen = (int) strlen(suffix);
2846 if (txtlen > BUFSZ - 1 - (4 + sfxlen)) /* 4: "c - " prefix */
2847 txtlen = BUFSZ - 1 - (4 + sfxlen);
2848 Sprintf(li, fmt, let, txtlen, txt, suffix);
2850 if (savequan)
2851 obj->quan = savequan;
2853 return li;
2856 RESTORE_WARNING_FORMAT_NONLITERAL
2858 enum item_action_actions {
2859 IA_NONE = 0,
2860 IA_UNWIELD, /* hack for 'w-' */
2861 IA_APPLY_OBJ, /* 'a' */
2862 IA_DIP_OBJ, /* 'a' on a potion == dip */
2863 IA_NAME_OBJ, /* 'c' name individual item */
2864 IA_NAME_OTYP, /* 'C' name item's type */
2865 IA_DROP_OBJ, /* 'd' */
2866 IA_EAT_OBJ, /* 'e' */
2867 IA_ENGRAVE_OBJ, /* 'E' */
2868 IA_FIRE_OBJ, /* 'f' */
2869 IA_ADJUST_OBJ, /* 'i' #adjust inventory letter */
2870 IA_ADJUST_STACK, /* 'I' #adjust with count to split stack */
2871 IA_SACRIFICE, /* 'O' offer sacrifice */
2872 IA_BUY_OBJ, /* 'p' pay shopkeeper */
2873 IA_QUAFF_OBJ,
2874 IA_QUIVER_OBJ,
2875 IA_READ_OBJ,
2876 IA_RUB_OBJ,
2877 IA_THROW_OBJ,
2878 IA_TAKEOFF_OBJ,
2879 IA_TIP_CONTAINER,
2880 IA_INVOKE_OBJ,
2881 IA_WIELD_OBJ,
2882 IA_WEAR_OBJ,
2883 IA_SWAPWEAPON,
2884 IA_TWOWEAPON,
2885 IA_ZAP_OBJ,
2886 IA_WHATIS_OBJ, /* '/' specify inventory object */
2889 /* construct text for the menu entries for IA_NAME_OBJ and IA_NAME_OTYP */
2890 staticfn boolean
2891 item_naming_classification(
2892 struct obj *obj,
2893 char *onamebuf,
2894 char *ocallbuf)
2896 static const char
2897 Name[] = "Name",
2898 Rename[] = "Rename or un-name",
2899 Call[] = "Call",
2900 /* "re-call" seems a bit weird, but "recall" and
2901 "rename" don't fit for changing a type name */
2902 Recall[] = "Re-call or un-call";
2904 onamebuf[0] = ocallbuf[0] = '\0';
2905 if (name_ok(obj) == GETOBJ_SUGGEST) {
2906 Sprintf(onamebuf, "%s %s %s",
2907 (!has_oname(obj) || !*ONAME(obj)) ? Name : Rename,
2908 the_unique_obj(obj) ? "the"
2909 : !is_plural(obj) ? "this specific"
2910 : "this stack of", /*"these",*/
2911 simpleonames(obj));
2913 if (call_ok(obj) == GETOBJ_SUGGEST) {
2914 char *callname = simpleonames(obj);
2916 /* prefix known unique item with "the", make all other types plural */
2917 if (the_unique_obj(obj)) /* treats unID'd fake amulets as if real */
2918 callname = the(callname);
2919 else if (!is_plural(obj))
2920 callname = makeplural(callname);
2921 Sprintf(ocallbuf, "%s the type for %s",
2922 (!objects[obj->otyp].oc_uname
2923 || !*objects[obj->otyp].oc_uname) ? Call : Recall,
2924 callname);
2926 return (*onamebuf || *ocallbuf) ? TRUE : FALSE;
2929 /* construct text for the menu entries for IA_READ_OBJ */
2930 staticfn int
2931 item_reading_classification(struct obj *obj, char *outbuf)
2933 int otyp = obj->otyp, res = IA_READ_OBJ;
2935 *outbuf = '\0';
2936 if (otyp == FORTUNE_COOKIE) {
2937 Strcpy(outbuf, "Read the message inside this cookie");
2938 } else if (otyp == T_SHIRT) {
2939 Strcpy(outbuf, "Read the slogan on the shirt");
2940 } else if (otyp == ALCHEMY_SMOCK) {
2941 Strcpy(outbuf, "Read the slogan on the apron");
2942 } else if (otyp == HAWAIIAN_SHIRT) {
2943 Strcpy(outbuf, "Look at the pattern on the shirt");
2944 } else if (obj->oclass == SCROLL_CLASS) {
2945 const char *magic = ((obj->dknown
2946 #ifdef MAIL_STRUCTURES
2947 && otyp != SCR_MAIL
2948 #endif
2949 && (otyp != SCR_BLANK_PAPER
2950 || !objects[otyp].oc_name_known))
2951 ? " to activate its magic" : "");
2953 Sprintf(outbuf, "Read this scroll%s", magic);
2954 } else if (obj->oclass == SPBOOK_CLASS) {
2955 boolean novel = (otyp == SPE_NOVEL),
2956 blank = (otyp == SPE_BLANK_PAPER
2957 && objects[otyp].oc_name_known),
2958 tome = (otyp == SPE_BOOK_OF_THE_DEAD
2959 && objects[otyp].oc_name_known);
2961 Sprintf(outbuf, "%s this %s",
2962 (novel || blank) ? "Read" : tome ? "Examine" : "Study",
2963 novel ? simpleonames(obj) /* "novel" or "paperback book" */
2964 : tome ? "tome" : "spellbook");
2965 } else {
2966 res = IA_NONE;
2968 return res;
2971 staticfn void
2972 ia_addmenu(winid win, int act, char let, const char *txt)
2974 anything any;
2975 int clr = NO_COLOR;
2977 any = cg.zeroany;
2978 any.a_int = act;
2979 add_menu(win, &nul_glyphinfo, &any, let, 0,
2980 ATR_NONE, clr, txt, MENU_ITEMFLAGS_NONE);
2983 staticfn void
2984 itemactions_pushkeys(struct obj *otmp, int act)
2986 switch (act) {
2987 default:
2988 impossible("Unknown item action");
2989 break;
2990 case IA_NONE:
2991 break;
2992 case IA_UNWIELD:
2993 cmdq_add_ec(CQ_CANNED, (otmp == uwep) ? dowield
2994 : (otmp == uswapwep) ? remarm_swapwep
2995 : (otmp == uquiver) ? dowieldquiver
2996 : donull); /* can't happen */
2997 cmdq_add_key(CQ_CANNED, '-');
2998 break;
2999 case IA_APPLY_OBJ:
3000 cmdq_add_ec(CQ_CANNED, doapply);
3001 cmdq_add_key(CQ_CANNED, otmp->invlet);
3002 break;
3003 case IA_DIP_OBJ:
3004 /* #altdip instead of normal #dip - takes potion to dip into
3005 first (the inventory item instigating this) and item to
3006 be dipped second, also ignores floor features such as
3007 fountain/sink so we don't need to force m-prefix here */
3008 cmdq_add_ec(CQ_CANNED, dip_into);
3009 cmdq_add_key(CQ_CANNED, otmp->invlet);
3010 break;
3011 case IA_NAME_OBJ:
3012 case IA_NAME_OTYP:
3013 cmdq_add_ec(CQ_CANNED, docallcmd);
3014 cmdq_add_key(CQ_CANNED, (act == IA_NAME_OBJ) ? 'i' : 'o');
3015 cmdq_add_key(CQ_CANNED, otmp->invlet);
3016 break;
3017 case IA_DROP_OBJ:
3018 cmdq_add_ec(CQ_CANNED, dodrop);
3019 cmdq_add_key(CQ_CANNED, otmp->invlet);
3020 break;
3021 case IA_EAT_OBJ:
3022 /* start with m-prefix; for #eat, it means ignore floor food
3023 if present and eat food from invent */
3024 cmdq_add_ec(CQ_CANNED, do_reqmenu);
3025 cmdq_add_ec(CQ_CANNED, doeat);
3026 cmdq_add_key(CQ_CANNED, otmp->invlet);
3027 break;
3028 case IA_ENGRAVE_OBJ:
3029 cmdq_add_ec(CQ_CANNED, doengrave);
3030 cmdq_add_key(CQ_CANNED, otmp->invlet);
3031 break;
3032 case IA_FIRE_OBJ:
3033 cmdq_add_ec(CQ_CANNED, dofire);
3034 break;
3035 case IA_ADJUST_OBJ:
3036 cmdq_add_ec(CQ_CANNED, doorganize); /* #adjust */
3037 cmdq_add_key(CQ_CANNED, otmp->invlet);
3038 break;
3039 case IA_ADJUST_STACK:
3040 cmdq_add_ec(CQ_CANNED, adjust_split); /* #altadjust */
3041 cmdq_add_key(CQ_CANNED, otmp->invlet);
3042 break;
3043 case IA_SACRIFICE:
3044 cmdq_add_ec(CQ_CANNED, dosacrifice);
3045 cmdq_add_key(CQ_CANNED, otmp->invlet);
3046 break;
3047 case IA_BUY_OBJ:
3048 cmdq_add_ec(CQ_CANNED, dopay);
3049 cmdq_add_key(CQ_CANNED, otmp->invlet);
3050 break;
3051 case IA_QUAFF_OBJ:
3052 /* start with m-prefix; for #quaff, it means ignore fountain
3053 or sink if present and drink a potion from invent */
3054 cmdq_add_ec(CQ_CANNED, do_reqmenu);
3055 cmdq_add_ec(CQ_CANNED, dodrink);
3056 cmdq_add_key(CQ_CANNED, otmp->invlet);
3057 break;
3058 case IA_QUIVER_OBJ:
3059 cmdq_add_ec(CQ_CANNED, dowieldquiver);
3060 cmdq_add_key(CQ_CANNED, otmp->invlet);
3061 break;
3062 case IA_READ_OBJ:
3063 cmdq_add_ec(CQ_CANNED, doread);
3064 cmdq_add_key(CQ_CANNED, otmp->invlet);
3065 break;
3066 case IA_RUB_OBJ:
3067 cmdq_add_ec(CQ_CANNED, dorub);
3068 cmdq_add_key(CQ_CANNED, otmp->invlet);
3069 break;
3070 case IA_THROW_OBJ:
3071 cmdq_add_ec(CQ_CANNED, dothrow);
3072 cmdq_add_key(CQ_CANNED, otmp->invlet);
3073 break;
3074 case IA_TAKEOFF_OBJ:
3075 cmdq_add_ec(CQ_CANNED, dotakeoff);
3076 cmdq_add_key(CQ_CANNED, otmp->invlet);
3077 break;
3078 case IA_TIP_CONTAINER:
3079 /* start with m-prefix to skip floor containers;
3080 for menustyle:Traditional when more than one floor
3081 container is present, player will get a #tip menu and
3082 have to pick the "tip something being carried" choice,
3083 then this item will be already chosen from inventory;
3084 suboptimal but possibly an acceptable tradeoff since
3085 combining item actions with use of traditional ggetobj()
3086 is an unlikely scenario */
3087 cmdq_add_ec(CQ_CANNED, do_reqmenu);
3088 cmdq_add_ec(CQ_CANNED, dotip);
3089 cmdq_add_key(CQ_CANNED, otmp->invlet);
3090 break;
3091 case IA_INVOKE_OBJ:
3092 cmdq_add_ec(CQ_CANNED, doinvoke);
3093 cmdq_add_key(CQ_CANNED, otmp->invlet);
3094 break;
3095 case IA_WIELD_OBJ:
3096 cmdq_add_ec(CQ_CANNED, dowield);
3097 cmdq_add_key(CQ_CANNED, otmp->invlet);
3098 break;
3099 case IA_WEAR_OBJ:
3100 cmdq_add_ec(CQ_CANNED, dowear);
3101 cmdq_add_key(CQ_CANNED, otmp->invlet);
3102 break;
3103 case IA_SWAPWEAPON:
3104 cmdq_add_ec(CQ_CANNED, doswapweapon);
3105 break;
3106 case IA_TWOWEAPON:
3107 cmdq_add_ec(CQ_CANNED, dotwoweapon);
3108 break;
3109 case IA_ZAP_OBJ:
3110 cmdq_add_ec(CQ_CANNED, dozap);
3111 cmdq_add_key(CQ_CANNED, otmp->invlet);
3112 break;
3113 case IA_WHATIS_OBJ:
3114 cmdq_add_ec(CQ_CANNED, dowhatis); /* "/" command */
3115 cmdq_add_key(CQ_CANNED, 'i'); /* "i" == item from inventory */
3116 cmdq_add_key(CQ_CANNED, otmp->invlet);
3117 break;
3121 /* Show menu of possible actions hero could do with item otmp */
3122 staticfn int
3123 itemactions(struct obj *otmp)
3125 int n, act = IA_NONE;
3126 winid win;
3127 char buf[BUFSZ], buf2[BUFSZ];
3128 menu_item *selected;
3129 struct monst *mtmp;
3130 const char *light = otmp->lamplit ? "Extinguish" : "Light";
3131 boolean already_worn = (otmp->owornmask & (W_ARMOR | W_ACCESSORY)) != 0;
3133 win = create_nhwindow(NHW_MENU);
3134 start_menu(win, MENU_BEHAVE_STANDARD);
3136 /* -: unwield; picking current weapon offers an opportunity for 'w-'
3137 to wield bare/gloved hands; likewise for 'Q-' with quivered item(s) */
3138 if (otmp == uwep || otmp == uswapwep || otmp == uquiver) {
3139 const char *verb = (otmp == uquiver) ? "Quiver" : "Wield",
3140 *action = (otmp == uquiver) ? "un-ready" : "un-wield",
3141 *which = is_plural(otmp) ? "these" : "this",
3142 *what = ((otmp->oclass == WEAPON_CLASS || is_weptool(otmp))
3143 ? "weapon" : "item");
3145 * TODO: if uwep is ammo, tell player that to shoot instead of toss,
3146 * the corresponding launcher must be wielded;
3148 Sprintf(buf, "%s '%c' to %s %s %s",
3149 verb, HANDS_SYM, action, which,
3150 is_plural(otmp) ? makeplural(what) : what);
3151 ia_addmenu(win, IA_UNWIELD, '-', buf);
3154 /* a: apply */
3155 if (otmp->oclass == COIN_CLASS)
3156 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Flip a coin");
3157 else if (otmp->otyp == CREAM_PIE)
3158 ia_addmenu(win, IA_APPLY_OBJ, 'a',
3159 "Hit yourself with this cream pie");
3160 else if (otmp->otyp == BULLWHIP)
3161 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Lash out with this whip");
3162 else if (otmp->otyp == GRAPPLING_HOOK)
3163 ia_addmenu(win, IA_APPLY_OBJ, 'a',
3164 "Grapple something with this hook");
3165 else if (otmp->otyp == BAG_OF_TRICKS && objects[otmp->otyp].oc_name_known)
3166 /* bag of tricks skips this unless discovered */
3167 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Reach into this bag");
3168 else if (Is_container(otmp))
3169 /* bag of tricks gets here only if not yet discovered */
3170 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Open this container");
3171 else if (otmp->otyp == CAN_OF_GREASE)
3172 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use the can to grease an item");
3173 else if (otmp->otyp == LOCK_PICK
3174 || otmp->otyp == CREDIT_CARD
3175 || otmp->otyp == SKELETON_KEY)
3176 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this tool to pick a lock");
3177 else if (otmp->otyp == TINNING_KIT)
3178 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this kit to tin a corpse");
3179 else if (otmp->otyp == LEASH)
3180 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Tie a pet to this leash");
3181 else if (otmp->otyp == SADDLE)
3182 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Place this saddle on a pet");
3183 else if (otmp->otyp == MAGIC_WHISTLE
3184 || otmp->otyp == TIN_WHISTLE)
3185 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Blow this whistle");
3186 else if (otmp->otyp == EUCALYPTUS_LEAF)
3187 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this leaf as a whistle");
3188 else if (otmp->otyp == STETHOSCOPE)
3189 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Listen through the stethoscope");
3190 else if (otmp->otyp == MIRROR)
3191 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Show something its reflection");
3192 else if (otmp->otyp == BELL || otmp->otyp == BELL_OF_OPENING)
3193 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Ring the bell");
3194 else if (otmp->otyp == CANDELABRUM_OF_INVOCATION) {
3195 Sprintf(buf, "%s the candelabrum", light);
3196 ia_addmenu(win, IA_APPLY_OBJ, 'a', buf);
3197 } else if (otmp->otyp == WAX_CANDLE || otmp->otyp == TALLOW_CANDLE) {
3198 Sprintf(buf, "%s %s %s", light,
3199 is_plural(otmp) ? "these" : "this",
3200 simpleonames(otmp));
3201 ia_addmenu(win, IA_APPLY_OBJ, 'a', buf);
3202 } else if (otmp->otyp == OIL_LAMP || otmp->otyp == MAGIC_LAMP
3203 || otmp->otyp == BRASS_LANTERN) {
3204 Sprintf(buf, "%s this light source", light);
3205 ia_addmenu(win, IA_APPLY_OBJ, 'a', buf);
3206 } else if (otmp->otyp == POT_OIL && objects[otmp->otyp].oc_name_known) {
3207 Sprintf(buf, "%s this oil", light);
3208 ia_addmenu(win, IA_APPLY_OBJ, 'a', buf);
3209 } else if (otmp->oclass == POTION_CLASS) {
3210 /* FIXME? this should probably be moved to 'D' rather than be 'a' */
3211 Sprintf(buf, "Dip something into %s potion%s",
3212 is_plural(otmp) ? "one of these" : "this", plur(otmp->quan));
3213 ia_addmenu(win, IA_DIP_OBJ, 'a', buf);
3214 } else if (otmp->otyp == EXPENSIVE_CAMERA)
3215 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Take a photograph");
3216 else if (otmp->otyp == TOWEL)
3217 ia_addmenu(win, IA_APPLY_OBJ, 'a',
3218 "Clean yourself off with this towel");
3219 else if (otmp->otyp == CRYSTAL_BALL)
3220 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Peer into this crystal ball");
3221 else if (otmp->otyp == MAGIC_MARKER)
3222 ia_addmenu(win, IA_APPLY_OBJ, 'a',
3223 "Write on something with this marker");
3224 else if (otmp->otyp == FIGURINE)
3225 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Make this figurine transform");
3226 else if (otmp->otyp == UNICORN_HORN)
3227 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this unicorn horn");
3228 else if (otmp->otyp == HORN_OF_PLENTY
3229 && objects[otmp->otyp].oc_name_known)
3230 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Blow into the horn of plenty");
3231 else if (otmp->otyp >= WOODEN_FLUTE && otmp->otyp <= DRUM_OF_EARTHQUAKE)
3232 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Play this musical instrument");
3233 else if (otmp->otyp == LAND_MINE || otmp->otyp == BEARTRAP)
3234 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Arm this trap");
3235 else if (otmp->otyp == PICK_AXE || otmp->otyp == DWARVISH_MATTOCK)
3236 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Dig with this digging tool");
3237 else if (otmp->oclass == WAND_CLASS)
3238 ia_addmenu(win, IA_APPLY_OBJ, 'a', "Break this wand");
3240 /* 'c', 'C' - call an item or its type something */
3241 if (item_naming_classification(otmp, buf, buf2)) {
3242 if (*buf)
3243 ia_addmenu(win, IA_NAME_OBJ, 'c', buf);
3244 if (*buf2)
3245 ia_addmenu(win, IA_NAME_OTYP, 'C', buf2);
3248 /* d: drop item, works on everything except worn items; those will
3249 always have a takeoff/remove choice so we don't have to worry
3250 about the menu maybe being empty when 'd' is suppressed */
3251 if (!already_worn) {
3252 Sprintf(buf, "Drop this %s", (otmp->quan > 1L) ? "stack" : "item");
3253 ia_addmenu(win, IA_DROP_OBJ, 'd', buf);
3256 /* e: eat item */
3257 if (otmp->otyp == TIN) {
3258 Sprintf(buf, "Open %s%s and eat the contents",
3259 (otmp->quan > 1L) ? "one of these tins" : "this tin",
3260 (otmp->otyp == TIN && uwep && uwep->otyp == TIN_OPENER)
3261 ? " with your tin opener" : "");
3262 ia_addmenu(win, IA_EAT_OBJ, 'e', buf);
3263 } else if (is_edible(otmp)) {
3264 Sprintf(buf, "Eat %s", (otmp->quan > 1L) ? "one of these" : "this");
3265 ia_addmenu(win, IA_EAT_OBJ, 'e', buf);
3268 /* E: engrave with item */
3269 if (otmp->otyp == TOWEL) {
3270 ia_addmenu(win, IA_ENGRAVE_OBJ, 'E',
3271 "Wipe the floor with this towel");
3272 } else if (otmp->otyp == MAGIC_MARKER) {
3273 ia_addmenu(win, IA_ENGRAVE_OBJ, 'E',
3274 "Scribble graffiti on the floor");
3275 } else if (otmp->oclass == WEAPON_CLASS || otmp->oclass == WAND_CLASS
3276 || otmp->oclass == GEM_CLASS || otmp->oclass == RING_CLASS) {
3277 Sprintf(buf, "%s on the %s with %s",
3278 (is_blade(otmp) || otmp->oclass == WAND_CLASS
3279 || ((otmp->oclass == GEM_CLASS || otmp->oclass == RING_CLASS)
3280 && objects[otmp->otyp].oc_tough)) ? "Engrave" : "Write",
3281 surface(u.ux, u.uy),
3282 (otmp->quan > 1L) ? "one of these items" : "this item");
3283 ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', buf);
3286 /* f: fire quivered ammo */
3287 if (otmp == uquiver) {
3288 boolean shoot = ammo_and_launcher(otmp, uwep);
3290 /* FIXME: see the multi-shot FIXME about "one of" for 't: throw' */
3291 Sprintf(buf, "%s %s", shoot ? "Shoot" : "Throw",
3292 (otmp->quan > 1L) ? "one of these" : "this");
3293 if (shoot) {
3294 assert(uwep != NULL);
3295 Sprintf(eos(buf), " with your wielded %s", simpleonames(uwep));
3297 ia_addmenu(win, IA_FIRE_OBJ, 'f', buf);
3300 /* i: #adjust inventory letter; gold can't be adjusted unless there
3301 is some in a slot other than '$' (which shouldn't be possible) */
3302 if (otmp->oclass != COIN_CLASS || check_invent_gold("item-action"))
3303 ia_addmenu(win, IA_ADJUST_OBJ, 'i',
3304 "Adjust inventory by assigning new letter");
3305 /* I: #adjust inventory item by splitting its stack */
3306 if (otmp->quan > 1L && otmp->oclass != COIN_CLASS)
3307 ia_addmenu(win, IA_ADJUST_STACK, 'I',
3308 "Adjust inventory by splitting this stack");
3310 /* O: offer sacrifice */
3311 if (IS_ALTAR(levl[u.ux][u.uy].typ) && !u.uswallow) {
3312 /* FIXME: this doesn't match #offer's likely candidates, which don't
3313 include corpses on Astral and don't include amulets off Astral */
3314 if (otmp->otyp == CORPSE)
3315 ia_addmenu(win, IA_SACRIFICE, 'O',
3316 "Offer this corpse as a sacrifice at this altar");
3317 else if (otmp->otyp == AMULET_OF_YENDOR
3318 || otmp->otyp == FAKE_AMULET_OF_YENDOR)
3319 ia_addmenu(win, IA_SACRIFICE, 'O',
3320 "Offer this amulet as a sacrifice at this altar");
3323 /* p: pay for unpaid utems */
3324 if (otmp->unpaid
3325 /* FIXME: should also handle player owned container (so not
3326 flagged 'unpaid') holding shop owned items */
3327 && (mtmp = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE))) != 0
3328 && inhishop(mtmp)) {
3329 Sprintf(buf, "Buy this unpaid %s",
3330 (otmp->quan > 1L) ? "stack" : "item");
3331 ia_addmenu(win, IA_BUY_OBJ, 'p', buf);
3334 /* P: put on accessory */
3335 if (!already_worn) {
3336 if (otmp->oclass == RING_CLASS || otmp->otyp == MEAT_RING)
3337 ia_addmenu(win, IA_WEAR_OBJ, 'P', "Put this ring on");
3338 else if (otmp->oclass == AMULET_CLASS)
3339 ia_addmenu(win, IA_WEAR_OBJ, 'P', "Put this amulet on");
3340 else if (otmp->otyp == TOWEL || otmp->otyp == BLINDFOLD)
3341 ia_addmenu(win, IA_WEAR_OBJ, 'P',
3342 "Use this to blindfold yourself");
3343 else if (otmp->otyp == LENSES)
3344 ia_addmenu(win, IA_WEAR_OBJ, 'P', "Put these lenses on");
3347 /* q: drink item */
3348 if (otmp->oclass == POTION_CLASS) {
3349 Sprintf(buf, "Quaff (drink) %s",
3350 (otmp->quan > 1L) ? "one of these potions" : "this potion");
3351 ia_addmenu(win, IA_QUAFF_OBJ, 'q', buf);
3354 /* Q: quiver throwable item */
3355 if ((otmp->oclass == GEM_CLASS || otmp->oclass == WEAPON_CLASS)
3356 && otmp != uquiver) {
3357 Sprintf(buf, "Quiver this %s for easy %s with \'f\'ire",
3358 (otmp->quan > 1L) ? "stack" : "item",
3359 ammo_and_launcher(otmp, uwep) ? "shooting" : "throwing");
3360 ia_addmenu(win, IA_QUIVER_OBJ, 'Q', buf);
3363 /* r: read item */
3364 if (item_reading_classification(otmp, buf) == IA_READ_OBJ)
3365 ia_addmenu(win, IA_READ_OBJ, 'r', buf);
3367 /* R: remove accessory or rub item */
3368 if (otmp->owornmask & W_ACCESSORY)
3369 ia_addmenu(win, IA_TAKEOFF_OBJ, 'R', "Remove this accessory");
3370 if (otmp->otyp == OIL_LAMP || otmp->otyp == MAGIC_LAMP
3371 || otmp->otyp == BRASS_LANTERN) {
3372 Sprintf(buf, "Rub this %s", simpleonames(otmp));
3373 ia_addmenu(win, IA_RUB_OBJ, 'R', buf);
3374 } else if (otmp->oclass == GEM_CLASS && is_graystone(otmp))
3375 ia_addmenu(win, IA_RUB_OBJ, 'R', "Rub something on this stone");
3377 /* t: throw item */
3378 if (!already_worn) {
3379 boolean shoot = ammo_and_launcher(otmp, uwep);
3382 * FIXME:
3383 * 'one of these' should be changed to 'some of these' when there
3384 * is the possibility of a multi-shot volley but we don't have
3385 * any way to determine that except by actually calculating the
3386 * volley count and that could randomly yield 1 here and 2..N
3387 * while throwing or vice versa.
3389 Sprintf(buf, "%s %s%s", shoot ? "Shoot" : "Throw",
3390 (otmp->quan == 1L) ? "this item"
3391 : (otmp->otyp == GOLD_PIECE) ? "them"
3392 : "one of these",
3393 /* if otmp is quivered, we've already listed
3394 'f - shoot|throw this item' as a choice;
3395 if 't' is duplicating that, say so ('t' and 'f'
3396 behavior differs for throwing a stack of gold) */
3397 (otmp == uquiver && (otmp->otyp != GOLD_PIECE
3398 || otmp->quan == 1L))
3399 ? " (same as 'f')" : "");
3400 ia_addmenu(win, IA_THROW_OBJ, 't', buf);
3403 /* T: take off armor, tip carried container */
3404 if (otmp->owornmask & W_ARMOR)
3405 ia_addmenu(win, IA_TAKEOFF_OBJ, 'T', "Take off this armor");
3406 if ((Is_container(otmp) && (Has_contents(otmp) || !otmp->cknown))
3407 || (otmp->otyp == HORN_OF_PLENTY && (otmp->spe > 0 || !otmp->known)))
3408 ia_addmenu(win, IA_TIP_CONTAINER, 'T',
3409 "Tip all the contents out of this container");
3411 /* V: invoke */
3412 if ((otmp->otyp == FAKE_AMULET_OF_YENDOR && !otmp->known)
3413 || otmp->oartifact || objects[otmp->otyp].oc_unique
3414 /* non-artifact crystal balls don't have any unique power but
3415 the #invoke command lists them as likely candidates */
3416 || otmp->otyp == CRYSTAL_BALL)
3417 ia_addmenu(win, IA_INVOKE_OBJ, 'V',
3418 "Try to invoke a unique power of this object");
3420 /* w: wield, hold in hands, works on everything but with different
3421 advice text; not mentioned for things that are already wielded */
3422 if (otmp == uwep || cantwield(gy.youmonst.data)) {
3423 ; /* either already wielded or can't wield anything; skip 'w' */
3424 } else if (otmp->oclass == WEAPON_CLASS || is_weptool(otmp)
3425 || is_wet_towel(otmp) || otmp->otyp == HEAVY_IRON_BALL) {
3426 Sprintf(buf, "Wield this %s as your weapon",
3427 (otmp->quan > 1L) ? "stack" : "item");
3428 ia_addmenu(win, IA_WIELD_OBJ, 'w', buf);
3429 } else if (otmp->otyp == TIN_OPENER) {
3430 ia_addmenu(win, IA_WIELD_OBJ, 'w',
3431 "Wield the tin opener to easily open tins");
3432 } else if (!already_worn) {
3433 /* originally this was using "hold this item in your hands" but
3434 there's no concept of "holding an item", plus it unwields
3435 whatever item you already have wielded so use "wield this item" */
3436 Sprintf(buf, "Wield this %s in your %s",
3437 (otmp->quan > 1L) ? "stack" : "item",
3438 /* only two-handed weapons and unicorn horns care about
3439 pluralizing "hand" and they won't reach here, but plural
3440 sounds better when poly'd into something with "claw" */
3441 makeplural(body_part(HAND)));
3442 ia_addmenu(win, IA_WIELD_OBJ, 'w', buf);
3445 /* W: wear armor */
3446 if (!already_worn) {
3447 if (otmp->oclass == ARMOR_CLASS)
3448 ia_addmenu(win, IA_WEAR_OBJ, 'W', "Wear this armor");
3451 /* x: Swap main and readied weapon */
3452 if (otmp == uwep && uswapwep)
3453 ia_addmenu(win, IA_SWAPWEAPON, 'x',
3454 "Swap this with your alternate weapon");
3455 else if (otmp == uwep)
3456 ia_addmenu(win, IA_SWAPWEAPON, 'x',
3457 "Ready this as an alternate weapon");
3458 else if (otmp == uswapwep)
3459 ia_addmenu(win, IA_SWAPWEAPON, 'x',
3460 "Swap this with your main weapon");
3462 /* this is based on TWOWEAPOK() in wield.c; we don't call can_two_weapon()
3463 because it is very verbose; attempting to two-weapon might be rejected
3464 but we screen out most reasons for rejection before offering it as a
3465 choice */
3466 #define MAYBETWOWEAPON(obj) \
3467 ((((obj)->oclass == WEAPON_CLASS) \
3468 ? !(is_launcher(obj) || is_ammo(obj) || is_missile(obj)) \
3469 : is_weptool(obj)) \
3470 && !bimanual(obj))
3472 /* X: Toggle two-weapon mode on or off */
3473 if ((otmp == uwep || otmp == uswapwep)
3474 /* if already two-weaponing, no special checks needed to toggle off */
3475 && (u.twoweap
3476 /* but if not, try to filter most "you can't do that" here */
3477 || (could_twoweap(gy.youmonst.data) && !uarms
3478 && uwep && MAYBETWOWEAPON(uwep)
3479 && uswapwep && MAYBETWOWEAPON(uswapwep)))) {
3480 Sprintf(buf, "Toggle two-weapon combat %s", u.twoweap ? "off" : "on");
3481 ia_addmenu(win, IA_TWOWEAPON, 'X', buf);
3484 #undef MAYBETWOWEAPON
3486 /* z: Zap wand */
3487 if (otmp->oclass == WAND_CLASS)
3488 ia_addmenu(win, IA_ZAP_OBJ, 'z',
3489 "Zap this wand to release its magic");
3491 /* ?: Look up an item in the game's database */
3492 if (ia_checkfile(otmp)) {
3493 Sprintf(buf, "Look up information about %s",
3494 (otmp->quan > 1L) ? "these" : "this");
3495 ia_addmenu(win, IA_WHATIS_OBJ, '/', buf);
3498 Sprintf(buf, "Do what with %s?", the(cxname(otmp)));
3499 end_menu(win, buf);
3501 n = select_menu(win, PICK_ONE, &selected);
3503 if (n > 0) {
3504 act = selected[0].item.a_int;
3505 free((genericptr_t) selected);
3507 itemactions_pushkeys(otmp, act);
3509 destroy_nhwindow(win);
3511 /* finish the 'i' command: no time elapses and cancelling without
3512 selecting an action doesn't matter */
3513 return ECMD_OK;
3516 /* show some or all of inventory while allowing the picking of an item in
3517 order to preform context-sensitive item action on it; always returns 'ok';
3518 invent subsets specified by the ')', '[', '(', '=', '"', or '*' commands
3519 when they're invoked with the 'm' prefix (or without it for '*') */
3520 staticfn int
3521 dispinv_with_action(
3522 char *lets, /* list of invlet values to include */
3523 boolean use_inuse_ordering, /* affects sortloot() and header labels */
3524 const char *alt_label) /* alternate value for in-use "Accessories" */
3526 struct obj *otmp, *nextobj;
3527 const char *save_accessories = 0;
3528 char c, save_sortloot = 0;
3529 unsigned len = lets ? (unsigned) strlen(lets) : 0U;
3530 boolean menumode = (len != 1 || iflags.menu_requested) ? TRUE : FALSE,
3531 save_force_invmenu = iflags.force_invmenu;
3533 if (use_inuse_ordering) {
3534 save_accessories = inuse_headers[4];
3535 save_sortloot = flags.sortloot;
3537 flags.sortloot = 'i'; /* checked by display_pickinv() */
3538 if (alt_label)
3539 inuse_headers[4] = alt_label;
3541 iflags.force_invmenu = FALSE;
3543 c = display_inventory(lets, menumode);
3545 if (use_inuse_ordering) {
3546 flags.sortloot = save_sortloot;
3547 inuse_headers[4] = save_accessories;
3549 iflags.force_invmenu = save_force_invmenu;
3551 if (c && c != '\033') {
3552 for (otmp = gi.invent; otmp; otmp = nextobj) {
3553 nextobj = otmp->nobj;
3554 if (otmp->invlet == c)
3555 return itemactions(otmp);
3558 return ECMD_OK;
3561 /* the #inventory command (not much left...) */
3563 ddoinv(void)
3565 return dispinv_with_action((char *) 0, FALSE, NULL);
3569 * find_unpaid()
3571 * Scan the given list of objects. If last_found is NULL, return the first
3572 * unpaid object found. If last_found is not NULL, then skip over unpaid
3573 * objects until last_found is reached, then set last_found to NULL so the
3574 * next unpaid object is returned. This routine recursively follows
3575 * containers.
3577 staticfn struct obj *
3578 find_unpaid(struct obj *list, struct obj **last_found)
3580 struct obj *obj;
3582 while (list) {
3583 if (list->unpaid) {
3584 if (*last_found) {
3585 /* still looking for previous unpaid object */
3586 if (list == *last_found)
3587 *last_found = (struct obj *) 0;
3588 } else
3589 return ((*last_found = list));
3591 if (Has_contents(list)) {
3592 if ((obj = find_unpaid(list->cobj, last_found)) != 0)
3593 return obj;
3595 list = list->nobj;
3597 return (struct obj *) 0;
3600 void
3601 free_pickinv_cache(void)
3603 if (gc.cached_pickinv_win != WIN_ERR) {
3604 destroy_nhwindow(gc.cached_pickinv_win);
3605 gc.cached_pickinv_win = WIN_ERR;
3610 * Internal function used by display_inventory and getobj that can display
3611 * inventory and return a count as well as a letter.
3613 staticfn char
3614 display_pickinv(
3615 const char *lets, /* non-compacted list of invlet values */
3616 const char *xtra_choice, /* non-object "bare hands" or "fingers" */
3617 const char *query, /* optional; prompt string for menu */
3618 boolean allowxtra, /* hands are allowed (maybe alternate) choice */
3619 boolean want_reply, /* True: select an item, False: just display */
3620 long *out_cnt) /* optional; count player entered when selecting an item */
3622 static const char /* potential entries for perm_invent window */
3623 not_carrying_anything[] = "Not carrying anything",
3624 not_using_anything[] = "Not using any items",
3625 only_carrying_gold[] = "Only carrying gold";
3626 struct obj *otmp, wizid_fakeobj, inuse_fakeobj;
3627 char ilet, ret, *formattedobj;
3628 const char *invlet = flags.inv_order;
3629 int n, classcount, inusecount = 0;
3630 winid win; /* windows being used */
3631 anything any;
3632 menu_item *selected;
3633 unsigned sortflags;
3634 Loot *sortedinvent, *srtinv;
3635 int8_t prevorderclass;
3636 boolean (*filter)(struct obj *) = (boolean (*)(OBJ_P)) 0;
3637 boolean wizid = (wizard && iflags.override_ID), gotsomething = FALSE;
3638 int clr = NO_COLOR, menu_behavior = MENU_BEHAVE_STANDARD;
3639 boolean show_gold = TRUE, inuse_only = FALSE, skipped_gold = FALSE,
3640 doing_perm_invent = FALSE, save_flags_sortpack,
3641 usextra = (xtra_choice && allowxtra);
3643 if (lets && !*lets)
3644 lets = 0; /* simplify tests: (lets) instead of (lets && *lets) */
3646 #ifdef DUMPLOG
3647 if (iflags.in_dumplog) {
3648 win = 0; /* passed to dump_putstr() which ignores it... */
3649 } else
3650 #endif
3651 if (lets || usextra || wizid || want_reply
3652 #ifdef TTY_PERM_INVENT
3653 /*|| !gi.in_sync_perminvent*/
3654 #endif
3655 || WIN_INVEN == WIN_ERR) {
3656 /* partial inventory in perm_invent setting; don't operate on
3657 full inventory window, use an alternate one instead; create
3658 the first time needed and keep it for re-use as needed later */
3659 if (gc.cached_pickinv_win == WIN_ERR)
3660 gc.cached_pickinv_win = create_nhwindow(NHW_MENU);
3661 win = gc.cached_pickinv_win;
3662 if (flags.sortloot == 'i')
3663 inuse_only = TRUE;
3664 } else {
3665 win = WIN_INVEN;
3666 menu_behavior = MENU_BEHAVE_PERMINV;
3667 prepare_perminvent(win);
3668 show_gold = ((wri_info.fromcore.invmode & InvShowGold) != 0);
3669 inuse_only = ((wri_info.fromcore.invmode & InvInUse) != 0);
3670 doing_perm_invent = TRUE;
3673 * Exit early if no inventory -- but keep going if we are doing
3674 * a permanent inventory update. We need to keep going so the
3675 * permanent inventory window updates itself to remove the last
3676 * item(s) dropped. One down side: the addition of the exception
3677 * for permanent inventory window updates _can_ pop the window
3678 * up when it's not displayed -- even if it's empty -- because we
3679 * don't know at this level if its up or not. This may not be
3680 * an issue if empty checks are done before hand and the call
3681 * to here is short circuited away.
3683 * 2: our count here is only to distinguish between 0 or 1 or
3684 * more than 1; for the last case, we don't need a precise number.
3685 * For perm_invent update we force 'more than 1'.
3687 n = (doing_perm_invent && !lets && !want_reply) ? 2
3688 : lets ? (int) strlen(lets)
3689 : !gi.invent ? 0 : !gi.invent->nobj ? 1 : 2;
3690 /* for xtra_choice, there's another 'item' not included in initial 'n';
3691 for !lets (full invent or inuse_only) and for override_ID (wizard
3692 mode identify), skip message_menu handling of single item even if
3693 item count was 1 */
3694 if (usextra || (n == 1 && (!lets || wizid)))
3695 ++n;
3697 if (n == 0) {
3698 pline("%s.", not_carrying_anything);
3699 return 0;
3702 /* oxymoron? temporarily assign permanent inventory letters */
3703 if (!flags.invlet_constant)
3704 reassign();
3706 if (n == 1 && !iflags.force_invmenu && !iflags.menu_requested) {
3707 /* when only one item of interest, use pline instead of menus;
3708 we actually use a fake message-line menu in order to allow
3709 the user to perform selection at the --More-- prompt for tty */
3710 ret = '\0';
3711 if (usextra) {
3712 /* xtra_choice is "bare hands" (wield), "fingertip" (Engrave),
3713 "nothing" (prepare Quiver), "fingers" (apply grease), or
3714 "hands" (default) */
3715 ret = message_menu(HANDS_SYM, PICK_ONE,
3716 xprname((struct obj *) 0, xtra_choice,
3717 HANDS_SYM, TRUE, 0L, 0L)); /* '-' */
3718 } else {
3719 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
3720 if (!lets || otmp->invlet == lets[0])
3721 break;
3722 if (otmp)
3723 ret = message_menu(otmp->invlet,
3724 want_reply ? PICK_ONE : PICK_NONE,
3725 xprname(otmp, (char *) 0, lets[0],
3726 TRUE, 0L, 0L));
3728 if (out_cnt)
3729 *out_cnt = -1L; /* select all */
3730 return ret;
3733 sortflags = (flags.sortloot == 'f') ? SORTLOOT_LOOT : SORTLOOT_INVLET;
3734 if (flags.sortpack)
3735 sortflags |= SORTLOOT_PACK;
3736 save_flags_sortpack = flags.sortpack;
3737 #ifdef TTY_PERM_INVENT
3738 if (doing_perm_invent && WINDOWPORT(tty)) {
3739 flags.sortpack = FALSE;
3740 sortflags = SORTLOOT_INVLET;
3742 #endif
3743 if (inuse_only) {
3744 flags.sortpack = FALSE;
3745 sortflags = SORTLOOT_INUSE; /* override */
3746 filter = is_inuse;
3747 if (!uwep) {
3749 * inuse_only and not wielding anything: insert "bare hands"
3750 * into primary weapon slot. Unlike adding an extra menu
3751 * entry for 'xtra_choice' at top of menu, we need an object
3752 * in invent for it to be sorted into desired position.
3753 * It will need custom formatting below.
3755 inuse_fakeobj = cg.zeroobj; /* STRANGE_OBJECT, ILLOBJ_CLASS */
3756 inuse_fakeobj.invlet = HANDS_SYM; /* '-' */
3757 inuse_fakeobj.owornmask = W_WEP; /* inuse_classify needs this */
3758 inuse_fakeobj.where = OBJ_INVENT; /* is_inuse filter needs this */
3759 inuse_fakeobj.nobj = gi.invent;
3760 gi.invent = &inuse_fakeobj;
3764 sortedinvent = sortloot(&gi.invent, sortflags, FALSE, filter);
3765 /* inuse_only: if we inserted bare hands as a fake weapon, remove them;
3766 although the fake object will no longer be in invent, sortedinvent
3767 will still contain a pointer to it */
3768 if (gi.invent == &inuse_fakeobj) {
3769 gi.invent = inuse_fakeobj.nobj;
3770 inuse_fakeobj.nobj = (struct obj *) 0;
3771 /* if inuse_fakeobj is the only thing present in sortedinvent, get
3772 rid of it in order to produce "not using any items" */
3773 if (sortedinvent[0].obj == &inuse_fakeobj && !sortedinvent[1].obj)
3774 sortedinvent[0].obj = (struct obj *) 0;
3777 start_menu(win, menu_behavior);
3778 any = cg.zeroany;
3779 if (wizid) {
3780 int unid_cnt;
3781 char prompt[QBUFSZ];
3783 unid_cnt = count_unidentified(gi.invent);
3784 Sprintf(prompt, "Debug Identify"); /* 'title' rather than 'prompt' */
3785 if (unid_cnt)
3786 Sprintf(eos(prompt),
3787 " -- unidentified or partially identified item%s",
3788 plur(unid_cnt));
3789 add_menu_str(win, prompt);
3790 if (!unid_cnt) {
3791 add_menu_str(win,
3792 "(all items are permanently identified already)");
3793 gotsomething = TRUE;
3794 } else {
3795 any.a_obj = &wizid_fakeobj;
3796 Sprintf(prompt, "select %s to permanently identify",
3797 (unid_cnt == 1) ? "it": "any or all of them");
3798 /* wiz_identify stuffed the wiz_identify command character (^I)
3799 into iflags.override_ID for our use as an accelerator;
3800 it could be ambiguous if player has assigned a letter to
3801 the #wizidentify command, so include it as a group accelerator
3802 but use '_' as the primary selector */
3803 if (unid_cnt > 1)
3804 Sprintf(eos(prompt), " (%s for all)",
3805 visctrl(iflags.override_ID));
3806 add_menu(win, &nul_glyphinfo, &any, '_', iflags.override_ID,
3807 ATR_NONE, clr, prompt, MENU_ITEMFLAGS_SKIPINVERT);
3808 gotsomething = TRUE;
3810 } else if (usextra) {
3811 /* wizard override ID and xtra_choice are mutually exclusive */
3812 if (flags.sortpack)
3813 add_menu_heading(win, "Miscellaneous");
3814 any.a_char = HANDS_SYM; /* '-' */
3815 add_menu(win, &nul_glyphinfo, &any, HANDS_SYM, 0, ATR_NONE,
3816 clr, xtra_choice, MENU_ITEMFLAGS_NONE);
3817 gotsomething = TRUE;
3820 nextclass:
3821 classcount = 0;
3822 prevorderclass = 0;
3823 for (srtinv = sortedinvent; (otmp = srtinv->obj) != 0; ++srtinv) {
3824 int tmpglyph;
3825 glyph_info tmpglyphinfo = nul_glyphinfo;
3827 /* for showing a set of specific letters, skip ones not in the set */
3828 if (lets && !strchr(lets, otmp->invlet))
3829 continue;
3830 if (!flags.sortpack || otmp->oclass == *invlet) {
3831 if (wizid && !not_fully_identified(otmp))
3832 continue;
3833 if (inuse_only) {
3834 /* for inuse-only, start with an extra header */
3835 if (!inusecount++)
3836 add_menu_heading(win, doing_perm_invent ? "In use"
3837 : "Inventory in use");
3838 } else if (doing_perm_invent && !show_gold) {
3839 /* don't skip gold if it is quivered, even for !show_gold */
3840 if (otmp->invlet == GOLD_SYM && !otmp->owornmask) {
3841 skipped_gold = TRUE;
3842 continue;
3845 /* maybe insert a class header */
3846 if (inuse_only ? (srtinv->orderclass != prevorderclass)
3847 : (flags.sortpack && !classcount)) {
3848 boolean withsym = (want_reply && iflags.menu_head_objsym);
3849 const char *class_header = inuse_only
3850 ? inuse_headers[(int) srtinv->orderclass]
3851 : (const char *) let_to_name(*invlet, FALSE, withsym);
3853 add_menu_heading(win, class_header);
3854 classcount++;
3855 prevorderclass = srtinv->orderclass;
3858 ilet = otmp->invlet;
3859 any = cg.zeroany; /* all bits zero */
3860 if (wizid)
3861 any.a_obj = otmp;
3862 else
3863 any.a_char = ilet;
3865 if (otmp == &inuse_fakeobj) {
3866 /* fake item to format as "bare|gloved hands" */
3867 char barehands[QBUFSZ];
3869 /* like doname() below, makeplural() returns an obuf[] */
3870 formattedobj = makeplural(body_part(HAND));
3871 Sprintf(barehands, "%s %s (no weapon)",
3872 uarmg ? "gloved" : "bare", formattedobj);
3873 add_menu(win, &nul_glyphinfo, &any, ilet, 0,
3874 ATR_NONE, clr, barehands, MENU_ITEMFLAGS_NONE);
3875 } else {
3876 /* normal inventory item */
3877 tmpglyph = obj_to_glyph(otmp, rn2_on_display_rng);
3878 map_glyphinfo(0, 0, tmpglyph, 0U, &tmpglyphinfo);
3879 formattedobj = doname(otmp);
3880 add_menu(win, &tmpglyphinfo, &any, ilet,
3881 wizid ? def_oc_syms[(int) otmp->oclass].sym : 0,
3882 ATR_NONE, clr, formattedobj, MENU_ITEMFLAGS_NONE);
3884 /* doname() uses a static pool of obuf[] output buffers and
3885 we don't want inventory display to overwrite all of them,
3886 so when we've used one we release it for re-use */
3887 maybereleaseobuf(formattedobj);
3888 gotsomething = TRUE;
3891 if (flags.sortpack) {
3892 if (*++invlet)
3893 goto nextclass;
3894 if (--invlet != venom_inv) {
3895 invlet = venom_inv;
3896 goto nextclass;
3899 if (save_flags_sortpack != flags.sortpack)
3900 flags.sortpack = save_flags_sortpack;
3902 /* default for force_invmenu is a menu listing likely candidates;
3903 add '*' for 'list all' as an extra choice unless the menu already
3904 includes everything; when reissuing the menu after player has
3905 picked '*', add '?' for 'list likely candidates' to reverse that */
3906 if (iflags.force_invmenu && want_reply) {
3907 const char *menutext = NULL;
3909 any = cg.zeroany;
3910 if ((allowxtra && !usextra)
3911 || (lets && (int) strlen(lets) < inv_cnt(TRUE))) {
3912 any.a_char = '*';
3913 menutext = "(list everything)";
3914 } else if (!lets) {
3915 any.a_char = '?';
3916 menutext = "(list likely candidates)";
3918 if (menutext) {
3919 add_menu_heading(win, "Special");
3920 add_menu(win, &nul_glyphinfo, &any, any.a_char, 0, ATR_NONE, clr,
3921 menutext, MENU_ITEMFLAGS_NONE);
3922 gotsomething = TRUE; /* menu isn't empty */
3925 unsortloot(&sortedinvent);
3926 /* for permanent inventory where nothing has been listed (because
3927 there isn't anything applicable to list; the n==0 case above
3928 gets skipped for perm_invent), put something into the menu */
3929 if (doing_perm_invent && !lets && !gotsomething) {
3930 add_menu_str(win, inuse_only ? not_using_anything
3931 : (!show_gold && skipped_gold) ? only_carrying_gold
3932 : not_carrying_anything);
3933 want_reply = FALSE;
3935 end_menu(win, (query && *query) ? query : (char *) 0);
3937 n = select_menu(win,
3938 wizid ? PICK_ANY : want_reply ? PICK_ONE : PICK_NONE,
3939 &selected);
3940 if (n > 0) {
3941 if (wizid) {
3942 boolean all_id = FALSE;
3943 int i;
3945 /* identifying items will update perm_invent, calling this
3946 routine recursively, and we don't want the nested call
3947 to filter on unID'd items */
3948 iflags.override_ID = 0;
3949 ret = '\0';
3950 for (i = 0; i < n; ++i) {
3951 otmp = selected[i].item.a_obj;
3952 if (otmp == &wizid_fakeobj) {
3953 identify_pack(0, FALSE);
3954 /* identify_pack() performs update_inventory() */
3955 all_id = TRUE;
3956 break;
3957 } else {
3958 if (not_fully_identified(otmp))
3959 (void) identify(otmp);
3960 /* identify() does not perform update_inventory() */
3963 if (!all_id)
3964 update_inventory();
3965 } else {
3966 ret = selected[0].item.a_char;
3967 if (out_cnt)
3968 *out_cnt = selected[0].count;
3970 free((genericptr_t) selected);
3971 } else
3972 ret = !n ? '\0' : '\033'; /* cancelled */
3974 return ret;
3978 * If lets == NULL or "", list all objects in the inventory. Otherwise,
3979 * list all objects with object classes that match the order in lets.
3981 * Returns the letter identifier of a selected item, or 0 if nothing
3982 * was selected.
3984 char
3985 display_inventory(const char *lets, boolean want_reply)
3987 struct _cmd_queue *cmdq = cmdq_pop();
3989 if (cmdq) {
3990 if (cmdq->typ == CMDQ_KEY) {
3991 struct obj *otmp;
3993 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
3994 if (otmp->invlet == cmdq->key
3995 && (!lets || !*lets
3996 || strchr(lets,
3997 def_oc_syms[(int) otmp->oclass].sym))) {
3998 free(cmdq);
3999 return otmp->invlet;
4003 /* cmdq not a key, or did not find the object, abort */
4004 free(cmdq);
4005 cmdq_clear(CQ_CANNED);
4006 return '\0';
4008 return display_pickinv(lets, (char *) 0, (char *) 0,
4009 FALSE, want_reply, (long *) 0);
4013 * Show what is current using inventory letters.
4016 staticfn char
4017 display_used_invlets(char avoidlet)
4019 struct obj *otmp;
4020 char ilet, ret = 0;
4021 char *invlet = flags.inv_order;
4022 int n, classcount, invdone = 0, tmpglyph;
4023 glyph_info tmpglyphinfo = nul_glyphinfo;
4024 winid win;
4025 anything any;
4026 menu_item *selected;
4027 int clr = NO_COLOR;
4029 if (gi.invent) {
4030 win = create_nhwindow(NHW_MENU);
4031 start_menu(win, MENU_BEHAVE_STANDARD);
4032 while (!invdone) {
4033 any = cg.zeroany; /* set all bits to zero */
4034 classcount = 0;
4035 for (otmp = gi.invent; otmp; otmp = otmp->nobj) {
4036 ilet = otmp->invlet;
4037 if (ilet == avoidlet)
4038 continue;
4039 if (!flags.sortpack || otmp->oclass == *invlet) {
4040 if (flags.sortpack && !classcount) {
4041 any = cg.zeroany; /* zero */
4042 add_menu_heading(win,
4043 let_to_name(*invlet, FALSE, FALSE));
4044 classcount++;
4046 any.a_char = ilet;
4047 tmpglyph = obj_to_glyph(otmp, rn2_on_display_rng);
4048 map_glyphinfo(0, 0, tmpglyph, 0U, &tmpglyphinfo);
4049 add_menu(win, &tmpglyphinfo, &any, ilet, 0,
4050 ATR_NONE, clr, doname(otmp),
4051 MENU_ITEMFLAGS_NONE);
4054 if (flags.sortpack && *++invlet)
4055 continue;
4056 invdone = 1;
4058 end_menu(win, "Inventory letters used:");
4060 n = select_menu(win, PICK_ONE, &selected);
4061 if (n > 0) {
4062 ret = selected[0].item.a_char;
4063 free((genericptr_t) selected);
4064 } else
4065 ret = !n ? '\0' : '\033'; /* cancelled */
4066 destroy_nhwindow(win);
4068 return ret;
4072 * Returns the number of unpaid items within the given list. This includes
4073 * contained objects.
4076 count_unpaid(struct obj *list)
4078 int count = 0;
4080 while (list) {
4081 if (list->unpaid)
4082 count++;
4083 if (Has_contents(list))
4084 count += count_unpaid(list->cobj);
4085 list = list->nobj;
4087 return count;
4091 * Returns the number of items with b/u/c/unknown within the given list.
4092 * This does NOT include contained objects.
4094 * Assumes that the hero sees or touches or otherwise senses the objects
4095 * at some point: bknown is forced for priest[ess], like in xname().
4098 count_buc(struct obj *list, int type, boolean (*filterfunc)(OBJ_P))
4100 int count = 0;
4102 for (; list; list = list->nobj) {
4103 /* priests always know bless/curse state */
4104 if (Role_if(PM_CLERIC))
4105 list->bknown = (list->oclass != COIN_CLASS);
4106 /* some actions exclude some or most items */
4107 if (filterfunc && !(*filterfunc)(list))
4108 continue;
4110 /* coins are either uncursed or unknown based upon option setting */
4111 if (list->oclass == COIN_CLASS) {
4112 if (type == (flags.goldX ? BUC_UNKNOWN : BUC_UNCURSED))
4113 ++count;
4114 continue;
4116 /* check whether this object matches the requested type */
4117 if (!list->bknown
4118 ? (type == BUC_UNKNOWN)
4119 : list->blessed ? (type == BUC_BLESSED)
4120 : list->cursed ? (type == BUC_CURSED)
4121 : (type == BUC_UNCURSED))
4122 ++count;
4124 return count;
4127 /* similar to count_buc(), but tallies all states at once
4128 rather than looking for a specific type */
4129 void
4130 tally_BUCX(
4131 struct obj *list,
4132 boolean by_nexthere,
4133 int *bcp, int *ucp, int *ccp, int *xcp, int *ocp, int *jcp)
4135 /* Future extensions:
4136 * Skip current_container when list is invent, uchain when
4137 * first object of list is located on the floor. 'ocp' will then
4138 * have a function again (it was a counter for having skipped gold,
4139 * but that's not skipped anymore).
4141 *bcp = *ucp = *ccp = *xcp = *ocp = *jcp = 0;
4142 for ( ; list; list = (by_nexthere ? list->nexthere : list->nobj)) {
4143 /* priests always know bless/curse state */
4144 if (Role_if(PM_CLERIC))
4145 list->bknown = (list->oclass != COIN_CLASS);
4146 if (list->pickup_prev)
4147 ++(*jcp);
4148 /* coins are either uncursed or unknown based upon option setting */
4149 if (list->oclass == COIN_CLASS) {
4150 if (flags.goldX)
4151 ++(*xcp);
4152 else
4153 ++(*ucp);
4154 continue;
4156 /* ordinary items */
4157 if (!list->bknown)
4158 ++(*xcp);
4159 else if (list->blessed)
4160 ++(*bcp);
4161 else if (list->cursed)
4162 ++(*ccp);
4163 else /* neither blessed nor cursed => uncursed */
4164 ++(*ucp);
4168 /* count everything inside a container, or just shop-owned items inside */
4169 long
4170 count_contents(
4171 struct obj *container,
4172 boolean nested, /* include contents of any nested containers */
4173 boolean quantity, /* count all vs count separate stacks */
4174 boolean everything, /* all objects vs only unpaid objects */
4175 boolean newdrop) /* on floor, but hero-owned items haven't
4176 * been marked no_charge yet and shop-owned
4177 * items are still marked unpaid -- used
4178 * when asking the player whether to sell */
4180 struct obj *otmp, *topc;
4181 boolean shoppy = FALSE;
4182 long count = 0L;
4184 if (!everything && !newdrop) {
4185 coordxy x, y;
4187 for (topc = container; topc->where == OBJ_CONTAINED;
4188 topc = topc->ocontainer)
4189 continue;
4190 if (topc->where == OBJ_FLOOR && get_obj_location(topc, &x, &y, 0))
4191 shoppy = costly_spot(x, y);
4193 for (otmp = container->cobj; otmp; otmp = otmp->nobj) {
4194 if (nested && Has_contents(otmp))
4195 count += count_contents(otmp, nested, quantity, everything,
4196 newdrop);
4197 if (everything || otmp->unpaid || (shoppy && !otmp->no_charge))
4198 count += quantity ? otmp->quan : 1L;
4200 return count;
4203 staticfn void
4204 dounpaid(
4205 int count, /* unpaid items in inventory */
4206 int floorcount, /* unpaid items on floor (rare) */
4207 int buriedcount) /* unpaid items under the floor (extremely rare) */
4209 winid win;
4210 struct obj *otmp, *marker, *contnr;
4211 char ilet;
4212 char *invlet = flags.inv_order;
4213 int classcount, num_so_far, xtracount;
4214 long cost, totcost;
4216 otmp = marker = contnr = (struct obj *) 0;
4217 xtracount = floorcount + buriedcount;
4219 if (count == 1 && !xtracount) {
4220 otmp = find_unpaid(gi.invent, &marker);
4221 contnr = unknwn_contnr_contents(otmp);
4223 if (otmp && !contnr) {
4224 /* 1 item; use pline instead of popup menu */
4225 cost = unpaid_cost(otmp, COST_NOCONTENTS);
4226 iflags.suppress_price++; /* suppress "(unpaid)" suffix */
4227 pline1(xprname(otmp, distant_name(otmp, doname),
4228 carried(otmp) ? otmp->invlet : CONTAINED_SYM,
4229 TRUE, cost, 0L));
4230 iflags.suppress_price--;
4231 return;
4234 win = create_nhwindow(NHW_MENU);
4235 totcost = 0L;
4236 num_so_far = 0; /* count of # printed so far */
4237 if (!flags.invlet_constant)
4238 reassign();
4240 do {
4241 classcount = 0;
4242 for (otmp = gi.invent; otmp; otmp = otmp->nobj) {
4243 ilet = otmp->invlet;
4244 if (otmp->unpaid) {
4245 if (!flags.sortpack || otmp->oclass == *invlet) {
4246 if (flags.sortpack && !classcount) {
4247 putstr(win, 0, let_to_name(*invlet, TRUE, FALSE));
4248 classcount++;
4251 totcost += cost = unpaid_cost(otmp, COST_NOCONTENTS);
4252 iflags.suppress_price++; /* suppress "(unpaid)" suffix */
4253 putstr(win, 0, xprname(otmp, distant_name(otmp, doname),
4254 ilet, TRUE, cost, 0L));
4255 iflags.suppress_price--;
4256 num_so_far++;
4260 } while (flags.sortpack && (*++invlet));
4262 if (count > num_so_far) {
4263 /* something unpaid is contained */
4264 if (flags.sortpack)
4265 putstr(win, 0, let_to_name(CONTAINED_SYM, TRUE, FALSE));
4267 * Search through the container objects in the inventory for
4268 * unpaid items. The top level inventory items have already
4269 * been listed.
4271 for (otmp = gi.invent; otmp; otmp = otmp->nobj) {
4272 if (Has_contents(otmp)) {
4273 long contcost = 0L;
4275 marker = (struct obj *) 0; /* haven't found any */
4276 while (find_unpaid(otmp->cobj, &marker)) {
4277 totcost += cost = unpaid_cost(marker, COST_NOCONTENTS);
4278 contcost += cost;
4279 if (otmp->cknown) {
4280 iflags.suppress_price++; /* suppress "(unpaid)" sfx */
4281 putstr(win, 0,
4282 xprname(marker, distant_name(marker, doname),
4283 CONTAINED_SYM, TRUE, cost, 0L));
4284 iflags.suppress_price--;
4287 if (!otmp->cknown) {
4288 char contbuf[BUFSZ];
4290 /* Shopkeeper knows what to charge for contents */
4291 Sprintf(contbuf, "%s contents", s_suffix(xname(otmp)));
4292 putstr(win, 0,
4293 xprname((struct obj *) 0, contbuf, CONTAINED_SYM,
4294 TRUE, contcost, 0L));
4300 if (count > 0) {
4301 putstr(win, 0, "");
4302 putstr(win, 0,
4303 xprname((struct obj *) 0, "Total:", '*', FALSE, totcost, 0L));
4306 /* an unpaid item can be on the floor if dropped on the shop boundary
4307 (then possibly moved all the way into the shop during wall repair);
4308 one can be buried if it started that way and a pit was dug at its
4309 spot then filled by a boulder (or perhaps a theme room with a pool
4310 and an unpaid item moved into that by wall repair, then freezing) */
4311 if (xtracount > 0) { /* floorcount + buriedcount > 0 */
4312 char buf[BUFSZ];
4313 const char
4314 *floorverb = (xtracount > 1) ? "are" : "is",
4315 /* "under the floor" might actually be "under the floor
4316 beneath a wall" when shop repair is involved but that seems
4317 too nit-picky to bother trying to handle here (even more
4318 extreme description-wise: "under the floor beneath the
4319 door/doorway") */
4320 *where = (buriedcount == 0) ? "on the floor"
4321 : (floorcount == 0) ? "under the floor"
4322 : "on or under the floor";
4324 if (!count) {
4325 You("aren't carrying any unpaid items but there %s %d %s.",
4326 floorverb, xtracount, where);
4327 } else {
4328 putstr(win, 0, "");
4329 Sprintf(buf, "(There %s %d more unpaid object%s %s.)",
4330 floorverb, xtracount, plur(xtracount), where);
4331 putstr(win, 0, buf);
4335 if (count > 0)
4336 display_nhwindow(win, FALSE);
4337 destroy_nhwindow(win);
4338 return;
4342 staticfn boolean
4343 this_type_only(struct obj *obj)
4345 boolean res = (obj->oclass == gt.this_type);
4347 if (gt.this_type == 'P') {
4348 res = obj->pickup_prev;
4349 } else if (obj->oclass == COIN_CLASS) {
4350 /* if filtering by bless/curse state, gold is classified as
4351 either unknown or uncursed based on user option setting */
4352 if (gt.this_type && strchr("BUCX", gt.this_type))
4353 res = (gt.this_type == (flags.goldX ? 'X' : 'U'));
4354 } else {
4355 switch (gt.this_type) {
4356 case 'B':
4357 res = (obj->bknown && obj->blessed);
4358 break;
4359 case 'U':
4360 res = (obj->bknown && !(obj->blessed || obj->cursed));
4361 break;
4362 case 'C':
4363 res = (obj->bknown && obj->cursed);
4364 break;
4365 case 'X':
4366 res = !obj->bknown;
4367 break;
4368 default:
4369 break; /* use 'res' as-is */
4372 return res;
4375 /* the #inventtype command */
4377 dotypeinv(void)
4379 static const char
4380 prompt[] = "What type of object do you want an inventory of?";
4381 char c = '\0';
4382 int n, i = 0;
4383 char *extra_types, types[BUFSZ], title[QBUFSZ];
4384 const char *before = "", *after = "";
4385 int class_count, oclass, itemcount,
4386 any_unpaid, u_carried, u_floor, u_buried;
4387 int bcnt, ccnt, ucnt, xcnt, ocnt, jcnt;
4388 boolean billx = *u.ushops && doinvbill(0);
4389 menu_item *pick_list;
4390 boolean traditional = TRUE;
4392 gt.this_type = 0;
4393 gt.this_title = NULL;
4394 if (!gi.invent && !billx) {
4395 You("aren't carrying anything.");
4396 goto doI_done;
4398 title[0] = '\0';
4399 u_carried = count_unpaid(gi.invent);
4400 u_floor = count_unpaid(fobj);
4401 u_buried = count_unpaid(svl.level.buriedobjlist);
4402 any_unpaid = u_carried + u_floor + u_buried;
4403 tally_BUCX(gi.invent, FALSE, &bcnt, &ucnt, &ccnt, &xcnt, &ocnt, &jcnt);
4405 if (flags.menu_style != MENU_TRADITIONAL) {
4406 if (flags.menu_style == MENU_FULL
4407 || flags.menu_style == MENU_PARTIAL) {
4408 traditional = FALSE;
4409 i = UNPAID_TYPES;
4410 if (billx)
4411 i |= BILLED_TYPES;
4412 if (bcnt)
4413 i |= BUC_BLESSED;
4414 if (ucnt)
4415 i |= BUC_UNCURSED;
4416 if (ccnt)
4417 i |= BUC_CURSED;
4418 if (xcnt)
4419 i |= BUC_UNKNOWN;
4420 if (jcnt)
4421 i |= JUSTPICKED;
4422 i |= INCLUDE_VENOM;
4423 n = query_category(prompt, gi.invent, i, &pick_list, PICK_ONE);
4424 if (!n)
4425 goto doI_done;
4426 gt.this_type = c = pick_list[0].item.a_int;
4427 free((genericptr_t) pick_list);
4430 if (traditional) {
4431 /* collect list of classes of objects carried, for use as a prompt */
4432 types[0] = 0;
4433 class_count = collect_obj_classes(types, gi.invent, FALSE,
4434 (boolean (*)(OBJ_P)) 0,
4435 &itemcount);
4436 if (any_unpaid || billx || (bcnt + ccnt + ucnt + xcnt) != 0 || jcnt)
4437 types[class_count++] = ' ';
4438 if (any_unpaid)
4439 types[class_count++] = 'u';
4440 if (billx)
4441 types[class_count++] = 'x';
4442 if (bcnt)
4443 types[class_count++] = 'B';
4444 if (ucnt)
4445 types[class_count++] = 'U';
4446 if (ccnt)
4447 types[class_count++] = 'C';
4448 if (xcnt)
4449 types[class_count++] = 'X';
4450 if (jcnt)
4451 types[class_count++] = 'P';
4452 types[class_count] = '\0';
4453 /* add everything not already included; user won't see these */
4454 extra_types = eos(types);
4455 *extra_types++ = '\033';
4456 if (!any_unpaid)
4457 *extra_types++ = 'u';
4458 if (!billx)
4459 *extra_types++ = 'x';
4460 if (!bcnt)
4461 *extra_types++ = 'B';
4462 if (!ucnt)
4463 *extra_types++ = 'U';
4464 if (!ccnt)
4465 *extra_types++ = 'C';
4466 if (!xcnt)
4467 *extra_types++ = 'X';
4468 if (!jcnt)
4469 *extra_types++ = 'P';
4470 *extra_types = '\0'; /* for strchr() */
4471 for (i = 0; i < MAXOCLASSES; i++)
4472 if (!strchr(types, def_oc_syms[i].sym)) {
4473 *extra_types++ = def_oc_syms[i].sym;
4474 *extra_types = '\0';
4477 if (class_count > 1) {
4478 c = yn_function(prompt, types, '\0', TRUE);
4479 if (c == '\0') {
4480 clear_nhwindow(WIN_MESSAGE);
4481 goto doI_done;
4483 } else {
4484 /* only one thing to itemize */
4485 if (any_unpaid)
4486 c = 'u';
4487 else if (billx)
4488 c = 'x';
4489 else
4490 c = types[0];
4493 if (c == 'x' || (c == 'X' && billx && !xcnt)) {
4494 if (billx)
4495 (void) doinvbill(1);
4496 else
4497 pline("No used-up objects%s.",
4498 any_unpaid ? " on your shopping bill" : "");
4499 goto doI_done;
4501 if (c == 'u' || (c == 'U' && any_unpaid && !ucnt)) {
4502 if (any_unpaid)
4503 dounpaid(u_carried, u_floor, u_buried);
4504 else
4505 You("are not carrying any unpaid objects.");
4506 goto doI_done;
4509 if (strchr("BUCXP", c))
4510 oclass = c; /* not a class but understood by this_type_only() */
4511 else
4512 oclass = def_char_to_objclass(c); /* change to object class */
4513 #if 0
4514 /* this used to be done for the 'if traditional' case but not for the
4515 menu case; also unlike '$', 'I$' explicitly asks about inventory,
4516 so we no longer handle coin class differently from other classes */
4517 if (oclass == COIN_CLASS) {
4518 return doprgold();
4520 #endif
4521 /* these are used for traditional when not applicable and also for
4522 constructing a title to be used by query_objlist() */
4523 switch (c) {
4524 case 'B':
4525 before = "known to be blessed ";
4526 break;
4527 case 'U':
4528 before = "known to be uncursed ";
4529 break;
4530 case 'C':
4531 before = "known to be cursed ";
4532 break;
4533 case 'X':
4534 after = " whose blessed/uncursed/cursed status is unknown";
4535 break; /* better phrasing is desirable */
4536 case 'P':
4537 after = " that were just picked up";
4538 break;
4539 default:
4540 /* 'c' is an object class, because we've already handled
4541 all the non-class letters which were put into 'types[]';
4542 could/should move object class names[] array from below
4543 to somewhere above so that we can access it here (via
4544 lcase(strcpy(classnamebuf, names[(int) c]))), but the
4545 game-play value of doing so is low... */
4546 before = "such ";
4547 break;
4550 if (traditional) {
4551 if (strchr(types, c) > strchr(types, '\033')) {
4552 You("have no %sobjects%s.", before, after);
4553 goto doI_done;
4555 gt.this_type = oclass; /* extra input for this_type_only() */
4557 if (strchr("BUCXP", c)) {
4558 /* the before and after phrases for "you have no..." can both be
4559 treated as mutually-exclusive suffices when creating a title */
4560 Sprintf(title, "Items %s", (before && *before) ? before : after);
4561 /* get rid of trailing space from 'before' and double-space from
4562 'after's leading space */
4563 (void) mungspaces(title);
4564 Strcat(title, ":"); /* after removing unwanted trailing space */
4565 gt.this_title = title;
4568 if (query_objlist((char *) 0, &gi.invent,
4569 ((flags.invlet_constant ? USE_INVLET : 0)
4570 | INVORDER_SORT | INCLUDE_VENOM),
4571 &pick_list, PICK_ONE, this_type_only) > 0) {
4572 struct obj *otmp = pick_list[0].item.a_obj;
4574 free((genericptr_t) pick_list);
4575 (void) itemactions(otmp); /* always returns ECMD_OK */
4578 doI_done:
4579 gt.this_type = 0;
4580 gt.this_title = NULL;
4581 return ECMD_OK;
4584 /* return a string describing the dungeon feature at <x,y> if there
4585 is one worth mentioning at that location; otherwise null */
4586 const char *
4587 dfeature_at(coordxy x, coordxy y, char *buf)
4589 struct rm *lev = &levl[x][y];
4590 int ltyp = lev->typ, cmap = -1;
4591 const char *dfeature = 0;
4592 static char altbuf[BUFSZ];
4593 stairway *stway = stairway_at(x, y);
4595 if (IS_DOOR(ltyp)) {
4596 switch (lev->doormask) {
4597 case D_NODOOR:
4598 cmap = S_ndoor;
4599 break; /* "doorway" */
4600 case D_ISOPEN:
4601 cmap = S_vodoor;
4602 break; /* "open door" */
4603 case D_BROKEN:
4604 dfeature = "broken door";
4605 break;
4606 default:
4607 cmap = S_vcdoor;
4608 break; /* "closed door" */
4610 /* override door description for open drawbridge */
4611 if (is_drawbridge_wall(x, y) >= 0)
4612 dfeature = "open drawbridge portcullis", cmap = -1;
4613 } else if (IS_FOUNTAIN(ltyp))
4614 cmap = S_fountain; /* "fountain" */
4615 else if (IS_THRONE(ltyp))
4616 cmap = S_throne; /* "opulent throne" */
4617 else if (is_lava(x, y))
4618 cmap = S_lava; /* "molten lava" */
4619 else if (is_ice(x, y))
4620 dfeature = ice_descr(x, y, altbuf), cmap = -1; /* "ice" */
4621 else if (is_pool(x, y))
4622 dfeature = "pool of water";
4623 else if (IS_SINK(ltyp))
4624 cmap = S_sink; /* "sink" */
4625 else if (IS_ALTAR(ltyp)) {
4626 Sprintf(altbuf, "%saltar to %s (%s)",
4627 (lev->altarmask & AM_SANCTUM) ? "high " : "",
4628 a_gname(),
4629 align_str(Amask2align(lev->altarmask & ~AM_SHRINE)));
4630 dfeature = altbuf;
4631 } else if (stway) {
4632 dfeature = stairs_description(stway, altbuf, TRUE);
4633 } else if (ltyp == DRAWBRIDGE_DOWN)
4634 cmap = S_vodbridge; /* "lowered drawbridge" */
4635 else if (ltyp == DBWALL)
4636 cmap = S_vcdbridge; /* "raised drawbridge" */
4637 else if (IS_GRAVE(ltyp))
4638 cmap = S_grave; /* "grave" */
4639 else if (ltyp == TREE)
4640 cmap = S_tree; /* "tree" */
4641 else if (ltyp == IRONBARS)
4642 dfeature = "set of iron bars";
4644 if (cmap >= 0)
4645 dfeature = defsyms[cmap].explanation;
4646 if (dfeature)
4647 Strcpy(buf, dfeature);
4648 return dfeature;
4651 /* look at what is here; if there are many objects (pile_limit or more),
4652 don't show them unless obj_cnt is 0 */
4654 look_here(
4655 int obj_cnt, /* obj_cnt > 0 implies that autopickup is in progress */
4656 unsigned lookhere_flags)
4658 struct obj *otmp;
4659 struct trap *trap;
4660 const char *verb = Blind ? "feel" : "see";
4661 const char *dfeature = (char *) 0;
4662 char fbuf[BUFSZ], fbuf2[BUFSZ];
4663 winid tmpwin;
4664 boolean skip_objects, felt_cockatrice = FALSE,
4665 picked_some = (lookhere_flags & LOOKHERE_PICKED_SOME) != 0,
4666 /* skip 'dfeature' if caller used describe_decor() to show it */
4667 skip_dfeature = (lookhere_flags & LOOKHERE_SKIP_DFEATURE) != 0;
4669 /* default pile_limit is 5; a value of 0 means "never skip"
4670 (and 1 effectively forces "always skip") */
4671 skip_objects = (flags.pile_limit > 0 && obj_cnt >= flags.pile_limit);
4672 if (u.uswallow) {
4673 struct monst *mtmp = u.ustuck;
4676 * FIXME?
4677 * Engulfer's inventory can include worn items (specific case is
4678 * Juiblex being created with an amulet as random defensive item)
4679 * which will be flagged as "(being worn)". This code includes
4680 * such a worn item under the header "Contents of <mon>'s stomach",
4681 * a nifty trick for how/where to wear stuff. The situation is
4682 * rare enough to turn a blind eye.
4684 * 3.6.3: Pickup has been changed to decline to pick up a worn
4685 * item from inside an engulfer, but if player tries, it just
4686 * says "you can't" without giving a reason why (which would be
4687 * something along the lines of "because it's worn on the outside
4688 * so is unreachable from in here...").
4690 Sprintf(fbuf, "Contents of %s %s", s_suffix(mon_nam(mtmp)),
4691 mbodypart(mtmp, STOMACH));
4692 /* Skip "Contents of " by using fbuf index 12 */
4693 You("%s to %s what is lying in %s.", Blind ? "try" : "look around",
4694 verb, &fbuf[12]);
4695 otmp = mtmp->minvent;
4696 if (otmp) {
4697 for (; otmp; otmp = otmp->nobj) {
4698 /* If swallower is an animal, it should have become stone
4699 * but... */
4700 if (otmp->otyp == CORPSE)
4701 feel_cockatrice(otmp, FALSE);
4703 if (Blind)
4704 Strcpy(fbuf, "You feel");
4705 Strcat(fbuf, ":");
4706 (void) display_minventory(mtmp, MINV_ALL | PICK_NONE, fbuf);
4707 } else {
4708 You("%s no objects here.", verb);
4710 return (!!Blind ? ECMD_TIME : ECMD_OK);
4712 if (!skip_objects) {
4713 NhRegion *reg;
4714 char regbuf[QBUFSZ];
4716 regbuf[0] = '\0';
4717 if ((reg = visible_region_at(u.ux, u.uy)) != 0)
4718 Sprintf(regbuf, "a %s cloud",
4719 reg_damg(reg) ? "poison gas" : "vapor");
4720 if ((trap = t_at(u.ux, u.uy)) != 0 && !trap->tseen)
4721 trap = (struct trap *) NULL;
4723 if (reg || trap)
4724 There("is %s%s%s here.",
4725 reg ? regbuf : "",
4726 (reg && trap) ? " and " : "",
4727 trap ? an(trapname(trap->ttyp, FALSE)) : "");
4730 otmp = svl.level.objects[u.ux][u.uy];
4731 dfeature = dfeature_at(u.ux, u.uy, fbuf2);
4732 if (dfeature && !strcmp(dfeature, "pool of water") && Underwater)
4733 dfeature = 0;
4735 if (Blind) {
4736 boolean drift = Is_airlevel(&u.uz) || Is_waterlevel(&u.uz);
4738 if (dfeature && !strncmp(dfeature, "altar ", 6)) {
4739 /* don't say "altar" twice, dfeature has more info */
4740 You("try to feel what is here.");
4741 } else if (SURFACE_AT(u.ux, u.uy) == ICE) {
4742 /* using describe_decor() to handle ice is simpler than
4743 replicating it in the conditional message construction */
4744 if (!flags.mention_decor || iflags.prev_decor == ICE)
4745 force_decor(FALSE);
4746 /* plain "ice" if blind and levitating, otherwise "solid ice" &c;
4747 "There is [thin ]ice here. You try to feel what is on it." */
4748 You("try to feel what is on it.");
4749 skip_dfeature = TRUE; /* ice already described */
4750 } else {
4751 boolean cant_reach = !can_reach_floor(TRUE);
4752 const char *surf = surface(u.ux, u.uy),
4753 *where = cant_reach ? "lying beneath you"
4754 : "lying here on the ",
4755 *onwhat = cant_reach ? "" : surf;
4757 You("try to feel what is %s%s.", drift ? "floating here" : where,
4758 drift ? "" : onwhat);
4760 if (dfeature && !drift && !strcmp(dfeature, surf))
4761 skip_dfeature = TRUE; /* terrain feature already identified */
4763 trap = t_at(u.ux, u.uy);
4764 if (!can_reach_floor(trap && is_pit(trap->ttyp))) {
4765 pline("But you can't reach it!");
4766 return ECMD_OK;
4770 if (dfeature && !skip_dfeature) {
4771 const char *p;
4772 int article = 1; /* 0 => none, 1 => a/an, 2 => the (not used here) */
4774 /* "molten lava", "iron bars", and plain "ice" are handled as special
4775 cases in an() but probably shouldn't be; don't rely on that */
4776 if (!strcmp(dfeature, "molten lava")
4777 || !strcmp(dfeature, "iron bars")
4778 || !strcmp(dfeature, "ice")
4779 || !strncmp(dfeature, "frozen ", 7) /* ice while hallucinating */
4780 /* thawing ice ("solid ice", "thin ice", &c) */
4781 || ((p = strchr(dfeature, ' ')) != 0 && !strcmpi(p, " ice")))
4782 article = 0;
4783 if (article == 1)
4784 dfeature = an(dfeature);
4786 /* hardcoded "is" worked here because "iron bars" is actually
4787 "set of iron bars"; use vtense() instead of relying on that */
4788 Sprintf(fbuf, "There %s %s here.", vtense(dfeature, "are"), dfeature);
4791 if (!otmp || is_lava(u.ux, u.uy)
4792 || (is_pool(u.ux, u.uy) && !Underwater)) {
4793 if (dfeature && !skip_dfeature)
4794 pline1(fbuf);
4795 read_engr_at(u.ux, u.uy); /* Eric Backus */
4796 if (!skip_objects && (Blind || !dfeature))
4797 You("%s no objects here.", verb);
4798 return (!!Blind ? ECMD_TIME : ECMD_OK);
4800 /* we know there is something here */
4802 if (skip_objects) {
4803 if (dfeature && !skip_dfeature)
4804 pline1(fbuf);
4805 read_engr_at(u.ux, u.uy); /* Eric Backus */
4806 if (obj_cnt == 1 && otmp->quan == 1L)
4807 There("is %s object here.", picked_some ? "another" : "an");
4808 else
4809 There("are %s%s objects here.",
4810 (obj_cnt == 2) ? "two"
4811 : (obj_cnt < 5) ? "a few"
4812 : (obj_cnt < 10) ? "several"
4813 : "many",
4814 picked_some ? " more" : "");
4815 for (; otmp; otmp = otmp->nexthere)
4816 if (otmp->otyp == CORPSE && will_feel_cockatrice(otmp, FALSE)) {
4817 pline("%s %s%s.",
4818 (obj_cnt > 1) ? "Including"
4819 : (otmp->quan > 1L) ? "They're"
4820 : "It's",
4821 corpse_xname(otmp, (const char *) 0, CXN_ARTICLE),
4822 poly_when_stoned(gy.youmonst.data) ? ""
4823 : ", unfortunately");
4824 feel_cockatrice(otmp, FALSE);
4825 break;
4827 } else if (!otmp->nexthere) {
4828 /* only one object */
4829 if (dfeature && !skip_dfeature)
4830 pline1(fbuf);
4831 read_engr_at(u.ux, u.uy); /* Eric Backus */
4832 You("%s here %s.", verb, doname_with_price(otmp));
4833 iflags.last_msg = PLNMSG_ONE_ITEM_HERE;
4834 if (otmp->otyp == CORPSE)
4835 feel_cockatrice(otmp, FALSE);
4836 } else {
4837 char buf[BUFSZ];
4839 display_nhwindow(WIN_MESSAGE, FALSE);
4840 tmpwin = create_nhwindow(NHW_MENU);
4841 if (dfeature && !skip_dfeature) {
4842 putstr(tmpwin, 0, fbuf);
4843 putstr(tmpwin, 0, "");
4845 Sprintf(buf, "%s that %s here:",
4846 picked_some ? "Other things" : "Things",
4847 Blind ? "you feel" : "are");
4848 putstr(tmpwin, 0, buf);
4849 for (; otmp; otmp = otmp->nexthere) {
4850 if (otmp->otyp == CORPSE && will_feel_cockatrice(otmp, FALSE)) {
4851 felt_cockatrice = TRUE;
4852 Sprintf(buf, "%s...", doname(otmp));
4853 putstr(tmpwin, 0, buf);
4854 break;
4856 putstr(tmpwin, 0, doname_with_price(otmp));
4858 display_nhwindow(tmpwin, TRUE);
4859 destroy_nhwindow(tmpwin);
4860 if (felt_cockatrice)
4861 feel_cockatrice(otmp, FALSE);
4862 read_engr_at(u.ux, u.uy); /* Eric Backus */
4864 return (!!Blind ? ECMD_TIME : ECMD_OK);
4867 /* #look command - explicitly look at what is here, including all objects */
4869 dolook(void)
4871 int res;
4873 /* don't let
4874 MSGTYPE={norep,noshow} "You see here"
4875 interfere with feedback from the look-here command */
4876 hide_unhide_msgtypes(TRUE, MSGTYP_MASK_REP_SHOW);
4877 res = look_here(0, LOOKHERE_NOFLAGS);
4878 /* restore normal msgtype handling */
4879 hide_unhide_msgtypes(FALSE, MSGTYP_MASK_REP_SHOW);
4880 return res;
4883 boolean
4884 will_feel_cockatrice(struct obj *otmp, boolean force_touch)
4886 if ((Blind || force_touch) && !uarmg && !Stone_resistance
4887 && (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm])))
4888 return TRUE;
4889 return FALSE;
4892 void
4893 feel_cockatrice(struct obj *otmp, boolean force_touch)
4895 char kbuf[BUFSZ];
4897 if (will_feel_cockatrice(otmp, force_touch)) {
4898 /* "the <cockatrice> corpse" */
4899 Strcpy(kbuf, corpse_xname(otmp, (const char *) 0, CXN_PFX_THE));
4901 if (poly_when_stoned(gy.youmonst.data))
4902 You("touched %s with your bare %s.", kbuf,
4903 makeplural(body_part(HAND)));
4904 else
4905 pline("Touching %s is a fatal mistake...", kbuf);
4906 /* normalize body shape here; hand, not body_part(HAND) */
4907 Sprintf(kbuf, "touching %s bare-handed", killer_xname(otmp));
4908 /* will call polymon() for the poly_when_stoned() case */
4909 instapetrify(kbuf);
4913 /* 'obj' is being placed on the floor; if it can merge with something that
4914 is already there, combine them and discard obj as a separate object */
4915 void
4916 stackobj(struct obj *obj)
4918 struct obj *otmp;
4920 for (otmp = svl.level.objects[obj->ox][obj->oy]; otmp;
4921 otmp = otmp->nexthere)
4922 if (otmp != obj && merged(&obj, &otmp))
4923 break;
4924 return;
4927 /* returns TRUE if obj & otmp can be merged; used in invent.c and mkobj.c */
4928 boolean
4929 mergable(
4930 struct obj *otmp, /* potential 'into' stack */
4931 struct obj *obj) /* 'combine' stack */
4933 size_t objnamelth = 0, otmpnamelth = 0;
4935 /* fail if already the same object, if different types, if either is
4936 explicitly marked to prevent merge, or if not mergable in general */
4937 if (obj == otmp || obj->otyp != otmp->otyp
4938 || obj->nomerge || otmp->nomerge || !objects[obj->otyp].oc_merge)
4939 return FALSE;
4941 /* coins of the same kind will always merge */
4942 if (obj->oclass == COIN_CLASS)
4943 return TRUE;
4945 if (obj->cursed != otmp->cursed || obj->blessed != otmp->blessed)
4946 return FALSE;
4947 if ((obj->how_lost & ~LOSTOVERRIDEMASK) != 0)
4948 return FALSE;
4949 #if 0 /* don't require 'bypass' to match; that results in items dropped
4950 * via 'D' not stacking with compatible items already on the floor;
4951 * caller who wants that behavior should use 'nomerge' instead */
4952 if (obj->bypass != otmp->bypass)
4953 return FALSE;
4954 #endif
4956 if (obj->globby)
4957 return TRUE;
4958 /* Checks beyond this point either aren't applicable to globs
4959 * or don't inhibit their merger.
4962 if (obj->unpaid != otmp->unpaid || obj->spe != otmp->spe
4963 || obj->no_charge != otmp->no_charge || obj->obroken != otmp->obroken
4964 || obj->otrapped != otmp->otrapped || obj->lamplit != otmp->lamplit
4965 || obj->how_lost != otmp->how_lost)
4966 return FALSE;
4968 if (obj->oclass == FOOD_CLASS
4969 && (obj->oeaten != otmp->oeaten || obj->orotten != otmp->orotten))
4970 return FALSE;
4972 if (obj->dknown != otmp->dknown
4973 || (obj->bknown != otmp->bknown && !Role_if(PM_CLERIC) &&
4974 (Blind || Hallucination))
4975 || obj->oeroded != otmp->oeroded || obj->oeroded2 != otmp->oeroded2
4976 || obj->greased != otmp->greased)
4977 return FALSE;
4979 if ((erosion_matters(obj))
4980 && (obj->oerodeproof != otmp->oerodeproof
4981 || (obj->rknown != otmp->rknown && (Blind || Hallucination))))
4982 return FALSE;
4984 if (obj->otyp == CORPSE || obj->otyp == EGG || obj->otyp == TIN) {
4985 if (obj->corpsenm != otmp->corpsenm)
4986 return FALSE;
4989 /* hatching eggs don't merge; ditto for revivable corpses */
4990 if ((obj->otyp == EGG && (obj->timed || otmp->timed))
4991 || (obj->otyp == CORPSE && otmp->corpsenm >= LOW_PM
4992 && is_reviver(&mons[otmp->corpsenm])))
4993 return FALSE;
4995 /* allow candle merging only if their ages are close */
4996 /* see begin_burn() for a reference for the magic "25" */
4997 if (Is_candle(obj) && obj->age / 25 != otmp->age / 25)
4998 return FALSE;
5000 /* burning potions of oil never merge */
5001 if (obj->otyp == POT_OIL && obj->lamplit)
5002 return FALSE;
5004 /* don't merge surcharged item with base-cost item */
5005 if (obj->unpaid && !same_price(obj, otmp))
5006 return FALSE;
5008 /* some additional information is always incompatible */
5009 if (has_omonst(obj) || has_omid(obj)
5010 || has_omonst(otmp) || has_omid(otmp))
5011 return FALSE;
5013 /* if they have names, make sure they're the same */
5014 objnamelth = strlen(safe_oname(obj));
5015 otmpnamelth = strlen(safe_oname(otmp));
5016 if ((objnamelth != otmpnamelth
5017 && ((objnamelth && otmpnamelth) || obj->otyp == CORPSE))
5018 || (objnamelth && otmpnamelth
5019 /* verify pointers before deref for static analyzer */
5020 && has_oname(obj) && has_oname(otmp)
5021 && strncmp(ONAME(obj), ONAME(otmp), objnamelth)))
5022 return FALSE;
5024 /* if one has an attached mail command, other must have same command */
5025 if (!has_omailcmd(obj) ? has_omailcmd(otmp)
5026 : (!has_omailcmd(otmp) || strcmp(OMAILCMD(obj), OMAILCMD(otmp)) != 0))
5027 return FALSE;
5029 #ifdef MAIL_STRUCTURES
5030 if (obj->otyp == SCR_MAIL
5031 /* wished or bones mail and hand written stamped scrolls
5032 each have two flavors; spe keeps them separate from each
5033 other but we want to keep their flavors separate too */
5034 && obj->spe > 0 && (obj->o_id % 2) != (otmp->o_id % 2))
5035 return FALSE;
5036 #endif
5038 /* should be moot since matching artifacts wouldn't be unique */
5039 if (obj->oartifact != otmp->oartifact)
5040 return FALSE;
5042 if (obj->known != otmp->known && (Blind || Hallucination))
5043 return FALSE;
5045 return TRUE;
5048 /* the #showgold command */
5050 doprgold(void)
5052 /* Command takes containers into account. */
5053 long umoney = money_cnt(gi.invent);
5055 /* Only list the money you know about. Guards and shopkeepers
5056 can somehow tell if there is any gold anywhere on your
5057 person, but you have no such preternatural gold-sense. */
5058 long hmoney = hidden_gold(FALSE);
5060 if (flags.verbose) {
5061 char buf[BUFSZ];
5063 if (!umoney) {
5064 Strcpy(buf, "Your wallet is empty");
5065 } else {
5066 Sprintf(buf, "Your wallet contains %ld %s",
5067 umoney, currency(umoney));
5069 if (hmoney) {
5070 Sprintf(eos(buf),
5071 ", %s you have %ld %s stashed away in your pack",
5072 umoney ? "and" : "but", hmoney,
5073 umoney ? "more" : currency(hmoney));
5075 pline("%s.", buf);
5076 } else {
5077 long total = umoney + hmoney;
5078 if (total)
5079 You("are carrying a total of %ld %s.", total, currency(total));
5080 else
5081 You("have no money.");
5083 shopper_financial_report();
5085 if (umoney && iflags.menu_requested) {
5086 char dollarsign[] = "$";
5088 /* mustn't use TRUE or gold wouldn't show up unless it was quivered */
5089 (void) dispinv_with_action(dollarsign, FALSE, NULL);
5092 return ECMD_OK;
5095 /* the #seeweapon command */
5097 doprwep(void)
5099 if (!uwep) {
5100 You("are %s.", empty_handed());
5101 } else if (!iflags.menu_requested) {
5102 prinv((char *) 0, uwep, 0L);
5103 if (u.twoweap)
5104 prinv((char *) 0, uswapwep, 0L);
5105 } else {
5106 char lets[4]; /* 4: uwep, uswapwep, uquiver, terminator */
5107 int ct = 0;
5109 /* obj_to_let() will assign letters to all of invent if necessary
5110 (for '!fixinv') so doesn't need to be repeated once called here */
5111 lets[ct++] = obj_to_let(uwep);
5112 if (uswapwep)
5113 lets[ct++] = uswapwep->invlet;
5114 if (uquiver)
5115 lets[ct++] = uquiver->invlet;
5116 lets[ct] = '\0';
5118 (void) dispinv_with_action(lets, TRUE, NULL);
5120 return ECMD_OK;
5123 /* caller is responsible for checking !wearing_armor() */
5124 staticfn void
5125 noarmor(boolean report_uskin)
5127 if (!uskin || !report_uskin) {
5128 You("are not wearing any armor.");
5129 } else {
5130 char *p, *uskinname, buf[BUFSZ];
5132 uskinname = strcpy(buf, simpleonames(uskin));
5133 /* shorten "set of <color> dragon scales" to "<color> scales"
5134 and "<color> dragon scale mail" to "<color> scale mail" */
5135 if (!strncmpi(uskinname, "set of ", 7))
5136 uskinname += 7;
5137 if ((p = strstri(uskinname, " dragon ")) != 0)
5138 while ((p[1] = p[8]) != '\0')
5139 ++p;
5141 You("are not wearing armor but have %s embedded in your skin.",
5142 uskinname);
5146 /* the #seearmor command */
5148 doprarm(void)
5151 * Note: players sometimes get here by pressing a function key which
5152 * transmits ''ESC [ <something>'' rather than by pressing '[';
5153 * there's nothing we can--or should-do about that here.
5156 if (!wearing_armor()) {
5157 noarmor(TRUE);
5158 } else {
5159 char lets[8]; /* 8: up to 7 pieces of armor plus terminator */
5160 int ct = 0;
5162 /* obj_to_let() will assign letters to all of invent if necessary
5163 (for '!fixinv') so doesn't need to be repeated once called, but
5164 each armor slot doesn't know whether any that precede have made
5165 that call so just do it for each one; use SORTPACK_INUSE order */
5166 if (uarm)
5167 lets[ct++] = obj_to_let(uarm);
5168 if (uarmc)
5169 lets[ct++] = obj_to_let(uarmc);
5170 if (uarms)
5171 lets[ct++] = obj_to_let(uarms);
5172 if (uarmh)
5173 lets[ct++] = obj_to_let(uarmh);
5174 if (uarmg)
5175 lets[ct++] = obj_to_let(uarmg);
5176 if (uarmf)
5177 lets[ct++] = obj_to_let(uarmf);
5178 if (uarmu)
5179 lets[ct++] = obj_to_let(uarmu);
5180 lets[ct] = 0;
5182 (void) dispinv_with_action(lets, TRUE, NULL);
5184 return ECMD_OK;
5187 /* the #seerings command */
5189 doprring(void)
5191 if (!uleft && !uright) {
5192 You("are not wearing any rings.");
5193 } else {
5194 char lets[3]; /* 3: uright, uleft, terminator */
5195 boolean use_inuse_mode = FALSE;
5196 int ct = 0;
5198 /* if either ring is a meat ring, switch to use_inuse_mode in order
5199 to label it/them as "Rings" rather than "Comestibles" */
5200 if (uright) {
5201 lets[ct++] = obj_to_let(uright);
5202 if (uright->oclass != RING_CLASS)
5203 use_inuse_mode = TRUE;
5205 if (uleft) {
5206 lets[ct++] = obj_to_let(uleft);
5207 if (uleft->oclass != RING_CLASS)
5208 use_inuse_mode = TRUE;
5210 lets[ct] = '\0';
5211 /* also switch to use_inuse_mode if there are two rings or player
5212 used the 'm' prefix */
5213 if (ct > 1 || iflags.menu_requested)
5214 use_inuse_mode = TRUE;
5216 (void) dispinv_with_action(lets, use_inuse_mode,
5217 /* note; alternate label will be ignored
5218 if 'use_inuse_mode' is False */
5219 (ct == 1) ? "Ring" : "Rings");
5221 return ECMD_OK;
5224 /* the #seeamulet command */
5226 dopramulet(void)
5228 if (!uamul) {
5229 You("are not wearing an amulet.");
5230 } else {
5231 char lets[2];
5233 /* using display_inventory() instead of prinv() allows player
5234 to use 'm "' to force and menu and be able to choose amulet
5235 in order to perform a context-sensitive item action */
5236 lets[0] = obj_to_let(uamul), lets[1] = '\0';
5238 (void) dispinv_with_action(lets, TRUE, "Amulet");
5240 return ECMD_OK;
5243 /* is 'obj' a tool that's in use? can't simply check obj->owornmask */
5244 staticfn boolean
5245 tool_being_used(struct obj *obj)
5248 * [Should this also include lit potions of oil? They're not tools
5249 * but they are "in use" without being noticeable via obj->owornmask.]
5251 if ((obj->owornmask & (W_TOOL | W_SADDLE)) != 0L)
5252 return TRUE;
5253 if (obj->oclass != TOOL_CLASS)
5254 return FALSE;
5255 /* [don't actually need to check uwep here; caller catches it] */
5256 return (boolean) (obj == uwep || obj->lamplit
5257 || (obj->otyp == LEASH && obj->leashmon));
5260 /* the #seetools command */
5262 doprtool(void)
5264 struct obj *otmp;
5265 int ct = 0;
5266 char lets[invlet_basic + 1];
5268 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
5269 if (tool_being_used(otmp)) {
5270 /* we could be carrying more than 52 items; theoretically they
5271 might all be lit candles so avoid potential lets[] overflow */
5272 if (ct >= (int) sizeof lets - 1)
5273 break;
5274 lets[ct++] = obj_to_let(otmp);
5276 lets[ct] = '\0';
5277 if (!ct)
5278 You("are not using any tools.");
5279 else
5280 (void) dispinv_with_action(lets, TRUE, NULL);
5281 return ECMD_OK;
5284 /* the #seeall command; combines the ')' + '[' + '=' + '"' + '(' commands;
5285 show inventory of all currently wielded, worn, or used objects */
5287 doprinuse(void)
5289 struct obj *otmp;
5290 int ct = 0;
5292 /* no longer need to collect letters; sortloot() takes care of it, but
5293 still want to count far enough to know whether anything is in use */
5294 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
5295 if (is_inuse(otmp)) {
5296 ++ct;
5297 break;
5299 if (!ct)
5300 You("are not wearing or wielding anything.");
5301 else
5302 (void) dispinv_with_action((char *) 0, TRUE, NULL);
5303 return ECMD_OK;
5307 * uses up an object that's on the floor, charging for it as necessary
5309 void
5310 useupf(struct obj *obj, long numused)
5312 struct obj *otmp;
5313 boolean at_u = u_at(obj->ox, obj->oy);
5315 /* burn_floor_objects() keeps an object pointer that it tries to
5316 * useupf() multiple times, so obj must survive if plural */
5317 if (obj->quan > numused)
5318 otmp = splitobj(obj, numused);
5319 else
5320 otmp = obj;
5321 if (!svc.context.mon_moving && costly_spot(otmp->ox, otmp->oy)) {
5322 if (strchr(u.urooms, *in_rooms(otmp->ox, otmp->oy, 0)))
5323 addtobill(otmp, FALSE, FALSE, FALSE);
5324 else
5325 (void) stolen_value(otmp, otmp->ox, otmp->oy, FALSE, FALSE);
5327 delobj(otmp);
5328 if (at_u && u.uundetected && hides_under(gy.youmonst.data))
5329 (void) hideunder(&gy.youmonst);
5333 * Conversion from a class to a string for printing.
5334 * This must match the object class order.
5336 static NEARDATA const char *names[] = {
5337 0, "Illegal objects", "Weapons", "Armor", "Rings", "Amulets", "Tools",
5338 "Comestibles", "Potions", "Scrolls", "Spellbooks", "Wands", "Coins",
5339 "Gems/Stones", "Boulders/Statues", "Iron balls", "Chains", "Venoms"
5341 static NEARDATA const char oth_symbols[] = { CONTAINED_SYM, '\0' };
5342 static NEARDATA const char *oth_names[] = { "Bagged/Boxed items" };
5344 DISABLE_WARNING_FORMAT_NONLITERAL
5346 char *
5347 let_to_name(char let, boolean unpaid, boolean showsym)
5349 const char *ocsymfmt = " ('%c')";
5350 const int invbuf_sympadding = 8; /* arbitrary */
5351 const char *class_name;
5352 const char *pos;
5353 int oclass = (let >= 1 && let < MAXOCLASSES) ? let : 0;
5354 unsigned len;
5356 if (oclass)
5357 class_name = names[oclass];
5358 else if ((pos = strchr(oth_symbols, let)) != 0)
5359 class_name = oth_names[pos - oth_symbols];
5360 else
5361 class_name = names[ILLOBJ_CLASS];
5363 len = Strlen(class_name) + (unpaid ? sizeof "unpaid_" : sizeof "")
5364 + (oclass ? (Strlen(ocsymfmt) + invbuf_sympadding) : 0);
5365 if (len > gi.invbufsiz) {
5366 if (gi.invbuf)
5367 free((genericptr_t) gi.invbuf);
5368 gi.invbufsiz = len + 10; /* add slop to reduce incremental realloc */
5369 gi.invbuf = (char *) alloc(gi.invbufsiz);
5371 if (unpaid)
5372 Strcat(strcpy(gi.invbuf, "Unpaid "), class_name);
5373 else
5374 Strcpy(gi.invbuf, class_name);
5375 if ((oclass != 0) && showsym) {
5376 char *bp = eos(gi.invbuf);
5377 int mlen = invbuf_sympadding - Strlen(class_name);
5378 while (--mlen > 0) {
5379 *bp = ' ';
5380 bp++;
5382 *bp = '\0';
5383 Sprintf(eos(gi.invbuf), ocsymfmt, def_oc_syms[oclass].sym);
5385 return gi.invbuf;
5388 RESTORE_WARNING_FORMAT_NONLITERAL
5390 /* release the static buffer used by let_to_name() */
5391 void
5392 free_invbuf(void)
5394 if (gi.invbuf)
5395 free((genericptr_t) gi.invbuf), gi.invbuf = (char *) 0;
5396 gi.invbufsiz = 0;
5399 /* give consecutive letters to every item in inventory (for !fixinv mode);
5400 gold is always forced to '$' slot at head of list */
5401 void
5402 reassign(void)
5404 int i;
5405 struct obj *obj, *prevobj, *goldobj;
5407 /* first, remove [first instance of] gold from invent, if present */
5408 prevobj = goldobj = 0;
5409 for (obj = gi.invent; obj; prevobj = obj, obj = obj->nobj)
5410 if (obj->oclass == COIN_CLASS) {
5411 goldobj = obj;
5412 if (prevobj)
5413 prevobj->nobj = goldobj->nobj;
5414 else
5415 gi.invent = goldobj->nobj;
5416 break;
5418 /* second, re-letter the rest of the list */
5419 for (obj = gi.invent, i = 0; obj; obj = obj->nobj, i++)
5420 obj->invlet =
5421 (i < 26) ? ('a' + i) : (i < 52) ? ('A' + i - 26) : NOINVSYM;
5422 /* third, assign gold the "letter" '$' and re-insert it at head */
5423 if (goldobj) {
5424 goldobj->invlet = GOLD_SYM;
5425 goldobj->nobj = gi.invent;
5426 gi.invent = goldobj;
5428 if (i >= 52)
5429 i = 52 - 1;
5430 gl.lastinvnr = i;
5433 /* invent gold sanity check; used by doorganize() to control how getobj()
5434 deals with gold and also by wizard mode sanity_check() */
5435 boolean
5436 check_invent_gold(const char *why) /* 'why' == caller in case of warning */
5438 struct obj *otmp;
5439 int goldstacks = 0, wrongslot = 0;
5441 /* there should be at most one stack of gold in invent, in slot '$' */
5442 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
5443 if (otmp->oclass == COIN_CLASS) {
5444 ++goldstacks;
5445 if (otmp->invlet != GOLD_SYM)
5446 ++wrongslot;
5449 if (goldstacks > 1 || wrongslot > 0) {
5450 impossible("%s: %s%s%s", why,
5451 (wrongslot > 1) ? "gold in wrong slots"
5452 : (wrongslot > 0) ? "gold in wrong slot"
5453 : "",
5454 (wrongslot > 0 && goldstacks > 1) ? " and " : "",
5455 (goldstacks > 1) ? "multiple gold stacks" : "");
5456 return TRUE; /* gold can be #adjusted */
5459 return FALSE; /* gold can't be #adjusted */
5462 /* normal getobj callback for item to #adjust; excludes gold */
5463 staticfn int
5464 adjust_ok(struct obj *obj)
5466 if (!obj || obj->oclass == COIN_CLASS)
5467 return GETOBJ_EXCLUDE;
5469 return GETOBJ_SUGGEST;
5472 /* getobj callback for item to #adjust if gold is wonky; allows gold */
5473 staticfn int
5474 adjust_gold_ok(struct obj *obj)
5476 if (!obj)
5477 return GETOBJ_EXCLUDE;
5479 return GETOBJ_SUGGEST;
5482 /* #adjust command
5484 * User specifies a 'from' slot for inventory stack to move,
5485 * then a 'to' slot for its destination. Open slots and those
5486 * filled by compatible stacks are listed as likely candidates
5487 * but user can pick any inventory letter (including 'from').
5489 * to == from, 'from' has a name
5490 * All compatible items (same name or no name) are gathered
5491 * into the 'from' stack. No count is allowed.
5492 * to == from, 'from' does not have a name
5493 * All compatible items without a name are gathered into the
5494 * 'from' stack. No count is allowed. Compatible stacks with
5495 * names are left as-is.
5496 * to != from, no count
5497 * Move 'from' to 'to'. If 'to' is not empty, merge 'from'
5498 * into it if possible, otherwise swap it with the 'from' slot.
5499 * to != from, count given
5500 * If the user specifies a count when choosing the 'from' slot,
5501 * and that count is less than the full size of the stack,
5502 * then the stack will be split. The 'count' portion is moved
5503 * to the destination, and the only candidate for merging with
5504 * it is the stack already at the 'to' slot, if any. When the
5505 * destination is non-empty but won't merge, whatever is there
5506 * will be moved to an open slot; if there isn't any open slot
5507 * available, the adjustment attempt fails.
5509 * To minimize merging for 'from == to', unnamed stacks will
5510 * merge with named 'from' but named ones won't merge with
5511 * unnamed 'from'. Otherwise attempting to collect all unnamed
5512 * stacks would lump the first compatible named stack with them
5513 * and give them its name.
5515 * To maximize merging for 'from != to', compatible stacks will
5516 * merge when either lacks a name (or they already have the same
5517 * name). When no count is given and one stack has a name and
5518 * the other doesn't, the merged result will have that name.
5519 * However, when splitting results in a merger, the name of the
5520 * destination overrides that of the source, even if destination
5521 * is unnamed and source is named.
5523 * Gold is only a candidate to adjust if we've somehow managed
5524 * to get multiple stacks and/or it is in a slot other than '$'.
5525 * Specifying a count to split it into two stacks is not allowed.
5528 doorganize(void) /* inventory organizer by Del Lamb */
5530 int (*adjust_filter)(struct obj *);
5531 struct obj *obj;
5533 /* when no invent, or just gold in '$' slot, there's nothing to adjust */
5534 if (!gi.invent || (gi.invent->oclass == COIN_CLASS
5535 && gi.invent->invlet == GOLD_SYM && !gi.invent->nobj)) {
5536 You("aren't carrying anything %s.",
5537 !gi.invent ? "to adjust" : "adjustable");
5538 return ECMD_OK;
5541 if (!flags.invlet_constant)
5542 reassign();
5544 /* filter passed to getobj() depends upon gold sanity */
5545 adjust_filter = check_invent_gold("adjust") ? adjust_gold_ok : adjust_ok;
5547 /* get object the user wants to organize (the 'from' slot) */
5548 obj = getobj("adjust", adjust_filter, GETOBJ_PROMPT | GETOBJ_ALLOWCNT);
5550 return doorganize_core(obj);
5553 /* alternate version of #adjust used by itemactions() for splitting */
5555 adjust_split(void)
5557 struct obj *obj;
5558 cmdcount_nht splitamount = 0L;
5559 char let, dig = '\0';
5561 /* invlet should be queued so no getobj prompting is expected */
5562 obj = getobj("split", adjust_ok, GETOBJ_NOFLAGS);
5563 if (!obj || obj->quan < 2L || obj->otyp == GOLD_PIECE)
5564 return ECMD_FAIL; /* caller has set things up to avoid this */
5566 if (obj->quan == 2L) {
5567 splitamount = 1L;
5568 } else {
5569 /* get first digit; doesn't wait for <return> */
5570 dig = yn_function("Split off how many?", (char *) 0, '\0', TRUE);
5571 if (!digit(dig)) {
5572 pline1(Never_mind);
5573 return ECMD_CANCEL;
5575 /* got first digit, get more until next non-digit (except for
5576 backspace/delete which will take away most recent digit and
5577 keep going; we expect one of ' ', '\n', or '\r') */
5578 let = get_count(NULL, dig, 0L, &splitamount,
5579 /* yn_function() added the first digit to the
5580 prompt when recording message history; have
5581 get_count() display "Count: N" when waiting
5582 for additional digits (ordinarily that won't be
5583 shown until a second digit is entered) and also
5584 add "Count: N" to message history if more than
5585 one digit gets entered or the original N is
5586 deleted and replaced with different digit */
5587 GC_ECHOFIRST | GC_CONDHIST);
5588 /* \033 is in quitchars[] so we need to check for it separately
5589 in order to treat it as cancel rather than as accept */
5590 if (!let || let == '\033' || !strchr(quitchars, let)) {
5591 pline1(Never_mind);
5592 return ECMD_CANCEL;
5595 if (splitamount < 1L || splitamount >= obj->quan) {
5596 static const char
5597 Amount[] = "Amount to split from current stack must be";
5599 if (splitamount < 1L)
5600 pline("%s at least 1.", Amount);
5601 else
5602 pline("%s less than %ld.", Amount, obj->quan);
5603 return ECMD_CANCEL;
5606 /* normally a split would take place in getobj() if player supplies
5607 a count there, so doorganize_core() figures out 'splitamount'
5608 from the object; it will undo the split if player cancels while
5609 selecting the destination slot */
5610 obj = splitobj(obj, (long) splitamount);
5611 return doorganize_core(obj);
5614 staticfn int
5615 doorganize_core(struct obj *obj)
5617 struct obj *otmp, *splitting, *bumped;
5618 int ix, cur, trycnt;
5619 char let;
5620 #define GOLD_INDX 0
5621 #define GOLD_OFFSET 1
5622 #define OVRFLW_INDX (GOLD_OFFSET + invlet_basic) /* past gold+2*26 letters */
5623 char lets[1 + invlet_basic + 1 + 1]; /* room for '$a-zA-Z#\0' */
5624 char qbuf[QBUFSZ];
5625 char *objname, *otmpname;
5626 const char *adj_type;
5627 boolean ever_mind = FALSE, collect, isgold;
5629 if (!obj)
5630 return ECMD_CANCEL;
5632 /* can only be gold if check_invent_gold() found a problem: multiple '$'
5633 stacks and/or gold in some other slot, otherwise (*adjust_filter)()
5634 won't allow gold to be picked; if player has picked any stack of gold
5635 as #adjust 'from' slot, we'll force the 'to' slot to be '$' below */
5636 isgold = (obj->oclass == COIN_CLASS);
5638 /* figure out whether user gave a split count to getobj() */
5639 splitting = bumped = 0;
5640 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
5641 if (otmp->nobj == obj) { /* knowledge of splitobj() operation */
5642 if (otmp->invlet == obj->invlet)
5643 splitting = otmp;
5644 break;
5647 /* initialize the list with all lower and upper case letters */
5648 lets[GOLD_INDX] = (obj->oclass == COIN_CLASS) ? GOLD_SYM : ' ';
5649 for (ix = GOLD_OFFSET, let = 'a'; let <= 'z';)
5650 lets[ix++] = let++;
5651 for (let = 'A'; let <= 'Z';)
5652 lets[ix++] = let++;
5653 lets[OVRFLW_INDX] = ' ';
5654 lets[sizeof lets - 1] = '\0';
5655 /* for floating inv letters, truncate list after the first open slot */
5656 if (!flags.invlet_constant && (ix = inv_cnt(FALSE)) < invlet_basic)
5657 lets[ix + (splitting ? 1 : 2)] = '\0';
5659 /* blank out all the letters currently in use in the inventory
5660 except those that will be merged with the selected object */
5661 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
5662 if (otmp != obj && !mergable(otmp, obj)) {
5663 let = otmp->invlet;
5664 if (let >= 'a' && let <= 'z')
5665 lets[GOLD_OFFSET + let - 'a'] = ' ';
5666 else if (let >= 'A' && let <= 'Z')
5667 lets[GOLD_OFFSET + let - 'A' + 26] = ' ';
5668 /* overflow defaults to off, but it we find a stack using that
5669 slot, switch to on -- the opposite of normal invlet handling */
5670 else if (let == NOINVSYM)
5671 lets[OVRFLW_INDX] = NOINVSYM;
5674 /* compact the list by removing all the blanks */
5675 for (ix = cur = 0; lets[ix]; ix++)
5676 if (lets[ix] != ' ' && cur++ < ix)
5677 lets[cur - 1] = lets[ix];
5678 lets[cur] = '\0';
5679 /* and by dashing runs of letters */
5680 if (cur > 5)
5681 compactify(lets);
5683 /* get 'to' slot to use as destination */
5684 if (!splitting)
5685 Strcpy(qbuf, "Adjust letter");
5686 else /* note: splitting->quan is the amount being left in original slot */
5687 Sprintf(qbuf, "Split %ld", obj->quan);
5688 Sprintf(eos(qbuf), " to what [%s]%s?", lets,
5689 gi.invent ? " (? see used letters)" : "");
5690 for (trycnt = 1; ; ++trycnt) {
5691 let = !isgold ? yn_function(qbuf, (char *) 0, '\0', TRUE) : GOLD_SYM;
5692 if (let == '?' || let == '*') {
5693 let = display_used_invlets(splitting ? obj->invlet : 0);
5694 if (!let)
5695 continue;
5696 if (let == '\033')
5697 goto noadjust;
5699 if (strchr(quitchars, let)
5700 /* adjusting to same slot is meaningful since all
5701 compatible stacks get collected along the way,
5702 but splitting to same slot is not */
5703 || (splitting && let == obj->invlet)) {
5704 noadjust:
5705 if (splitting)
5706 (void) merged(&splitting, &obj);
5707 if (!ever_mind)
5708 pline1(Never_mind);
5709 return ECMD_OK;
5710 } else if (let == GOLD_SYM && obj->oclass != COIN_CLASS) {
5711 pline("Only gold coins may be moved into the '%c' slot.",
5712 GOLD_SYM);
5713 ever_mind = TRUE;
5714 goto noadjust;
5716 /* letter() classifies '@' as one; compactify() can put '-' in lets;
5717 the only thing of interest that strchr() might find is '$' or '#'
5718 since letter() catches everything else that we put into lets[] */
5719 if ((letter(let) && let != '@') || (strchr(lets, let) && let != '-'))
5720 break; /* got one */
5721 if (trycnt == 5)
5722 goto noadjust;
5723 pline("Select an inventory slot letter."); /* else try again */
5726 collect = (let == obj->invlet);
5727 /* change the inventory and print the resulting item */
5728 adj_type = collect ? "Collecting:"
5729 : !splitting ? "Moving:"
5730 : "Splitting:";
5733 * don't use freeinv/addinv to avoid double-touching artifacts,
5734 * dousing lamps, losing luck, cursing loadstone, etc.
5736 extract_nobj(obj, &gi.invent);
5738 for (otmp = gi.invent; otmp; ) {
5739 otmpname = has_oname(otmp) ? ONAME(otmp) : (char *) 0;
5740 /* it's tempting to pull this outside the loop, but merged() could
5741 free ONAME(obj) [via obfree()] and replace it with ONAME(otmp) */
5742 objname = has_oname(obj) ? ONAME(obj) : (char *) 0;
5744 if (collect) {
5745 /* Collecting: #adjust an inventory stack into its same slot;
5746 keep it there and merge other compatible stacks into it.
5747 Traditional inventory behavior is to merge unnamed stacks
5748 with compatible named ones; we only want that if it is
5749 the 'from' stack (obj) with a name and candidate (otmp)
5750 without one, not unnamed 'from' with named candidate. */
5751 if ((!otmpname || (objname && !strcmp(objname, otmpname)))
5752 && merged(&otmp, &obj)) {
5753 /*adj_type = "Collecting:"; //already set to this*/
5754 obj = otmp;
5755 otmp = otmp->nobj;
5756 extract_nobj(obj, &gi.invent);
5757 continue; /* otmp has already been updated */
5759 } else if (otmp->invlet == let) {
5760 /* Merging: when from and to are compatible */
5761 if ((!otmpname || (objname && !strcmp(objname, otmpname)))
5762 && merged(&otmp, &obj)) {
5763 adj_type = "Merging:";
5764 obj = otmp;
5765 otmp = otmp->nobj;
5766 extract_nobj(obj, &gi.invent);
5767 break; /* otmp has been updated and we're done merging */
5769 /* Moving or splitting: don't merge extra compatible stacks.
5770 Found 'otmp' in destination slot; merge if compatible,
5771 otherwise bump whatever is there to an open slot. */
5772 if (!splitting) {
5773 adj_type = "Swapping:";
5774 otmp->invlet = obj->invlet;
5775 } else {
5776 /* strip 'from' name if it has one */
5777 if (objname && !obj->oartifact)
5778 ONAME(obj) = (char *) 0;
5779 if (!mergable(otmp, obj)) {
5780 /* won't merge; put 'from' name back */
5781 if (objname)
5782 ONAME(obj) = objname;
5783 } else {
5784 /* will merge; discard 'from' name */
5785 if (objname)
5786 free((genericptr_t) objname), objname = 0;
5789 if (merged(&otmp, &obj)) {
5790 adj_type = "Splitting and merging:";
5791 obj = otmp;
5792 extract_nobj(obj, &gi.invent);
5793 } else if (inv_cnt(FALSE) >= invlet_basic) {
5794 (void) merged(&splitting, &obj); /* undo split */
5795 /* "knapsack cannot accommodate any more items" */
5796 Your("pack is too full.");
5797 return ECMD_OK;
5798 } else {
5799 bumped = otmp;
5800 extract_nobj(bumped, &gi.invent);
5802 } /* moving vs splitting */
5803 break; /* not collecting and found 'to' slot */
5804 } /* collect */
5805 otmp = otmp->nobj;
5808 /* inline addinv; insert loose object at beginning of inventory */
5809 obj->invlet = let;
5810 obj->nobj = gi.invent;
5811 obj->where = OBJ_INVENT;
5812 gi.invent = obj;
5813 reorder_invent();
5814 if (bumped) {
5815 /* splitting the 'from' stack is causing an incompatible
5816 stack in the 'to' slot to be moved into an open one;
5817 we need to do another inline insertion to inventory */
5818 assigninvlet(bumped);
5819 bumped->nobj = gi.invent;
5820 bumped->where = OBJ_INVENT;
5821 gi.invent = bumped;
5822 reorder_invent();
5825 /* messages deferred until inventory has been fully reestablished */
5826 prinv(adj_type, obj, 0L);
5827 if (bumped)
5828 prinv("Moving:", bumped, 0L);
5829 if (splitting)
5830 clear_splitobjs(); /* reset splitobj context */
5831 update_inventory();
5832 return ECMD_OK;
5835 /* common to display_minventory and display_cinventory */
5836 staticfn void
5837 invdisp_nothing(const char *hdr, const char *txt)
5839 winid win;
5840 menu_item *selected;
5842 win = create_nhwindow(NHW_MENU);
5843 start_menu(win, MENU_BEHAVE_STANDARD);
5844 add_menu_heading(win, hdr);
5845 add_menu_str(win, "");
5846 add_menu_str(win, txt);
5847 end_menu(win, (char *) 0);
5848 if (select_menu(win, PICK_NONE, &selected) > 0)
5849 free((genericptr_t) selected);
5850 destroy_nhwindow(win);
5851 return;
5854 /* query_objlist callback: return things that are worn or wielded */
5855 staticfn boolean
5856 worn_wield_only(struct obj *obj)
5858 #if 1
5859 /* check for things that *are* worn or wielded (only used for monsters,
5860 so we don't worry about excluding W_CHAIN, W_ARTI and the like) */
5861 return (boolean) (obj->owornmask != 0L);
5862 #else
5863 /* this used to check for things that *might* be worn or wielded,
5864 but that's not particularly interesting */
5865 if (is_weptool(obj) || is_wet_towel(obj) || obj->otyp == MEAT_RING)
5866 return TRUE;
5867 return (boolean) (obj->oclass == WEAPON_CLASS
5868 || obj->oclass == ARMOR_CLASS
5869 || obj->oclass == AMULET_CLASS
5870 || obj->oclass == RING_CLASS);
5871 #endif
5875 * Display a monster's inventory.
5876 * Returns a pointer to the object from the monster's inventory selected
5877 * or NULL if nothing was selected.
5879 * By default, only worn and wielded items are displayed. The caller
5880 * can pick one. Modifier flags are:
5882 * PICK_NONE, PICK_ONE - standard menu control
5883 * PICK_ANY - allowed, but we only return a single object
5884 * MINV_NOLET - nothing selectable
5885 * MINV_ALL - display all inventory
5887 struct obj *
5888 display_minventory(
5889 struct monst *mon, /* monster whose minvent we're showing */
5890 int dflags, /* control over what to display */
5891 char *title) /* menu title */
5893 struct obj *ret;
5894 char tmp[QBUFSZ];
5895 int n;
5896 menu_item *selected = 0;
5897 int do_all = (dflags & MINV_ALL) != 0,
5898 incl_hero = (do_all && engulfing_u(mon)),
5899 have_inv = (mon->minvent != 0),
5900 have_any = (have_inv || incl_hero),
5901 pickings = (dflags & MINV_PICKMASK);
5903 Sprintf(tmp, "%s %s:", s_suffix(noit_Monnam(mon)),
5904 do_all ? "possessions" : "armament");
5906 if (do_all ? have_any : (mon->misc_worn_check || MON_WEP(mon))) {
5907 /* Fool the 'weapon in hand' routine into
5908 * displaying 'weapon in claw', etc. properly.
5910 gy.youmonst.data = mon->data;
5911 /* in case inside a shop, don't append "for sale" prices */
5912 iflags.suppress_price++;
5914 n = query_objlist(title ? title : tmp, &(mon->minvent),
5915 (INVORDER_SORT | (incl_hero ? INCLUDE_HERO : 0)),
5916 &selected, pickings,
5917 do_all ? allow_all : worn_wield_only);
5919 iflags.suppress_price--;
5920 /* was 'set_uasmon();' but that potentially has side-effects */
5921 gy.youmonst.data = &mons[u.umonnum]; /* basic part of set_uasmon() */
5922 } else {
5923 invdisp_nothing(title ? title : tmp, "(none)");
5924 n = 0;
5927 if (n > 0) {
5928 ret = selected[0].item.a_obj;
5929 free((genericptr_t) selected);
5930 } else
5931 ret = (struct obj *) 0;
5932 return ret;
5935 /* format a container name for cinventory_display(), inserting "trapped"
5936 if that's appropriate */
5937 staticfn char *
5938 cinv_doname(struct obj *obj)
5940 char *result = doname(obj);
5943 * If obj->tknown ever gets implemented, doname() will handle this.
5944 * Assumes that probing reveals the trap prior to calling us. Since
5945 * we lack that flag, hero forgets about it as soon as we're done....
5948 /* 'result' is an obuf[] but might point into the middle (&buf[PREFIX])
5949 rather than the beginning and we don't have access to that;
5950 assume that there is at least QBUFSZ available when reusing it */
5951 if (obj->otrapped && strlen(result) + sizeof "trapped " <= QBUFSZ) {
5952 /* obj->lknown has been set before calling us so either "locked" or
5953 "unlocked" should always be present (for a trapped container) */
5954 char *p = strstri(result, " locked"),
5955 *q = strstri(result, " unlocked");
5957 if (p && (!q || p < q))
5958 (void) strsubst(p, " locked ", " trapped locked ");
5959 else if (q)
5960 (void) strsubst(q, " unlocked ", " trapped unlocked ");
5961 /* might need to change "an" to "a"; when no BUC is present,
5962 "an unlocked" yielded "an trapped unlocked" above */
5963 (void) strsubst(result, "an trapped ", "a trapped ");
5965 return result;
5968 /* used by safe_qbuf() if the full doname() result is too long */
5969 staticfn char *
5970 cinv_ansimpleoname(struct obj *obj)
5972 char *result = ansimpleoname(obj);
5974 /* result is an obuf[] so we know this will always fit */
5975 if (obj->otrapped) {
5976 if (strncmp(result, "a ", 2))
5977 (void) strsubst(result, "a ", "a trapped ");
5978 else if (strncmp(result, "an ", 3))
5979 (void) strsubst(result, "an ", "an trapped ");
5980 /* unique container? nethack doesn't have any */
5981 else if (strncmp(result, "the ", 4))
5982 (void) strsubst(result, "the ", "the trapped ");
5983 /* no leading article at all? shouldn't happen with ansimpleoname() */
5984 else
5985 (void) strsubst(result, "", "trapped "); /* insert at beginning */
5987 return result;
5990 /* Display the contents of a container in inventory style.
5991 Used for wand of probing of non-empty containers and statues. */
5992 struct obj *
5993 display_cinventory(struct obj *obj)
5995 struct obj *ret;
5996 char qbuf[QBUFSZ];
5997 int n;
5998 menu_item *selected = 0;
6000 (void) safe_qbuf(qbuf, "Contents of ", ":", obj,
6001 /* custom formatting routines to insert "trapped"
6002 into the object's name when appropriate;
6003 last resort "that" won't ever get used */
6004 cinv_doname, cinv_ansimpleoname, "that");
6006 if (obj->cobj) {
6007 n = query_objlist(qbuf, &(obj->cobj), INVORDER_SORT,
6008 &selected, PICK_NONE, allow_all);
6009 } else {
6010 invdisp_nothing(qbuf, "(empty)");
6011 n = 0;
6013 if (n > 0) {
6014 ret = selected[0].item.a_obj;
6015 free((genericptr_t) selected);
6016 } else
6017 ret = (struct obj *) 0;
6018 obj->cknown = 1;
6019 return ret;
6023 staticfn boolean
6024 only_here(struct obj *obj)
6026 return (obj->ox == go.only.x && obj->oy == go.only.y);
6030 * Display a list of buried or underwater items in inventory style.
6031 * Return a non-zero value if there were items at that spot.
6033 * Currently, this is only used with a wand of probing zapped downwards.
6036 display_binventory(coordxy x, coordxy y, boolean as_if_seen)
6038 struct obj *obj;
6039 char qbuf[QBUFSZ];
6040 const char *underwhat = "here";
6041 menu_item *selected = 0;
6042 int n, n2 = 0;
6044 /* if hero is levitating or flying over water or lava, list any items
6045 below (the map won't be showing them); if hero is underwater, player
6046 should use the normal look_here command instead of probing (caller
6047 has already used bhitpile() which will have set dknown on all items) */
6048 if (is_pool_or_lava(x, y) && !Underwater
6049 && (obj = svl.level.objects[x][y]) != 0) {
6050 const char *real_liquid = is_pool(x, y) ? "water" : "lava",
6051 *seen_liquid = hliquid(real_liquid);
6053 if (!obj->nexthere) {
6054 boolean more_than_1 = is_plural(obj);
6056 There("%s %s under the %s here.", more_than_1 ? "are" : "is",
6057 doname(obj), seen_liquid);
6058 n2 = 1;
6059 /* "pair of boots" is singular but "beneath it" sounds strange */
6060 if (pair_of(obj))
6061 more_than_1 = TRUE;
6062 underwhat = more_than_1 ? "under them" : "beneath it";
6063 } else {
6064 Sprintf(qbuf, "Things that are under the %s here:", seen_liquid);
6065 if (query_objlist(qbuf, &svl.level.objects[x][y], BY_NEXTHERE,
6066 &selected, PICK_NONE, allow_all) > 0)
6067 free((genericptr_t) selected), selected = 0;
6068 for (n2 = 0; obj; obj = obj->nexthere)
6069 ++n2;
6070 underwhat = "beneath them";
6074 /* count # of buried objects here */
6075 for (n = 0, obj = svl.level.buriedobjlist; obj; obj = obj->nobj)
6076 if (obj->ox == x && obj->oy == y) {
6077 if (as_if_seen)
6078 obj->dknown = 1;
6079 n++;
6082 if (n) {
6083 go.only.x = x;
6084 go.only.y = y;
6085 /* "buried here", but vary if we've already shown underwater items */
6086 Sprintf(qbuf, "Things that are buried %s:", underwhat);
6087 if (query_objlist(qbuf, &svl.level.buriedobjlist, INVORDER_SORT,
6088 &selected, PICK_NONE, only_here) > 0)
6089 free((genericptr_t) selected);
6090 go.only.x = go.only.y = 0;
6092 return n + n2;
6095 void
6096 prepare_perminvent(winid window)
6098 win_request_info *wri;
6099 int invmode = (int) iflags.perminv_mode;
6101 if (perminv_flags != invmode) {
6102 wri_info = zerowri;
6103 wri_info.fromcore.invmode = invmode;
6104 /* relay the mode settings to the window port */
6105 wri = ctrl_nhwindow(window, set_mode, &wri_info);
6106 perminv_flags = invmode;
6107 nhUse(wri);
6111 void
6112 sync_perminvent(void)
6114 static win_request_info *wri = 0;
6115 const char *wport_id;
6117 if (WIN_INVEN == WIN_ERR) {
6118 if ((gc.core_invent_state
6119 || (wri_info.tocore.tocore_flags & prohibited))
6120 && !(in_perm_invent_toggled
6121 && gp.perm_invent_toggling_direction == toggling_on))
6122 return;
6124 prepare_perminvent(WIN_INVEN);
6126 if ((!iflags.perm_invent && gc.core_invent_state)) {
6127 /* Odd - but this could be end-of-game disclosure
6128 * which just sets boolean iflags.perm_invent to
6129 * FALSE without actually doing anything else.
6131 #ifdef TTY_PERM_INVENT
6132 if (WINDOWPORT(tty))
6133 perm_invent_toggled(TRUE); /* TRUE means negated */
6134 #endif
6135 docrt();
6136 return;
6140 * The following conditions can bring us to here:
6141 * 1. iflags.perm_invent is on
6142 * AND
6143 * gc.core_invent_state is still zero.
6144 * OR
6145 * 2. iflags.perm_invent is off, but we're in the
6146 * midst of toggling it on.
6147 * OR
6148 * 3. iflags.perminv_mode has been changed via 'm O'.
6151 if ((iflags.perm_invent && !gc.core_invent_state)
6152 || (!iflags.perm_invent
6153 && (in_perm_invent_toggled
6154 && gp.perm_invent_toggling_direction == toggling_on))) {
6155 /* Send windowport a request to return the related settings to us */
6156 if ((iflags.perm_invent && !gc.core_invent_state)
6157 || in_perm_invent_toggled) {
6158 wri = ctrl_nhwindow(WIN_INVEN, request_settings, &wri_info);
6159 if (wri != 0) {
6160 if ((wri->tocore.tocore_flags & (too_early)) != 0) {
6161 /* don't be too noisy about this as it's really
6162 * a startup timing issue. Just set a marker. */
6163 iflags.perm_invent_pending = TRUE;
6164 return;
6166 if ((wri->tocore.tocore_flags & (too_small | prohibited))
6167 != 0) {
6168 /* sizes aren't good enough */
6169 if ((wri->tocore.tocore_flags & prohibited) != 0) {
6170 set_option_mod_status("perm_invent", set_gameview);
6171 set_option_mod_status("perminv_mode", set_gameview);
6173 iflags.perm_invent = FALSE;
6174 if (WIN_INVEN != WIN_ERR)
6175 destroy_nhwindow(WIN_INVEN), WIN_INVEN = WIN_ERR;
6176 wport_id = WINDOWPORT(tty) ? "tty perm_invent"
6177 : "perm_invent";
6178 pline("%s could not be enabled.", wport_id);
6179 pline("%s needs a terminal that is at least %dx%d, yours "
6180 "is %dx%d.",
6181 wport_id, wri->tocore.needrows,
6182 wri->tocore.needcols, wri->tocore.haverows,
6183 wri->tocore.havecols);
6184 wait_synch();
6185 return;
6188 gc.core_invent_state++;
6192 if (!wri || wri->tocore.maxslot == 0)
6193 return;
6195 if (in_perm_invent_toggled
6196 && gp.perm_invent_toggling_direction == toggling_on) {
6197 WIN_INVEN = create_nhwindow(NHW_MENU);
6200 if (WIN_INVEN != WIN_ERR && program_state.beyond_savefile_load) {
6201 gi.in_sync_perminvent = 1;
6202 (void) display_inventory((char *) 0, FALSE);
6203 gi.in_sync_perminvent = 0;
6207 void
6208 perm_invent_toggled(boolean negated)
6210 in_perm_invent_toggled = TRUE;
6211 if (negated) {
6212 gp.perm_invent_toggling_direction = toggling_off;
6213 if (WIN_INVEN != WIN_ERR)
6214 destroy_nhwindow(WIN_INVEN), WIN_INVEN = WIN_ERR;
6215 gc.core_invent_state = 0;
6216 } else {
6217 gp.perm_invent_toggling_direction = toggling_on;
6218 if (iflags.perminv_mode == InvOptNone)
6219 iflags.perminv_mode = InvOptOn; /* all inventory except gold */
6220 sync_perminvent();
6222 gp.perm_invent_toggling_direction = toggling_not;
6223 in_perm_invent_toggled = FALSE;
6226 /*invent.c*/