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. */
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
*,
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 */
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) \
87 /* 'rating' advances for each USE_RATING() call */ \
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" */
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
);
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
);
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
);
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 */
143 altclass
= -1; /* 'orderclass' must end up non-zero */
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;
156 /* sortloot() classification; called at most once [per sort] for each object;
157 also called by '\' command if discoveries use sortloot order */
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
;
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.
181 obj
->dknown
= 1; /* xname(obj) does this; we want it sooner */
182 seen
= obj
->dknown
? TRUE
: FALSE
,
184 classorder
= flags
.sortpack
? flags
.inv_order
: def_srt_order
;
185 p
= strchr(classorder
, oclass
);
187 k
= 1 + (int) (p
- classorder
);
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 */
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 */
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);
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 */
241 case DRUM_OF_EARTHQUAKE
:
242 case HORN_OF_PLENTY
: /* not a musical instrument */
243 k
= 3; /* instrument or unknown horn of plenty */
246 k
= 4; /* 'other' tool */
251 /* [what about separating "partly eaten" within each group?] */
257 /* [maybe separate one-bite foods from rations and such?] */
258 k
= obj
->globby
? 6 : 2;
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).
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
) {
290 k
= !seen
? 1 : !discovered
? 2 : 3;
293 k
= !seen
? 1 : !discovered
? 2 : 4;
295 default: /* MINERAL */
296 k
= !seen
? 5 : (obj
->otyp
!= ROCK
) ? (!discovered
? 6 : 7) : 8;
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 */
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
;
314 sort_item
->inuse
= 0;
317 /* sortloot() formatting routine; for alphabetizing, not shown to user */
319 loot_xname(struct obj
*obj
)
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
) {
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
)
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 */
350 obj
->owt
= 20; /* weight of a fresh glob (one pudding's worth) */
351 /* suppress user-assigned name */
352 if (save_oname
&& !obj
->oartifact
)
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;
362 res
= cxname_singular(obj
);
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");
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"
393 if (save_oname
&& !obj
->oartifact
)
394 ONAME(obj
) = save_oname
;
399 /* '$'==1, 'a'-'z'==2..27, 'A'-'Z'==28..53, '#'==54, catchall 55 */
401 invletter_value(char c
)
403 return ('a' <= c
&& c
<= 'z') ? (c
- 'a' + 2)
404 : ('A' <= c
&& c
<= 'Z') ? (c
- 'A' + 2 + 26)
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
,
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
);
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) */
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
);
451 val1
= sli1
->orderclass
;
452 val2
= sli2
->orderclass
;
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
;
464 /* Class and subclass match; sort by discovery status:
465 * first unseen, then seen but not named or discovered,
466 * then named, lastly discovered.
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.
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
);
489 if ((gs
.sortlootmode
& SORTLOOT_LOOT
) == 0)
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.
500 tmpstr
= loot_xname(obj1
);
501 nam1
= sli1
->str
= dupstr(tmpstr
);
502 maybereleaseobuf(tmpstr
);
506 tmpstr
= loot_xname(obj2
);
507 nam2
= sli2
->str
= dupstr(tmpstr
);
508 maybereleaseobuf(tmpstr
);
510 if ((namcmp
= strcmpi(nam1
, nam2
)) != 0)
514 val1
= obj1
->bknown
? (obj1
->blessed
? 3 : !obj1
->cursed
? 2 : 1) : 0;
515 val2
= obj2
->bknown
? (obj2
->blessed
? 3 : !obj2
->cursed
? 2 : 1) : 0;
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
;
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
);
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
;
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;
549 return val2
- val1
; /* bigger is better */
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
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.
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 */
613 boolean augment_filter
;
615 for (n
= 0, o
= *olist
; o
; o
= by_nexthere
? o
->nexthere
: o
->nobj
)
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
])))
632 sliarray
[i
] = zerosli
;
633 sliarray
[i
].obj
= o
, sliarray
[i
].indx
= (int) 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 */
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
)
650 free((genericptr_t
) sliarray
[i
].str
), sliarray
[i
].str
= 0;
655 /* sortloot() callers should use this to free up memory it allocates */
657 unsortloot(Loot
**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 */
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
;
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;
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
) {
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
);
704 assigninvlet(struct obj
*otmp
)
706 boolean inuse
[invlet_basic
];
710 /* there should be at most one of these in inventory... */
711 if (otmp
->oclass
== COIN_CLASS
) {
712 otmp
->invlet
= GOLD_SYM
;
716 for (i
= 0; i
< invlet_basic
; i
++)
718 for (obj
= gi
.invent
; obj
; obj
= obj
->nobj
)
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
)
728 if ((i
= otmp
->invlet
)
729 && (('a' <= i
&& i
<= 'z') || ('A' <= i
&& i
<= 'Z')))
731 for (i
= gl
.lastinvnr
+ 1; i
!= gl
.lastinvnr
; i
++) {
732 if (i
== invlet_basic
) {
740 (inuse
[i
] ? NOINVSYM
: (i
< 26) ? ('a' + i
) : ('A' + i
- 26));
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() */
751 struct obj
*otmp
, *prev
, *next
;
752 boolean need_more_sorting
;
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
; ) {
762 if (next
&& inv_rank(next
) < inv_rank(otmp
)) {
763 need_more_sorting
= TRUE
;
768 otmp
->nobj
= next
->nobj
;
776 } while (need_more_sorting
);
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 */
785 merge_choice(struct obj
*objlist
, struct obj
*obj
)
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) {
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;
813 /*assert(objlist != NULL);*/
814 if (mergable(objlist
, obj
))
816 objlist
= objlist
->nobj
;
818 obj
->no_charge
= save_nocharge
;
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
);
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 */
862 obj_merge_light_sources(obj
, otmp
);
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
) {
876 if (obj
->rknown
!= otmp
->rknown
) {
878 if (otmp
->oerodeproof
)
881 if (obj
->bknown
!= otmp
->bknown
) {
883 if (!Role_if(PM_CLERIC
))
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) {
901 } else if ((wmask
& W_SWAPWEP
) != 0L) {
903 } else if ((wmask
& W_QUIVER
) != 0L) {
906 impossible("merging strangely worn items (%lx)", wmask
);
907 wmask
= otmp
->owornmask
;
909 if ((otmp
->owornmask
& ~wmask
) != 0L)
911 setworn(otmp
, wmask
);
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
;
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') */
936 /* handle puddings a bit differently; absorption will free the
937 other object automatically so we can just return out from here */
939 pudding_merge_message(otmp
, obj
);
940 obj_absorb(potmp
, pobj
);
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 */
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
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
969 * It may be valid to merge this code with addinv_core2().
972 addinv_core1(struct obj
*obj
)
974 if (obj
->oclass
== COIN_CLASS
) {
976 } else if (obj
->otyp
== AMULET_OF_YENDOR
) {
978 impossible("already have amulet?");
980 record_achievement(ACH_AMUL
);
981 } else if (obj
->otyp
== CANDELABRUM_OF_INVOCATION
) {
983 impossible("already have candelabrum?");
985 record_achievement(ACH_CNDL
);
986 } else if (obj
->otyp
== BELL_OF_OPENING
) {
988 impossible("already have silver bell?");
990 record_achievement(ACH_BELL
);
991 } else if (obj
->otyp
== SPE_BOOK_OF_THE_DEAD
) {
993 impossible("already have the book?");
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;
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
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
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 */
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 */
1071 for (otmp
= gi
.invent
; otmp
; otmp
= otmp
->nobj
) {
1072 if (otmp
->nobj
== other_obj
) {
1073 obj
->nobj
= other_obj
;
1075 obj
->where
= OBJ_INVENT
;
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
)) {
1087 panic("addinv: null obj after quiver merge otyp=%d", saved_otyp
);
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
)) {
1095 panic("addinv: null obj after merge otyp=%d", saved_otyp
);
1098 /* didn't merge, so insert into chain */
1100 if (flags
.invlet_constant
|| !prev
) {
1101 obj
->nobj
= gi
.invent
; /* insert at beginning */
1103 if (flags
.invlet_constant
)
1106 prev
->nobj
= obj
; /* insert at end */
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
)))
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
)
1133 /* add obj to the hero's inventory in the default fashion */
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 */
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' */
1151 addinv_nomerge(struct obj
*obj
)
1154 unsigned save_nomerge
= obj
->nomerge
;
1157 result
= addinv(obj
);
1158 obj
->nomerge
= save_nomerge
;
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.
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.
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 */
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 :-) */
1213 } else if (wasUpolyd
&& !Upolyd
) {
1214 /* lose your grip if you revert your form */
1216 pline(drop_fmt
, drop_arg
);
1217 obj_extract_self(obj
);
1221 obj_extract_self(obj
);
1223 obj
->otyp
= CRYSKNIFE
;
1224 obj
->oerodeproof
= oerode
;
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
);
1233 } else if (obj
->otyp
== CORPSE
1234 && !u_safe_from_fatal_corpse(obj
, st_all
)
1235 && obj
->wishedfor
) {
1237 obj
= addinv_core0(obj
, (struct obj
*) 0, FALSE
);
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] */
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
);
1264 if (flags
.autoquiver
&& !uquiver
&& !obj
->owornmask
1265 && (is_missile(obj
) || ammo_and_launcher(obj
, uwep
)
1266 || ammo_and_launcher(obj
, uswapwep
)))
1268 if (hold_msg
|| drop_fmt
)
1269 prinv(hold_msg
, obj
, oquan
);
1270 /* obj made it into inventory and is staying there */
1272 (void) encumber_msg();
1279 pline(drop_fmt
, drop_arg
);
1281 if (can_reach_floor(TRUE
) || u
.uswallow
) {
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 */
1294 useupall(struct obj
*obj
)
1298 obfree(obj
, (struct obj
*) 0); /* deletes contents also */
1301 /* an item in inventory is going away after being used */
1303 useup(struct obj
*obj
)
1305 /* Note: This works correctly for containers because they (containers)
1307 if (obj
->quan
> 1L) {
1308 obj
->in_use
= FALSE
; /* no longer in use */
1310 obj
->owt
= weight(obj
);
1317 /* use one charge from an item and possibly incur shop debt for it */
1321 boolean maybe_unpaid
) /* false if caller handles shop billing */
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...
1338 freeinv_core(struct obj
*obj
)
1340 if (obj
->oclass
== COIN_CLASS
) {
1343 } else if (obj
->otyp
== AMULET_OF_YENDOR
) {
1344 if (!u
.uhave
.amulet
)
1345 impossible("don't have amulet?");
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
) {
1353 impossible("don't have silver bell?");
1355 } else if (obj
->otyp
== SPE_BOOK_OF_THE_DEAD
) {
1357 impossible("don't have the book?");
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
) {
1370 } else if (confers_luck(obj
)) {
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 */
1385 freeinv(struct obj
*obj
)
1387 extract_nobj(obj
, &gi
.invent
);
1388 obj
->pickup_prev
= 0;
1393 /* drawbridge is destroying all objects at <x,y> */
1395 delallobj(coordxy x
, coordxy y
)
1397 struct obj
*otmp
, *otmp2
;
1399 for (otmp
= svl
.level
.objects
[x
][y
]; otmp
; otmp
= otmp2
) {
1402 /* after unpunish(), or might get deallocated chain */
1403 otmp2
= otmp
->nexthere
;
1410 /* normal object deletion (if unpaid, it remains on the bill) */
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 */
1422 boolean force
) /* 'force==TRUE' used when reviving Rider corpses */
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 */
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 */
1448 sobj_at(int otyp
, coordxy x
, coordxy y
)
1452 for (otmp
= svl
.level
.objects
[x
][y
]; otmp
; otmp
= otmp
->nexthere
)
1453 if (otmp
->otyp
== otyp
)
1459 /* sobj_at(&c) traversal -- find next object of specified type */
1461 nxtobj(struct obj
*obj
, int type
, boolean by_nexthere
)
1465 otmp
= obj
; /* start with the object after this one */
1467 otmp
= !by_nexthere
? otmp
->nobj
: otmp
->nexthere
;
1470 } while (otmp
->otyp
!= type
);
1475 /* return inventory object of type 'type' if hero has one, otherwise Null */
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
)
1488 /* return inventory object of type that will petrify on touch */
1490 carrying_stoning_corpse(void)
1494 for (otmp
= gi
.invent
; otmp
; otmp
= otmp
->nobj
)
1495 if (otmp
->otyp
== CORPSE
&& touch_petrifies(&mons
[otmp
->corpsenm
]))
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 */
1528 currency(long amount
)
1532 res
= Hallucination
? ROLL_FROM(currencies
) : "zorkmid";
1534 res
= makeplural(res
);
1539 u_carried_gloves(void)
1541 struct obj
*otmp
, *gloves
= (struct obj
*) 0;
1546 for (otmp
= gi
.invent
; otmp
; otmp
= otmp
->nobj
)
1547 if (is_gloves(otmp
)) {
1562 for (otmp
= gi
.invent
; otmp
; otmp
= otmp
->nobj
)
1563 if (otmp
->otyp
== SPE_NOVEL
)
1565 return (struct obj
*) 0;
1569 o_on(unsigned int id
, struct obj
*objchn
)
1574 if (objchn
->o_id
== id
)
1576 if (Has_contents(objchn
) && (temp
= o_on(id
, objchn
->cobj
)))
1578 objchn
= objchn
->nobj
;
1580 return (struct obj
*) 0;
1584 obj_here(struct obj
*obj
, coordxy x
, coordxy y
)
1588 for (otmp
= svl
.level
.objects
[x
][y
]; otmp
; otmp
= otmp
->nexthere
)
1595 g_at(coordxy x
, coordxy y
)
1597 struct obj
*obj
= svl
.level
.objects
[x
][y
];
1600 if (obj
->oclass
== COIN_CLASS
)
1602 obj
= obj
->nexthere
;
1604 return (struct obj
*) 0;
1607 /* compact a string of inventory letters by dashing runs of letters */
1609 compactify(char *buf
)
1612 char ilet
, ilet1
, ilet2
;
1616 buf
[++i2
] = buf
[++i1
];
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
];
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
)
1633 else if (i2
>= 3 && buf
[i2
- 3] == NOINVSYM
&& buf
[i2
- 2] == '-'
1634 && buf
[i2
- 1] == NOINVSYM
)
1639 buf
[++i2
] = buf
[++i1
];
1644 /* some objects shouldn't be split when count given to getobj or askchain */
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 */
1654 taking_off(const char *action
)
1656 return !strcmp(action
, "take off") || !strcmp(action
, "remove");
1660 mime_action(const char *word
)
1663 char *bp
, *pfx
, *sfx
;
1666 bp
= pfx
= sfx
= (char *) 0;
1668 if ((bp
= strstr(buf
, " on the ")) != 0) {
1669 /* rub on the stone[s] */
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" */
1678 pfx
= &buf
[3 + 1]; /* "<pfx> something" */
1680 if ((bp
= strstr(buf
, " or ")) != 0) {
1682 bp
= (rn2(2) ? buf
: (bp
+ 4));
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
)
1695 return GETOBJ_SUGGEST
;
1696 return GETOBJ_EXCLUDE
;
1699 /* return string describing your hands based on action. */
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)" : "");
1715 Sprintf(qbuf
, "your %s", makeplural(body_part(HAND
)));
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
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 */
1741 char buf
[BUFSZ
], qbuf
[QBUFSZ
];
1742 char lets
[BUFSZ
], altlets
[BUFSZ
];
1744 char *bp
= buf
, *ap
= altlets
;
1745 boolean allowcnt
= (ctrlflags
& GETOBJ_ALLOWCNT
),
1746 forceprompt
= (ctrlflags
& GETOBJ_PROMPT
),
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) */
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
;
1761 if ((cmdq
= cmdq_pop()) != 0) {
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
) {
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
)
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
)
1787 } else if (cq
.typ
== CMDQ_INT
) {
1788 /* getting a partial stack */
1789 if (!cntgiven
&& allowcnt
) {
1790 cnt
= (long) cq
.intval
;
1792 goto need_more_cq
; /* now, get CMDQ_KEY */
1794 cmdq_clear(CQ_CANNED
);
1795 /* should maybe clear the CQ_REPEAT too? */
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
)
1808 } /* !CMDQ_USER_INPUT */
1809 } else if (need_more_cq
) {
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 */
1818 *bp
++ = ' '; /* put a space after the '-' in the prompt */
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
1824 case GETOBJ_EXCLUDE_SELECTABLE
: /* ditto, I think... */
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
;
1838 if (!flags
.invlet_constant
)
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");
1854 bp
[suggested
++] = otmp
->invlet
;
1855 switch ((*obj_ok
)(otmp
)) {
1856 case GETOBJ_EXCLUDE_INACCESS
:
1857 /* remove inaccessible things */
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 ___" */
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 */
1873 *ap
++ = otmp
->invlet
;
1875 case GETOBJ_SUGGEST
:
1876 break; /* adding otmp->invlet is all that's needed */
1877 case GETOBJ_EXCLUDE_NONINVENT
: /* not applicable for invent items */
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] == ' ')
1889 Strcpy(lets
, bp
); /* necessary since we destroy buf */
1890 if (suggested
> 5) /* compactify string */
1894 if (suggested
== 0 && !forceprompt
&& !allownone
) {
1895 You("don't have anything %sto %s.", inaccess
? "else " : "", word
);
1896 return (struct obj
*) 0;
1901 Sprintf(qbuf
, "What do you want to %s?", word
);
1902 if (gi
.in_doagain
) {
1904 } else if (iflags
.force_invmenu
) {
1905 /* don't overwrite a possible quitchars */
1907 ilet
= (*lets
|| *altlets
) ? '?' : '*';
1909 putmsghistory(qbuf
, FALSE
);
1914 Strcat(qbuf
, " [*]");
1916 Sprintf(eos(qbuf
), " [%s or ?*]", buf
);
1917 ilet
= yn_function(qbuf
, (char *) 0, '\0', FALSE
);
1923 pline("No count allowed with this command.");
1926 ilet
= get_count(NULL
, ilet
, LARGEST_INT
, &tmpcnt
, GC_SAVEHIST
);
1932 if (strchr(quitchars
, ilet
)) {
1935 return (struct obj
*) 0;
1937 if (ilet
== HANDS_SYM
) { /* '-' */
1940 return (allownone
? &hands_obj
: (struct obj
*) 0);
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;
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);
1966 return (struct obj
*) 0;
1969 if (ilet
== HANDS_SYM
)
1971 if (ilet
== '\033') {
1974 return (struct obj
*) 0;
1976 if (ilet
== '*' || ilet
== '?')
1978 if (allowcnt
&& ctmp
>= 0L) {
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
)
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) {
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";
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
: "");
2027 You("%s.", only_one
);
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 */
2041 You("don't have that object.");
2043 return (struct obj
*) 0;
2045 } else if (cnt
< 0L || otmp
->quan
< cnt
) {
2046 You("don't have that many! You have only %ld.", otmp
->quan
);
2048 return (struct obj
*) 0;
2053 if (obj_ok(otmp
) == GETOBJ_EXCLUDE
) {
2054 silly_thing(word
, otmp
);
2055 return (struct obj
*) 0;
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
;
2073 DISABLE_WARNING_FORMAT_NONLITERAL
2076 silly_thing(const char *word
,
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
;
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
= "";
2101 pline("Use the '%s' command to %s %s%s.", s1
, s2
,
2102 !(is_plural(otmp
) || pair_of(otmp
)) ? "that" : "those", s3
);
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.");
2112 pline(silly_thing_to
, word
);
2115 RESTORE_WARNING_FORMAT_NONLITERAL
2118 ckvalidcat(struct obj
*otmp
)
2120 /* use allow_category() from pickup.c */
2121 return (int) allow_category(otmp
);
2125 ckunpaid(struct obj
*otmp
)
2127 return (otmp
->unpaid
|| (Has_contents(otmp
) && count_unpaid(otmp
->cobj
)));
2133 return (boolean
) (uarm
|| uarmc
|| uarmf
|| uarmg
2134 || uarmh
|| uarms
|| uarmu
);
2138 is_worn(struct obj
*otmp
)
2140 return (otmp
->owornmask
& (W_ARMOR
| W_ACCESSORY
| W_SADDLE
| W_WEAPONS
))
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 */
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
{
2160 /* safe_qbuf() -> short_oname() callback */
2162 safeq_xprname(struct obj
*obj
)
2164 return xprname(obj
, (char *) 0, safeq_xprn_ctx
.let
, safeq_xprn_ctx
.dot
,
2168 /* alternate safe_qbuf() -> short_oname() callback */
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
,
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
;
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
];
2198 You("have nothing to %s.", word
);
2200 *resultflags
= ALL_FINISHED
;
2205 takeoff
= ident
= allflag
= m_seen
= FALSE
;
2206 add_valid_menu_class(0); /* reset */
2207 if (taking_off(word
)) {
2210 } else if (!strcmp(word
, "identify")) {
2212 ofilter
= not_fully_identified
;
2215 iletct
= collect_obj_classes(ilets
, gi
.invent
, FALSE
, ofilter
,
2217 unpaid
= count_unpaid(gi
.invent
);
2219 if (ident
&& !iletct
) {
2220 return -1; /* no further identifications */
2221 } else if (gi
.invent
) {
2222 ilets
[iletct
++] = ' ';
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';
2239 ilets
[iletct
++] = 'm'; /* allow menu presentation on request */
2240 ilets
[iletct
] = '\0';
2243 Sprintf(qbuf
, "What kinds of thing do you want to %s? [%s]",
2246 if (buf
[0] == '\033')
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 */
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')
2267 extra_removeables
[0] = '\0';
2269 /* arbitrary types of items can be placed in the weapon slots
2270 [any duplicate entries in extra_removeables[] won't matter] */
2272 (void) strkitten(extra_removeables
, uwep
->oclass
);
2274 (void) strkitten(extra_removeables
, uswapwep
->oclass
);
2276 (void) strkitten(extra_removeables
, uquiver
->oclass
);
2280 olets
[oletct
= 0] = '\0';
2281 while ((sym
= *ip
++) != '\0') {
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.");
2291 } else if (oc_of_sym
== ARMOR_CLASS
&& !wearing_armor()) {
2294 } else if (oc_of_sym
== WEAPON_CLASS
&& !uwep
&& !uswapwep
2296 You("are not wielding anything.");
2298 } else if (oc_of_sym
== RING_CLASS
&& !uright
&& !uleft
) {
2299 You("are not wearing rings.");
2301 } else if (oc_of_sym
== AMULET_CLASS
&& !uamul
) {
2302 You("are not wearing an amulet.");
2304 } else if (oc_of_sym
== TOOL_CLASS
&& !ublindf
) {
2305 You("are not wearing a blindfold.");
2312 } else if (sym
== 'A') {
2313 ; /* same as the default */
2314 } else if (sym
== 'u') {
2315 add_valid_menu_class('u');
2317 } else if (strchr("BUCXP", sym
)) {
2318 add_valid_menu_class(sym
); /* 'B','U','C','X', or 'P' */
2320 } else if (sym
== 'm') {
2322 } else if (oc_of_sym
== MAXOCLASSES
) {
2323 You("don't have any %c's.", sym
);
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';
2335 || (!oletct
&& ckfn
!= ckunpaid
&& ckfn
!= ckvalidcat
))
2337 } else if (flags
.menu_style
!= MENU_TRADITIONAL
&& combo
&& !allflag
) {
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
;
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.
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
;
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);
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
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
2407 * for (otmp = *objchn; otmp; otmp = otmp2) {
2408 * otmp2 = otmp->nobj;
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) {
2418 else if (ilet
== 'Z')
2419 ilet
= NOINVSYM
; /* '#' */
2422 if (olets
&& *olets
&& otmp
->oclass
!= *olets
)
2424 if (takeoff
&& !is_worn(otmp
))
2426 if (ident
&& !not_fully_identified(otmp
))
2428 if (ckfn
&& !(*ckfn
)(otmp
))
2430 if (bycat
&& !ckvalidcat(otmp
))
2433 safeq_xprn_ctx
.let
= ilet
;
2434 safeq_xprn_ctx
.dot
= !nodot
;
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
);
2444 (void) safe_qbuf(qbuf
, qpfx
, "?", otmp
,
2445 ininv
? safeq_xprname
: doname
,
2446 ininv
? safeq_shortxprname
: ansimpleoname
,
2448 sym
= (takeoff
|| ident
|| otmp
->quan
< 2L) ? nyaq(qbuf
)
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). */
2464 if (yn_number
< otmp
->quan
&& splittable(otmp
))
2465 otmp
= splitobj(otmp
, yn_number
);
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
);
2500 /* special case for seffects() */
2506 if (olets
&& *olets
&& *++olets
)
2509 if (!takeoff
&& (dud
|| cnt
))
2510 pline("That was all.");
2511 else if (!dud
&& !cnt
)
2512 pline("No applicable objects.");
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);*/
2523 * Object identification routines:
2526 /* set the cknown and lknown flags on an object if they're applicable */
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
)
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 */
2539 /* make an object actually be identified; no display updating */
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);
2561 /* menu of unidentified objects; select and identify up to id_limit of them */
2563 menu_identify(int id_limit
)
2565 menu_item
*pick_list
;
2566 int n
, i
, first
= 1, tryct
= 5;
2568 /* assumptions: id_limit > 0 and at least one unID'd item is present */
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
);
2580 for (i
= 0; i
< n
; i
++, id_limit
--)
2581 (void) identify(pick_list
[i
].item
.a_obj
);
2582 free((genericptr_t
) pick_list
);
2584 wait_synch(); /* Before we loop to pop open another menu */
2586 } else if (n
== -2) { /* player used ESC to quit menu */
2588 } else if (n
== -1) { /* no eligible items found */
2589 pline("That was all.");
2591 } else if (!--tryct
) { /* stop re-prompting */
2592 pline1(thats_enough_tries
);
2594 } else { /* try again */
2595 pline("Choose an item; use ESC to decline.");
2599 /* count the unidentified items */
2601 count_unidentified(struct obj
*objchn
)
2606 for (obj
= objchn
; obj
; obj
= obj
->nobj
)
2607 if (not_fully_identified(obj
))
2612 /* dialog with user to identify a given number of items; 0 means all */
2616 boolean learning_id
) /* T: just read unknown identify scroll */
2619 int n
, unid_cnt
= count_unidentified(gi
.invent
);
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
);
2635 /* identify up to `id_limit' items */
2637 if (flags
.menu_style
== MENU_TRADITIONAL
)
2639 n
= ggetobj("identify", identify
, id_limit
, FALSE
,
2642 break; /* quit or no eligible items */
2643 } while ((id_limit
-= n
) > 0);
2644 if (n
== 0 || n
< -1)
2645 menu_identify(id_limit
);
2650 /* called when regaining sight; mark inventory objects which were picked
2651 up while blind as now having been seen */
2653 learn_unseen_invent(void)
2656 boolean invupdated
= FALSE
;
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 */
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.
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 */
2682 update_inventory(void)
2684 int save_suppress_price
;
2686 if (!program_state
.in_moveloop
) /* not covered by suppress_map_output */
2688 if (suppress_map_output()) /* despite name, used for perm_invent too */
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 */
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).
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 */
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'.",
2739 } else if (!iflags
.perm_invent
) {
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.");
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 */
2759 /* should of course only be called for things in invent */
2761 obj_to_let(struct obj
*obj
)
2763 if (!flags
.invlet_constant
) {
2764 obj
->invlet
= NOINVSYM
;
2771 * Print the indicated quantity of the given object. If quan == 0L then use
2772 * the current quantity.
2775 prinv(const char *prefix
, struct obj
*obj
, long quan
)
2777 boolean total_of
= (quan
&& (quan
< obj
->quan
));
2778 char totalbuf
[QBUFSZ
];
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
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 */
2807 boolean use_invlet
= (flags
.invlet_constant
&& obj
!= NULL
2808 && let
!= CONTAINED_SYM
&& let
!= HANDS_SYM
);
2812 savequan
= obj
->quan
;
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";
2823 assert(obj
!= NULL
);
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
)
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";
2840 /* ordinary inventory display or pickup message */
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
);
2851 obj
->quan
= savequan
;
2856 RESTORE_WARNING_FORMAT_NONLITERAL
2858 enum item_action_actions
{
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 */
2886 IA_WHATIS_OBJ
, /* '/' specify inventory object */
2889 /* construct text for the menu entries for IA_NAME_OBJ and IA_NAME_OTYP */
2891 item_naming_classification(
2898 Rename
[] = "Rename or un-name",
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",*/
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
,
2926 return (*onamebuf
|| *ocallbuf
) ? TRUE
: FALSE
;
2929 /* construct text for the menu entries for IA_READ_OBJ */
2931 item_reading_classification(struct obj
*obj
, char *outbuf
)
2933 int otyp
= obj
->otyp
, res
= IA_READ_OBJ
;
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
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");
2972 ia_addmenu(winid win
, int act
, char let
, const char *txt
)
2979 add_menu(win
, &nul_glyphinfo
, &any
, let
, 0,
2980 ATR_NONE
, clr
, txt
, MENU_ITEMFLAGS_NONE
);
2984 itemactions_pushkeys(struct obj
*otmp
, int act
)
2988 impossible("Unknown item action");
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
, '-');
3000 cmdq_add_ec(CQ_CANNED
, doapply
);
3001 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
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
);
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
);
3018 cmdq_add_ec(CQ_CANNED
, dodrop
);
3019 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
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
);
3028 case IA_ENGRAVE_OBJ
:
3029 cmdq_add_ec(CQ_CANNED
, doengrave
);
3030 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
3033 cmdq_add_ec(CQ_CANNED
, dofire
);
3036 cmdq_add_ec(CQ_CANNED
, doorganize
); /* #adjust */
3037 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
3039 case IA_ADJUST_STACK
:
3040 cmdq_add_ec(CQ_CANNED
, adjust_split
); /* #altadjust */
3041 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
3044 cmdq_add_ec(CQ_CANNED
, dosacrifice
);
3045 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
3048 cmdq_add_ec(CQ_CANNED
, dopay
);
3049 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
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
);
3059 cmdq_add_ec(CQ_CANNED
, dowieldquiver
);
3060 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
3063 cmdq_add_ec(CQ_CANNED
, doread
);
3064 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
3067 cmdq_add_ec(CQ_CANNED
, dorub
);
3068 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
3071 cmdq_add_ec(CQ_CANNED
, dothrow
);
3072 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
3074 case IA_TAKEOFF_OBJ
:
3075 cmdq_add_ec(CQ_CANNED
, dotakeoff
);
3076 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
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
);
3092 cmdq_add_ec(CQ_CANNED
, doinvoke
);
3093 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
3096 cmdq_add_ec(CQ_CANNED
, dowield
);
3097 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
3100 cmdq_add_ec(CQ_CANNED
, dowear
);
3101 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
3104 cmdq_add_ec(CQ_CANNED
, doswapweapon
);
3107 cmdq_add_ec(CQ_CANNED
, dotwoweapon
);
3110 cmdq_add_ec(CQ_CANNED
, dozap
);
3111 cmdq_add_key(CQ_CANNED
, otmp
->invlet
);
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
);
3121 /* Show menu of possible actions hero could do with item otmp */
3123 itemactions(struct obj
*otmp
)
3125 int n
, act
= IA_NONE
;
3127 char buf
[BUFSZ
], buf2
[BUFSZ
];
3128 menu_item
*selected
;
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
);
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
)) {
3243 ia_addmenu(win
, IA_NAME_OBJ
, 'c', buf
);
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
);
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");
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 */
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");
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
);
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");
3378 if (!already_worn
) {
3379 boolean shoot
= ammo_and_launcher(otmp
, uwep
);
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"
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");
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
);
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
3466 #define MAYBETWOWEAPON(obj) \
3467 ((((obj)->oclass == WEAPON_CLASS) \
3468 ? !(is_launcher(obj) || is_ammo(obj) || is_missile(obj)) \
3469 : is_weptool(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 */
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
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
)));
3501 n
= select_menu(win
, PICK_ONE
, &selected
);
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 */
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 '*') */
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() */
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
);
3561 /* the #inventory command (not much left...) */
3565 return dispinv_with_action((char *) 0, FALSE
, NULL
);
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
3577 staticfn
struct obj
*
3578 find_unpaid(struct obj
*list
, struct obj
**last_found
)
3585 /* still looking for previous unpaid object */
3586 if (list
== *last_found
)
3587 *last_found
= (struct obj
*) 0;
3589 return ((*last_found
= list
));
3591 if (Has_contents(list
)) {
3592 if ((obj
= find_unpaid(list
->cobj
, last_found
)) != 0)
3597 return (struct obj
*) 0;
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.
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 */
3632 menu_item
*selected
;
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
);
3644 lets
= 0; /* simplify tests: (lets) instead of (lets && *lets) */
3647 if (iflags
.in_dumplog
) {
3648 win
= 0; /* passed to dump_putstr() which ignores it... */
3651 if (lets
|| usextra
|| wizid
|| want_reply
3652 #ifdef TTY_PERM_INVENT
3653 /*|| !gi.in_sync_perminvent*/
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')
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
3694 if (usextra
|| (n
== 1 && (!lets
|| wizid
)))
3698 pline("%s.", not_carrying_anything
);
3702 /* oxymoron? temporarily assign permanent inventory letters */
3703 if (!flags
.invlet_constant
)
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 */
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)); /* '-' */
3719 for (otmp
= gi
.invent
; otmp
; otmp
= otmp
->nobj
)
3720 if (!lets
|| otmp
->invlet
== lets
[0])
3723 ret
= message_menu(otmp
->invlet
,
3724 want_reply
? PICK_ONE
: PICK_NONE
,
3725 xprname(otmp
, (char *) 0, lets
[0],
3729 *out_cnt
= -1L; /* select all */
3733 sortflags
= (flags
.sortloot
== 'f') ? SORTLOOT_LOOT
: SORTLOOT_INVLET
;
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
;
3744 flags
.sortpack
= FALSE
;
3745 sortflags
= SORTLOOT_INUSE
; /* override */
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
);
3781 char prompt
[QBUFSZ
];
3783 unid_cnt
= count_unidentified(gi
.invent
);
3784 Sprintf(prompt
, "Debug Identify"); /* 'title' rather than 'prompt' */
3786 Sprintf(eos(prompt
),
3787 " -- unidentified or partially identified item%s",
3789 add_menu_str(win
, prompt
);
3792 "(all items are permanently identified already)");
3793 gotsomething
= TRUE
;
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 */
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 */
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
;
3823 for (srtinv
= sortedinvent
; (otmp
= srtinv
->obj
) != 0; ++srtinv
) {
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
))
3830 if (!flags
.sortpack
|| otmp
->oclass
== *invlet
) {
3831 if (wizid
&& !not_fully_identified(otmp
))
3834 /* for inuse-only, start with an extra header */
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
;
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
);
3855 prevorderclass
= srtinv
->orderclass
;
3858 ilet
= otmp
->invlet
;
3859 any
= cg
.zeroany
; /* all bits zero */
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
);
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
) {
3894 if (--invlet
!= venom_inv
) {
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
;
3910 if ((allowxtra
&& !usextra
)
3911 || (lets
&& (int) strlen(lets
) < inv_cnt(TRUE
))) {
3913 menutext
= "(list everything)";
3916 menutext
= "(list likely candidates)";
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
);
3935 end_menu(win
, (query
&& *query
) ? query
: (char *) 0);
3937 n
= select_menu(win
,
3938 wizid
? PICK_ANY
: want_reply
? PICK_ONE
: PICK_NONE
,
3942 boolean all_id
= FALSE
;
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;
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() */
3958 if (not_fully_identified(otmp
))
3959 (void) identify(otmp
);
3960 /* identify() does not perform update_inventory() */
3966 ret
= selected
[0].item
.a_char
;
3968 *out_cnt
= selected
[0].count
;
3970 free((genericptr_t
) selected
);
3972 ret
= !n
? '\0' : '\033'; /* cancelled */
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
3985 display_inventory(const char *lets
, boolean want_reply
)
3987 struct _cmd_queue
*cmdq
= cmdq_pop();
3990 if (cmdq
->typ
== CMDQ_KEY
) {
3993 for (otmp
= gi
.invent
; otmp
; otmp
= otmp
->nobj
)
3994 if (otmp
->invlet
== cmdq
->key
3997 def_oc_syms
[(int) otmp
->oclass
].sym
))) {
3999 return otmp
->invlet
;
4003 /* cmdq not a key, or did not find the object, abort */
4005 cmdq_clear(CQ_CANNED
);
4008 return display_pickinv(lets
, (char *) 0, (char *) 0,
4009 FALSE
, want_reply
, (long *) 0);
4013 * Show what is current using inventory letters.
4017 display_used_invlets(char avoidlet
)
4021 char *invlet
= flags
.inv_order
;
4022 int n
, classcount
, invdone
= 0, tmpglyph
;
4023 glyph_info tmpglyphinfo
= nul_glyphinfo
;
4026 menu_item
*selected
;
4030 win
= create_nhwindow(NHW_MENU
);
4031 start_menu(win
, MENU_BEHAVE_STANDARD
);
4033 any
= cg
.zeroany
; /* set all bits to zero */
4035 for (otmp
= gi
.invent
; otmp
; otmp
= otmp
->nobj
) {
4036 ilet
= otmp
->invlet
;
4037 if (ilet
== avoidlet
)
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
));
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
)
4058 end_menu(win
, "Inventory letters used:");
4060 n
= select_menu(win
, PICK_ONE
, &selected
);
4062 ret
= selected
[0].item
.a_char
;
4063 free((genericptr_t
) selected
);
4065 ret
= !n
? '\0' : '\033'; /* cancelled */
4066 destroy_nhwindow(win
);
4072 * Returns the number of unpaid items within the given list. This includes
4073 * contained objects.
4076 count_unpaid(struct obj
*list
)
4083 if (Has_contents(list
))
4084 count
+= count_unpaid(list
->cobj
);
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
))
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
))
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
))
4116 /* check whether this object matches the requested type */
4118 ? (type
== BUC_UNKNOWN
)
4119 : list
->blessed
? (type
== BUC_BLESSED
)
4120 : list
->cursed
? (type
== BUC_CURSED
)
4121 : (type
== BUC_UNCURSED
))
4127 /* similar to count_buc(), but tallies all states at once
4128 rather than looking for a specific type */
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
)
4148 /* coins are either uncursed or unknown based upon option setting */
4149 if (list
->oclass
== COIN_CLASS
) {
4156 /* ordinary items */
4159 else if (list
->blessed
)
4161 else if (list
->cursed
)
4163 else /* neither blessed nor cursed => uncursed */
4168 /* count everything inside a container, or just shop-owned items inside */
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
;
4184 if (!everything
&& !newdrop
) {
4187 for (topc
= container
; topc
->where
== OBJ_CONTAINED
;
4188 topc
= topc
->ocontainer
)
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
,
4197 if (everything
|| otmp
->unpaid
|| (shoppy
&& !otmp
->no_charge
))
4198 count
+= quantity
? otmp
->quan
: 1L;
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) */
4210 struct obj
*otmp
, *marker
, *contnr
;
4212 char *invlet
= flags
.inv_order
;
4213 int classcount
, num_so_far
, xtracount
;
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
,
4230 iflags
.suppress_price
--;
4234 win
= create_nhwindow(NHW_MENU
);
4236 num_so_far
= 0; /* count of # printed so far */
4237 if (!flags
.invlet_constant
)
4242 for (otmp
= gi
.invent
; otmp
; otmp
= otmp
->nobj
) {
4243 ilet
= otmp
->invlet
;
4245 if (!flags
.sortpack
|| otmp
->oclass
== *invlet
) {
4246 if (flags
.sortpack
&& !classcount
) {
4247 putstr(win
, 0, let_to_name(*invlet
, TRUE
, FALSE
));
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
--;
4260 } while (flags
.sortpack
&& (*++invlet
));
4262 if (count
> num_so_far
) {
4263 /* something unpaid is contained */
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
4271 for (otmp
= gi
.invent
; otmp
; otmp
= otmp
->nobj
) {
4272 if (Has_contents(otmp
)) {
4275 marker
= (struct obj
*) 0; /* haven't found any */
4276 while (find_unpaid(otmp
->cobj
, &marker
)) {
4277 totcost
+= cost
= unpaid_cost(marker
, COST_NOCONTENTS
);
4280 iflags
.suppress_price
++; /* suppress "(unpaid)" sfx */
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
)));
4293 xprname((struct obj
*) 0, contbuf
, CONTAINED_SYM
,
4294 TRUE
, contcost
, 0L));
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 */
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
4320 *where
= (buriedcount
== 0) ? "on the floor"
4321 : (floorcount
== 0) ? "under the floor"
4322 : "on or under the floor";
4325 You("aren't carrying any unpaid items but there %s %d %s.",
4326 floorverb
, xtracount
, where
);
4329 Sprintf(buf
, "(There %s %d more unpaid object%s %s.)",
4330 floorverb
, xtracount
, plur(xtracount
), where
);
4331 putstr(win
, 0, buf
);
4336 display_nhwindow(win
, FALSE
);
4337 destroy_nhwindow(win
);
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'));
4355 switch (gt
.this_type
) {
4357 res
= (obj
->bknown
&& obj
->blessed
);
4360 res
= (obj
->bknown
&& !(obj
->blessed
|| obj
->cursed
));
4363 res
= (obj
->bknown
&& obj
->cursed
);
4369 break; /* use 'res' as-is */
4375 /* the #inventtype command */
4380 prompt
[] = "What type of object do you want an inventory of?";
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
;
4393 gt
.this_title
= NULL
;
4394 if (!gi
.invent
&& !billx
) {
4395 You("aren't carrying anything.");
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
;
4423 n
= query_category(prompt
, gi
.invent
, i
, &pick_list
, PICK_ONE
);
4426 gt
.this_type
= c
= pick_list
[0].item
.a_int
;
4427 free((genericptr_t
) pick_list
);
4431 /* collect list of classes of objects carried, for use as a prompt */
4433 class_count
= collect_obj_classes(types
, gi
.invent
, FALSE
,
4434 (boolean (*)(OBJ_P
)) 0,
4436 if (any_unpaid
|| billx
|| (bcnt
+ ccnt
+ ucnt
+ xcnt
) != 0 || jcnt
)
4437 types
[class_count
++] = ' ';
4439 types
[class_count
++] = 'u';
4441 types
[class_count
++] = 'x';
4443 types
[class_count
++] = 'B';
4445 types
[class_count
++] = 'U';
4447 types
[class_count
++] = 'C';
4449 types
[class_count
++] = 'X';
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';
4457 *extra_types
++ = 'u';
4459 *extra_types
++ = 'x';
4461 *extra_types
++ = 'B';
4463 *extra_types
++ = 'U';
4465 *extra_types
++ = 'C';
4467 *extra_types
++ = 'X';
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
);
4480 clear_nhwindow(WIN_MESSAGE
);
4484 /* only one thing to itemize */
4493 if (c
== 'x' || (c
== 'X' && billx
&& !xcnt
)) {
4495 (void) doinvbill(1);
4497 pline("No used-up objects%s.",
4498 any_unpaid
? " on your shopping bill" : "");
4501 if (c
== 'u' || (c
== 'U' && any_unpaid
&& !ucnt
)) {
4503 dounpaid(u_carried
, u_floor
, u_buried
);
4505 You("are not carrying any unpaid objects.");
4509 if (strchr("BUCXP", c
))
4510 oclass
= c
; /* not a class but understood by this_type_only() */
4512 oclass
= def_char_to_objclass(c
); /* change to object class */
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
) {
4521 /* these are used for traditional when not applicable and also for
4522 constructing a title to be used by query_objlist() */
4525 before
= "known to be blessed ";
4528 before
= "known to be uncursed ";
4531 before
= "known to be cursed ";
4534 after
= " whose blessed/uncursed/cursed status is unknown";
4535 break; /* better phrasing is desirable */
4537 after
= " that were just picked up";
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... */
4551 if (strchr(types
, c
) > strchr(types
, '\033')) {
4552 You("have no %sobjects%s.", before
, after
);
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 */
4580 gt
.this_title
= NULL
;
4584 /* return a string describing the dungeon feature at <x,y> if there
4585 is one worth mentioning at that location; otherwise null */
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
) {
4599 break; /* "doorway" */
4602 break; /* "open door" */
4604 dfeature
= "broken door";
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 " : "",
4629 align_str(Amask2align(lev
->altarmask
& ~AM_SHRINE
)));
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";
4645 dfeature
= defsyms
[cmap
].explanation
;
4647 Strcpy(buf
, 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 */
4655 int obj_cnt
, /* obj_cnt > 0 implies that autopickup is in progress */
4656 unsigned lookhere_flags
)
4660 const char *verb
= Blind
? "feel" : "see";
4661 const char *dfeature
= (char *) 0;
4662 char fbuf
[BUFSZ
], fbuf2
[BUFSZ
];
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
);
4673 struct monst
*mtmp
= u
.ustuck
;
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",
4695 otmp
= mtmp
->minvent
;
4697 for (; otmp
; otmp
= otmp
->nobj
) {
4698 /* If swallower is an animal, it should have become stone
4700 if (otmp
->otyp
== CORPSE
)
4701 feel_cockatrice(otmp
, FALSE
);
4704 Strcpy(fbuf
, "You feel");
4706 (void) display_minventory(mtmp
, MINV_ALL
| PICK_NONE
, fbuf
);
4708 You("%s no objects here.", verb
);
4710 return (!!Blind
? ECMD_TIME
: ECMD_OK
);
4712 if (!skip_objects
) {
4714 char regbuf
[QBUFSZ
];
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
;
4724 There("is %s%s%s here.",
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
)
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
)
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 */
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!");
4770 if (dfeature
&& !skip_dfeature
) {
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")))
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
)
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 */
4803 if (dfeature
&& !skip_dfeature
)
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");
4809 There("are %s%s objects here.",
4810 (obj_cnt
== 2) ? "two"
4811 : (obj_cnt
< 5) ? "a few"
4812 : (obj_cnt
< 10) ? "several"
4814 picked_some
? " more" : "");
4815 for (; otmp
; otmp
= otmp
->nexthere
)
4816 if (otmp
->otyp
== CORPSE
&& will_feel_cockatrice(otmp
, FALSE
)) {
4818 (obj_cnt
> 1) ? "Including"
4819 : (otmp
->quan
> 1L) ? "They're"
4821 corpse_xname(otmp
, (const char *) 0, CXN_ARTICLE
),
4822 poly_when_stoned(gy
.youmonst
.data
) ? ""
4823 : ", unfortunately");
4824 feel_cockatrice(otmp
, FALSE
);
4827 } else if (!otmp
->nexthere
) {
4828 /* only one object */
4829 if (dfeature
&& !skip_dfeature
)
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
);
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
);
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 */
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
);
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
])))
4893 feel_cockatrice(struct obj
*otmp
, boolean force_touch
)
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
)));
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 */
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 */
4916 stackobj(struct obj
*obj
)
4920 for (otmp
= svl
.level
.objects
[obj
->ox
][obj
->oy
]; otmp
;
4921 otmp
= otmp
->nexthere
)
4922 if (otmp
!= obj
&& merged(&obj
, &otmp
))
4927 /* returns TRUE if obj & otmp can be merged; used in invent.c and mkobj.c */
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
)
4941 /* coins of the same kind will always merge */
4942 if (obj
->oclass
== COIN_CLASS
)
4945 if (obj
->cursed
!= otmp
->cursed
|| obj
->blessed
!= otmp
->blessed
)
4947 if ((obj
->how_lost
& ~LOSTOVERRIDEMASK
) != 0)
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
)
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
)
4968 if (obj
->oclass
== FOOD_CLASS
4969 && (obj
->oeaten
!= otmp
->oeaten
|| obj
->orotten
!= otmp
->orotten
))
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
)
4979 if ((erosion_matters(obj
))
4980 && (obj
->oerodeproof
!= otmp
->oerodeproof
4981 || (obj
->rknown
!= otmp
->rknown
&& (Blind
|| Hallucination
))))
4984 if (obj
->otyp
== CORPSE
|| obj
->otyp
== EGG
|| obj
->otyp
== TIN
) {
4985 if (obj
->corpsenm
!= otmp
->corpsenm
)
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
])))
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)
5000 /* burning potions of oil never merge */
5001 if (obj
->otyp
== POT_OIL
&& obj
->lamplit
)
5004 /* don't merge surcharged item with base-cost item */
5005 if (obj
->unpaid
&& !same_price(obj
, otmp
))
5008 /* some additional information is always incompatible */
5009 if (has_omonst(obj
) || has_omid(obj
)
5010 || has_omonst(otmp
) || has_omid(otmp
))
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
)))
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))
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))
5038 /* should be moot since matching artifacts wouldn't be unique */
5039 if (obj
->oartifact
!= otmp
->oartifact
)
5042 if (obj
->known
!= otmp
->known
&& (Blind
|| Hallucination
))
5048 /* the #showgold command */
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
) {
5064 Strcpy(buf
, "Your wallet is empty");
5066 Sprintf(buf
, "Your wallet contains %ld %s",
5067 umoney
, currency(umoney
));
5071 ", %s you have %ld %s stashed away in your pack",
5072 umoney
? "and" : "but", hmoney
,
5073 umoney
? "more" : currency(hmoney
));
5077 long total
= umoney
+ hmoney
;
5079 You("are carrying a total of %ld %s.", total
, currency(total
));
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
);
5095 /* the #seeweapon command */
5100 You("are %s.", empty_handed());
5101 } else if (!iflags
.menu_requested
) {
5102 prinv((char *) 0, uwep
, 0L);
5104 prinv((char *) 0, uswapwep
, 0L);
5106 char lets
[4]; /* 4: uwep, uswapwep, uquiver, terminator */
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
);
5113 lets
[ct
++] = uswapwep
->invlet
;
5115 lets
[ct
++] = uquiver
->invlet
;
5118 (void) dispinv_with_action(lets
, TRUE
, NULL
);
5123 /* caller is responsible for checking !wearing_armor() */
5125 noarmor(boolean report_uskin
)
5127 if (!uskin
|| !report_uskin
) {
5128 You("are not wearing any armor.");
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))
5137 if ((p
= strstri(uskinname
, " dragon ")) != 0)
5138 while ((p
[1] = p
[8]) != '\0')
5141 You("are not wearing armor but have %s embedded in your skin.",
5146 /* the #seearmor command */
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()) {
5159 char lets
[8]; /* 8: up to 7 pieces of armor plus terminator */
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 */
5167 lets
[ct
++] = obj_to_let(uarm
);
5169 lets
[ct
++] = obj_to_let(uarmc
);
5171 lets
[ct
++] = obj_to_let(uarms
);
5173 lets
[ct
++] = obj_to_let(uarmh
);
5175 lets
[ct
++] = obj_to_let(uarmg
);
5177 lets
[ct
++] = obj_to_let(uarmf
);
5179 lets
[ct
++] = obj_to_let(uarmu
);
5182 (void) dispinv_with_action(lets
, TRUE
, NULL
);
5187 /* the #seerings command */
5191 if (!uleft
&& !uright
) {
5192 You("are not wearing any rings.");
5194 char lets
[3]; /* 3: uright, uleft, terminator */
5195 boolean use_inuse_mode
= FALSE
;
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" */
5201 lets
[ct
++] = obj_to_let(uright
);
5202 if (uright
->oclass
!= RING_CLASS
)
5203 use_inuse_mode
= TRUE
;
5206 lets
[ct
++] = obj_to_let(uleft
);
5207 if (uleft
->oclass
!= RING_CLASS
)
5208 use_inuse_mode
= TRUE
;
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");
5224 /* the #seeamulet command */
5229 You("are not wearing an amulet.");
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");
5243 /* is 'obj' a tool that's in use? can't simply check obj->owornmask */
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)
5253 if (obj
->oclass
!= TOOL_CLASS
)
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 */
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)
5274 lets
[ct
++] = obj_to_let(otmp
);
5278 You("are not using any tools.");
5280 (void) dispinv_with_action(lets
, TRUE
, NULL
);
5284 /* the #seeall command; combines the ')' + '[' + '=' + '"' + '(' commands;
5285 show inventory of all currently wielded, worn, or used objects */
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
)) {
5300 You("are not wearing or wielding anything.");
5302 (void) dispinv_with_action((char *) 0, TRUE
, NULL
);
5307 * uses up an object that's on the floor, charging for it as necessary
5310 useupf(struct obj
*obj
, long numused
)
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
);
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
);
5325 (void) stolen_value(otmp
, otmp
->ox
, otmp
->oy
, FALSE
, FALSE
);
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
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
;
5353 int oclass
= (let
>= 1 && let
< MAXOCLASSES
) ? let
: 0;
5357 class_name
= names
[oclass
];
5358 else if ((pos
= strchr(oth_symbols
, let
)) != 0)
5359 class_name
= oth_names
[pos
- oth_symbols
];
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
) {
5367 free((genericptr_t
) gi
.invbuf
);
5368 gi
.invbufsiz
= len
+ 10; /* add slop to reduce incremental realloc */
5369 gi
.invbuf
= (char *) alloc(gi
.invbufsiz
);
5372 Strcat(strcpy(gi
.invbuf
, "Unpaid "), class_name
);
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) {
5383 Sprintf(eos(gi
.invbuf
), ocsymfmt
, def_oc_syms
[oclass
].sym
);
5388 RESTORE_WARNING_FORMAT_NONLITERAL
5390 /* release the static buffer used by let_to_name() */
5395 free((genericptr_t
) gi
.invbuf
), gi
.invbuf
= (char *) 0;
5399 /* give consecutive letters to every item in inventory (for !fixinv mode);
5400 gold is always forced to '$' slot at head of list */
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
) {
5413 prevobj
->nobj
= goldobj
->nobj
;
5415 gi
.invent
= goldobj
->nobj
;
5418 /* second, re-letter the rest of the list */
5419 for (obj
= gi
.invent
, i
= 0; obj
; obj
= obj
->nobj
, i
++)
5421 (i
< 26) ? ('a' + i
) : (i
< 52) ? ('A' + i
- 26) : NOINVSYM
;
5422 /* third, assign gold the "letter" '$' and re-insert it at head */
5424 goldobj
->invlet
= GOLD_SYM
;
5425 goldobj
->nobj
= gi
.invent
;
5426 gi
.invent
= goldobj
;
5433 /* invent gold sanity check; used by doorganize() to control how getobj()
5434 deals with gold and also by wizard mode sanity_check() */
5436 check_invent_gold(const char *why
) /* 'why' == caller in case of warning */
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
) {
5445 if (otmp
->invlet
!= GOLD_SYM
)
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"
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 */
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 */
5474 adjust_gold_ok(struct obj
*obj
)
5477 return GETOBJ_EXCLUDE
;
5479 return GETOBJ_SUGGEST
;
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
*);
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");
5541 if (!flags
.invlet_constant
)
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 */
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) {
5569 /* get first digit; doesn't wait for <return> */
5570 dig
= yn_function("Split off how many?", (char *) 0, '\0', TRUE
);
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
)) {
5595 if (splitamount
< 1L || splitamount
>= obj
->quan
) {
5597 Amount
[] = "Amount to split from current stack must be";
5599 if (splitamount
< 1L)
5600 pline("%s at least 1.", Amount
);
5602 pline("%s less than %ld.", Amount
, obj
->quan
);
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
);
5615 doorganize_core(struct obj
*obj
)
5617 struct obj
*otmp
, *splitting
, *bumped
;
5618 int ix
, cur
, trycnt
;
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' */
5625 char *objname
, *otmpname
;
5626 const char *adj_type
;
5627 boolean ever_mind
= FALSE
, collect
, isgold
;
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
)
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';)
5651 for (let
= 'A'; let
<= 'Z';)
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
)) {
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
];
5679 /* and by dashing runs of letters */
5683 /* get 'to' slot to use as destination */
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);
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
)) {
5706 (void) merged(&splitting
, &obj
);
5710 } else if (let
== GOLD_SYM
&& obj
->oclass
!= COIN_CLASS
) {
5711 pline("Only gold coins may be moved into the '%c' slot.",
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 */
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:"
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;
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*/
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:";
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. */
5773 adj_type
= "Swapping:";
5774 otmp
->invlet
= obj
->invlet
;
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 */
5782 ONAME(obj
) = objname
;
5784 /* will merge; discard 'from' name */
5786 free((genericptr_t
) objname
), objname
= 0;
5789 if (merged(&otmp
, &obj
)) {
5790 adj_type
= "Splitting and merging:";
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.");
5800 extract_nobj(bumped
, &gi
.invent
);
5802 } /* moving vs splitting */
5803 break; /* not collecting and found 'to' slot */
5808 /* inline addinv; insert loose object at beginning of inventory */
5810 obj
->nobj
= gi
.invent
;
5811 obj
->where
= OBJ_INVENT
;
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
;
5825 /* messages deferred until inventory has been fully reestablished */
5826 prinv(adj_type
, obj
, 0L);
5828 prinv("Moving:", bumped
, 0L);
5830 clear_splitobjs(); /* reset splitobj context */
5835 /* common to display_minventory and display_cinventory */
5837 invdisp_nothing(const char *hdr
, const char *txt
)
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
);
5854 /* query_objlist callback: return things that are worn or wielded */
5856 worn_wield_only(struct obj
*obj
)
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);
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
)
5867 return (boolean
) (obj
->oclass
== WEAPON_CLASS
5868 || obj
->oclass
== ARMOR_CLASS
5869 || obj
->oclass
== AMULET_CLASS
5870 || obj
->oclass
== RING_CLASS
);
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
5889 struct monst
*mon
, /* monster whose minvent we're showing */
5890 int dflags
, /* control over what to display */
5891 char *title
) /* menu title */
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() */
5923 invdisp_nothing(title
? title
: tmp
, "(none)");
5928 ret
= selected
[0].item
.a_obj
;
5929 free((genericptr_t
) selected
);
5931 ret
= (struct obj
*) 0;
5935 /* format a container name for cinventory_display(), inserting "trapped"
5936 if that's appropriate */
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 ");
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 ");
5968 /* used by safe_qbuf() if the full doname() result is too long */
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() */
5985 (void) strsubst(result
, "", "trapped "); /* insert at beginning */
5990 /* Display the contents of a container in inventory style.
5991 Used for wand of probing of non-empty containers and statues. */
5993 display_cinventory(struct obj
*obj
)
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");
6007 n
= query_objlist(qbuf
, &(obj
->cobj
), INVORDER_SORT
,
6008 &selected
, PICK_NONE
, allow_all
);
6010 invdisp_nothing(qbuf
, "(empty)");
6014 ret
= selected
[0].item
.a_obj
;
6015 free((genericptr_t
) selected
);
6017 ret
= (struct obj
*) 0;
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
)
6040 const char *underwhat
= "here";
6041 menu_item
*selected
= 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
);
6059 /* "pair of boots" is singular but "beneath it" sounds strange */
6062 underwhat
= more_than_1
? "under them" : "beneath it";
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
)
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
) {
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;
6096 prepare_perminvent(winid window
)
6098 win_request_info
*wri
;
6099 int invmode
= (int) iflags
.perminv_mode
;
6101 if (perminv_flags
!= invmode
) {
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
;
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
))
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 */
6140 * The following conditions can bring us to here:
6141 * 1. iflags.perm_invent is on
6143 * gc.core_invent_state is still zero.
6145 * 2. iflags.perm_invent is off, but we're in the
6146 * midst of toggling it on.
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
);
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
;
6166 if ((wri
->tocore
.tocore_flags
& (too_small
| prohibited
))
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"
6178 pline("%s could not be enabled.", wport_id
);
6179 pline("%s needs a terminal that is at least %dx%d, yours "
6181 wport_id
, wri
->tocore
.needrows
,
6182 wri
->tocore
.needcols
, wri
->tocore
.haverows
,
6183 wri
->tocore
.havecols
);
6188 gc
.core_invent_state
++;
6192 if (!wri
|| wri
->tocore
.maxslot
== 0)
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;
6208 perm_invent_toggled(boolean negated
)
6210 in_perm_invent_toggled
= TRUE
;
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;
6217 gp
.perm_invent_toggling_direction
= toggling_on
;
6218 if (iflags
.perminv_mode
== InvOptNone
)
6219 iflags
.perminv_mode
= InvOptOn
; /* all inventory except gold */
6222 gp
.perm_invent_toggling_direction
= toggling_not
;
6223 in_perm_invent_toggled
= FALSE
;