item actions for '* ) [ = " (' commands

Make the various item-in-use commands put up a menu--which allows
choosing an item for context-sensitive item action--if/when preceded
by the 'm' prefix.  Some of them do that even without the prefix ('*',
'[', or '=' when more than one ring is worn).  By default '(' shows
primary weapon, then secondary one if dual-wielding.  'm (' shows a
menu of primary, alternate whether dual-wielding it or not, and quiver.
This commit is contained in:
PatR
2023-11-28 00:31:21 -08:00
parent 75104c69ab
commit e6a7eb25d4
5 changed files with 184 additions and 63 deletions

View File

@@ -46,7 +46,7 @@
.ds f0 \*(vr .ds f0 \*(vr
.ds f1 \" empty .ds f1 \" empty
.\"DO NOT REMOVE NH_DATESUB .ds f2 DATE(%B %-d, %Y) .\"DO NOT REMOVE NH_DATESUB .ds f2 DATE(%B %-d, %Y)
.ds f2 "November 13, 2023 .ds f2 "November 17, 2023
. .
.\" A note on some special characters: .\" A note on some special characters:
.\" \(lq = left double quote .\" \(lq = left double quote
@@ -1576,21 +1576,44 @@ Default key is \(oqs\(cq.
.lp "#seeall " .lp "#seeall "
Show all equipment in use. Show all equipment in use.
Default key is \(oq*\(cq. Default key is \(oq*\(cq.
.lp ""
Will display in-use items in a menu even when there is only one.
.lp #seeamulet .lp #seeamulet
Show the amulet currently worn. Show the amulet currently worn.
Default key is \(oq\(dq\(cq. \" double quote Default key is \(oq\(dq\(cq. \" double quote
.lp ""
Using the \(oq\f(CRm\fP\(cq prefix will force the display of a worn
amulet in a menu rather than with just a message.
.lp #seearmor .lp #seearmor
Show the armor currently worn. Show the armor currently worn.
Default key is \(oq[\(cq. Default key is \(oq[\(cq.
.lp ""
Will display worn armor in a menu even when there is only thing worn.
.lp #seerings .lp #seerings
Show the ring(s) currently worn. Show the ring(s) currently worn.
Default key is \(oq=\(cq. Default key is \(oq=\(cq.
.lp ""
Will display worn rings in a menu if there are two (or there is
just one and is a meat ring rather than a \(lqreal\(rq ring).
Use the \(oq\f(CRm\fP\(cq prefix to force a menu for one ring.
.lp #seetools .lp #seetools
Show the tools currently in use. Show the tools currently in use.
Default key is \(oq(\(cq. Default key is \(oq(\(cq.
.lp ""
Will display the result in a message if there is one tool in use (worn
blindfold or towel or lenses, lit lamp(s) and/or candle(s), leashes
attached to pets).
Will display a menu if there are more than one or if the command is
preceded by the \(oq\f(CRm\fP\(cq prefix.
.lp #seeweapon .lp #seeweapon
Show the weapon currently wielded. Show the weapon currently wielded.
Default key is \(oq)\(cq. Default key is \(oq)\(cq.
.lp ""
If dual-wielding, a separate message about the secondary weapon will be
given.
Using the \(oq\f(CRm\fP\(cq prefix will force a menu and it will include
primary weapon, alternate weapon even when not dual-wielding, and also
whatever is currently assigned to the quiver slot.
.lp "#shell " .lp "#shell "
Do a shell escape, switching from NetHack to a subprocess. Do a shell escape, switching from NetHack to a subprocess.
Can be disabled at the time the program is built. Can be disabled at the time the program is built.

View File

@@ -46,7 +46,7 @@
\author{Original version - Eric S. Raymond\\ \author{Original version - Eric S. Raymond\\
(Edited and expanded for 3.7.0 by Mike Stephenson and others)} (Edited and expanded for 3.7.0 by Mike Stephenson and others)}
%DO NOT REMOVE NH_DATESUB \date{DATE(%B %-d, %Y)} %DO NOT REMOVE NH_DATESUB \date{DATE(%B %-d, %Y)}
\date{November 13, 2023} \date{November 27, 2023}
\maketitle \maketitle
@@ -1697,21 +1697,47 @@ Search for traps and secret doors around you. Default key is `{\tt s}'.
%.lp %.lp
\item[\tb{\#seeall}] \item[\tb{\#seeall}]
Show all equipment in use. Default key is `{\tt *}'. Show all equipment in use. Default key is `{\tt *}'.
%.lp ""
\\
Will display in-use items in a menu even when there is only one.
%.lp %.lp
\item[\tb{\#seeamulet}] \item[\tb{\#seeamulet}]
Show the amulet currently worn. Default key is `{\tt "}'. Show the amulet currently worn. Default key is `{\tt "}'.
%.lp ""
\\
Using the `{\tt m}' prefix will force the display of a worn
amulet in a menu rather than with just a message.
%.lp %.lp
\item[\tb{\#seearmor}] \item[\tb{\#seearmor}]
Show the armor currently worn. Default key is `{\tt [}'. Show the armor currently worn. Default key is `{\tt [}'.
%.lp ""
\\
Will display worn armor in a menu even when there is only thing worn.
%.lp %.lp
\item[\tb{\#seerings}] \item[\tb{\#seerings}]
Show the ring(s) currently worn. Default key is `{\tt =}'. Show the ring(s) currently worn. Default key is `{\tt =}'.
%.lp ""
Will display worn rings in a menu if there are two (or there is
just one and is a meat ring rather than a ``real'' ring).
Use the `{\tt m}' prefix to force a menu for one ring.
%.lp %.lp
\item[\tb{\#seetools}] \item[\tb{\#seetools}]
Show the tools currently in use. Default key is `{\tt (}'. Show the tools currently in use. Default key is `{\tt (}'.
%.lp ""
Will display the result in a message if there is one tool in use (worn
blindfold or towel or lenses, lit lamp(s) and/or candle(s), leashes
attached to pets).
Will display a menu if there are more than one or if the command is
preceded by the `{\tt m}' prefix.
%.lp %.lp
\item[\tb{\#seeweapon}] \item[\tb{\#seeweapon}]
Show the weapon currently wielded. Default key is `{\tt )}'. Show the weapon currently wielded. Default key is `{\tt )}'.
%.lp ""
If dual-wielding, a separate message about the secondary weapon will be
given.
Using the `{\tt m}' prefix will force a menu and it will include
primary weapon, alternate weapon even when not dual-wielding, and also
whatever is currently assigned to the quiver slot.
%.lp %.lp
\item[\tb{\#shell}] \item[\tb{\#shell}]
Do a shell escape, switching from NetHack to a subprocess. Do a shell escape, switching from NetHack to a subprocess.

View File

@@ -2351,6 +2351,10 @@ the '*' command (list inventory items in use) now shows items in a specific
gloves, boots, shirt, lit lamps and candles, leashes attached to pets gloves, boots, shirt, lit lamps and candles, leashes attached to pets
instead of by object class (sortpack) or inventory letter (!sortpack); instead of by object class (sortpack) or inventory letter (!sortpack);
new perm_invent+perminv_mode==inuse does the same new perm_invent+perminv_mode==inuse does the same
allow the ')', '[', '(', '=', '"', and '*' commands to be preceded by the 'm'
prefix to force a menu where the player can pick an item and choose
a context-sensitive item action for it; some give a menu even without
the prefix and for those, same item selection and action can be used
Platform- and/or Interface-Specific New Features Platform- and/or Interface-Specific New Features

View File

@@ -2698,17 +2698,17 @@ struct ext_func_tab extcmdlist[] = {
{ 's', "search", "search for traps and secret doors", { 's', "search", "search for traps and secret doors",
dosearch, IFBURIED | CMD_M_PREFIX, "searching" }, dosearch, IFBURIED | CMD_M_PREFIX, "searching" },
{ '*', "seeall", "show all equipment in use", { '*', "seeall", "show all equipment in use",
doprinuse, IFBURIED, NULL }, doprinuse, IFBURIED | CMD_M_PREFIX, NULL },
{ AMULET_SYM, "seeamulet", "show the amulet currently worn", { AMULET_SYM, "seeamulet", "show the amulet currently worn",
dopramulet, IFBURIED, NULL }, dopramulet, IFBURIED | CMD_M_PREFIX, NULL },
{ ARMOR_SYM, "seearmor", "show the armor currently worn", { ARMOR_SYM, "seearmor", "show the armor currently worn",
doprarm, IFBURIED, NULL }, doprarm, IFBURIED | CMD_M_PREFIX, NULL },
{ RING_SYM, "seerings", "show the ring(s) currently worn", { RING_SYM, "seerings", "show the ring(s) currently worn",
doprring, IFBURIED, NULL }, doprring, IFBURIED | CMD_M_PREFIX, NULL },
{ TOOL_SYM, "seetools", "show the tools currently in use", { TOOL_SYM, "seetools", "show the tools currently in use",
doprtool, IFBURIED, NULL }, doprtool, IFBURIED | CMD_M_PREFIX, NULL },
{ WEAPON_SYM, "seeweapon", "show the weapon currently wielded", { WEAPON_SYM, "seeweapon", "show the weapon currently wielded",
doprwep, IFBURIED, NULL }, doprwep, IFBURIED | CMD_M_PREFIX, NULL },
{ '!', "shell", "leave game to enter a sub-shell ('exit' to come back)", { '!', "shell", "leave game to enter a sub-shell ('exit' to come back)",
dosh_core, (IFBURIED | GENERALCMD | NOFUZZERCMD dosh_core, (IFBURIED | GENERALCMD | NOFUZZERCMD
#ifndef SHELL #ifndef SHELL
@@ -2717,7 +2717,7 @@ struct ext_func_tab extcmdlist[] = {
), NULL }, ), NULL },
/* $ is like ),=,&c but is not included with *, so not called "seegold" */ /* $ is like ),=,&c but is not included with *, so not called "seegold" */
{ GOLD_SYM, "showgold", "show gold, possibly shop credit or debt", { GOLD_SYM, "showgold", "show gold, possibly shop credit or debt",
doprgold, IFBURIED, NULL }, doprgold, IFBURIED | CMD_M_PREFIX, NULL },
{ SPBOOK_SYM, "showspells", "list and reorder known spells", { SPBOOK_SYM, "showspells", "list and reorder known spells",
dovspell, IFBURIED, NULL }, dovspell, IFBURIED, NULL },
{ '^', "showtrap", "describe an adjacent, discovered trap", { '^', "showtrap", "describe an adjacent, discovered trap",

View File

@@ -24,6 +24,7 @@ static char *cinv_ansimpleoname(struct obj *);
static boolean only_here(struct obj *); static boolean only_here(struct obj *);
static void compactify(char *); static void compactify(char *);
static boolean taking_off(const char *); static boolean taking_off(const char *);
static void mime_action(const char *);
static int ckvalidcat(struct obj *); static int ckvalidcat(struct obj *);
static int ckunpaid(struct obj *); static int ckunpaid(struct obj *);
static char *safeq_xprname(struct obj *); static char *safeq_xprname(struct obj *);
@@ -44,7 +45,8 @@ static boolean item_naming_classification(struct obj *, char *, char *);
static int item_reading_classification(struct obj *, char *); static int item_reading_classification(struct obj *, char *);
static void ia_addmenu(winid, int, char, const char *); static void ia_addmenu(winid, int, char, const char *);
static void itemactions_pushkeys(struct obj *, int); static void itemactions_pushkeys(struct obj *, int);
static void mime_action(const char *); static int itemactions(struct obj *);
static int dispinv_with_action(char *, boolean, const char *);
/* enum and structs are defined in wintype.h */ /* enum and structs are defined in wintype.h */
static win_request_info wri_info; static win_request_info wri_info;
@@ -62,8 +64,10 @@ static boolean in_perm_invent_toggled;
*/ */
static const char venom_inv[] = { VENOM_CLASS, 0 }; /* (constant) */ static const char venom_inv[] = { VENOM_CLASS, 0 }; /* (constant) */
/* menu heading lines used instead of object classes when sorting by in-use */ /* menu heading lines used instead of object classes when sorting by in-use;
static const char *const inuse_headers[] = { /* [4] shown first, [1] last */ pointers aren't const because dispinv_with_action() might temporarily
change "Accessories" to "Rings" or "Amulet", then back again */
static const char *inuse_headers[] = { /* [4] shown first, [1] last */
"", "Miscellaneous", "Worn Armor", "", "Miscellaneous", "Worn Armor",
"Wielded/Readied Weapons", "Accessories", "Wielded/Readied Weapons", "Accessories",
}; };
@@ -3362,13 +3366,40 @@ itemactions(struct obj *otmp)
return ECMD_OK; return ECMD_OK;
} }
/* show some or all of inventory while allowing the picking of an item in
/* the #inventory command */ order to preform context-sensitive item action on it; always returns 'ok';
int invent subsets specified by the ')', '[', '(', '=', '"', or '*' commands
ddoinv(void) when they're invoked with the 'm' prefix (or without it for '*') */
static int
dispinv_with_action(
char *lets, /* list of invlet values to include */
boolean use_inuse_ordering, /* affects sortloot() and header labels */
const char *alt_label) /* alternate value for in-use "Accessories" */
{ {
struct obj *otmp; struct obj *otmp;
char c = display_inventory((char *) 0, TRUE); const char *save_accessories = 0;
char c, save_sortloot = 0;
unsigned len = lets ? (unsigned) strlen(lets) : 0U;
boolean menumode = (len != 1 || iflags.menu_requested) ? TRUE : FALSE,
save_force_invmenu = iflags.force_invmenu;
if (use_inuse_ordering) {
save_accessories = inuse_headers[4];
save_sortloot = flags.sortloot;
flags.sortloot = 'i'; /* checked by display_pickinv() */
if (alt_label)
inuse_headers[4] = alt_label;
}
iflags.force_invmenu = FALSE;
c = display_inventory(lets, menumode);
if (use_inuse_ordering) {
flags.sortloot = save_sortloot;
inuse_headers[4] = save_accessories;
}
iflags.force_invmenu = save_force_invmenu;
if (c && c != '\033') { if (c && c != '\033') {
for (otmp = gi.invent; otmp; otmp = otmp->nobj) for (otmp = gi.invent; otmp; otmp = otmp->nobj)
@@ -3378,6 +3409,13 @@ ddoinv(void)
return ECMD_OK; return ECMD_OK;
} }
/* the #inventory command (not much left...) */
int
ddoinv(void)
{
return dispinv_with_action((char *) 0, FALSE, NULL);
}
/* /*
* find_unpaid() * find_unpaid()
* *
@@ -3515,7 +3553,7 @@ display_pickinv(
if (!flags.invlet_constant) if (!flags.invlet_constant)
reassign(); reassign();
if (n == 1 && !iflags.force_invmenu) { if (n == 1 && !iflags.force_invmenu && !iflags.menu_requested) {
/* when only one item of interest, use pline instead of menus; /* when only one item of interest, use pline instead of menus;
we actually use a fake message-line menu in order to allow we actually use a fake message-line menu in order to allow
the user to perform selection at the --More-- prompt for tty */ the user to perform selection at the --More-- prompt for tty */
@@ -4860,6 +4898,14 @@ doprgold(void)
You("have no money."); You("have no money.");
} }
shopper_financial_report(); shopper_financial_report();
if (umoney && iflags.menu_requested) {
char dollarsign[] = "$";
/* mustn't use TRUE or gold wouldn't show up unless it was quivered */
(void) dispinv_with_action(dollarsign, FALSE, NULL);
}
return ECMD_OK; return ECMD_OK;
} }
@@ -4869,10 +4915,24 @@ doprwep(void)
{ {
if (!uwep) { if (!uwep) {
You("are %s.", empty_handed()); You("are %s.", empty_handed());
} else { } else if (!iflags.menu_requested) {
prinv((char *) 0, uwep, 0L); prinv((char *) 0, uwep, 0L);
if (u.twoweap) if (u.twoweap)
prinv((char *) 0, uswapwep, 0L); prinv((char *) 0, uswapwep, 0L);
} else {
char lets[4]; /* 4: uwep, uswapwep, uquiver, terminator */
int ct = 0;
/* obj_to_let() will assign letters to all of invent if necessary
(for '!fixinv') so doesn't need to be repeated once called here */
lets[ct++] = obj_to_let(uwep);
if (uswapwep)
lets[ct++] = uswapwep->invlet;
if (uquiver)
lets[ct++] = uquiver->invlet;
lets[ct] = '\0';
(void) dispinv_with_action(lets, TRUE, NULL);
} }
return ECMD_OK; return ECMD_OK;
} }
@@ -4904,8 +4964,6 @@ noarmor(boolean report_uskin)
int int
doprarm(void) doprarm(void)
{ {
char lets[8];
int ct = 0;
/* /*
* Note: players sometimes get here by pressing a function key which * Note: players sometimes get here by pressing a function key which
* transmits ''ESC [ <something>'' rather than by pressing '['; * transmits ''ESC [ <something>'' rather than by pressing '[';
@@ -4915,22 +4973,30 @@ doprarm(void)
if (!wearing_armor()) { if (!wearing_armor()) {
noarmor(TRUE); noarmor(TRUE);
} else { } else {
if (uarmu) char lets[8]; /* 8: up to 7 pieces of armor plus terminator */
lets[ct++] = obj_to_let(uarmu); int ct = 0;
/* obj_to_let() will assign letters to all of invent if necessary
(for '!fixinv') so doesn't need to be repeated once called, but
each armor slot doesn't know whether any that precede have made
that call so just do it for each one; use SORTPACK_INUSE order */
if (uarm) if (uarm)
lets[ct++] = obj_to_let(uarm); lets[ct++] = obj_to_let(uarm);
if (uarmc) if (uarmc)
lets[ct++] = obj_to_let(uarmc); lets[ct++] = obj_to_let(uarmc);
if (uarmh)
lets[ct++] = obj_to_let(uarmh);
if (uarms) if (uarms)
lets[ct++] = obj_to_let(uarms); lets[ct++] = obj_to_let(uarms);
if (uarmh)
lets[ct++] = obj_to_let(uarmh);
if (uarmg) if (uarmg)
lets[ct++] = obj_to_let(uarmg); lets[ct++] = obj_to_let(uarmg);
if (uarmf) if (uarmf)
lets[ct++] = obj_to_let(uarmf); lets[ct++] = obj_to_let(uarmf);
if (uarmu)
lets[ct++] = obj_to_let(uarmu);
lets[ct] = 0; lets[ct] = 0;
(void) display_inventory(lets, FALSE);
(void) dispinv_with_action(lets, TRUE, NULL);
} }
return ECMD_OK; return ECMD_OK;
} }
@@ -4942,15 +5008,32 @@ doprring(void)
if (!uleft && !uright) { if (!uleft && !uright) {
You("are not wearing any rings."); You("are not wearing any rings.");
} else { } else {
char lets[3]; char lets[3]; /* 3: uright, uleft, terminator */
boolean use_inuse_mode = FALSE;
int ct = 0; int ct = 0;
if (uleft) /* if either ring is a meat ring, switch to use_inuse_mode in order
lets[ct++] = obj_to_let(uleft); to label it/them as "Rings" rather than "Comestibles" */
if (uright) if (uright) {
lets[ct++] = obj_to_let(uright); lets[ct++] = obj_to_let(uright);
lets[ct] = 0; if (uright->oclass != RING_CLASS)
(void) display_inventory(lets, FALSE); use_inuse_mode = TRUE;
}
if (uleft) {
lets[ct++] = obj_to_let(uleft);
if (uleft->oclass != RING_CLASS)
use_inuse_mode = TRUE;
}
lets[ct] = '\0';
/* also switch to use_inuse_mode if there are two rings or player
used the 'm' prefix */
if (ct > 1 || iflags.menu_requested)
use_inuse_mode = TRUE;
(void) dispinv_with_action(lets, use_inuse_mode,
/* note; alternate label will be ignored
if 'use_inuse_mode' is False */
(ct == 1) ? "Ring" : "Rings");
} }
return ECMD_OK; return ECMD_OK;
} }
@@ -4959,10 +5042,18 @@ doprring(void)
int int
dopramulet(void) dopramulet(void)
{ {
if (!uamul) if (!uamul) {
You("are not wearing an amulet."); You("are not wearing an amulet.");
else } else {
prinv((char *) 0, uamul, 0L); char lets[2];
/* using display_inventory() instead of prinv() allows player
to use 'm "' to force and menu and be able to choose amulet
in order to perform a context-sensitve item action */
lets[0] = obj_to_let(uamul), lets[1] = '\0';
(void) dispinv_with_action(lets, TRUE, "Amulet");
}
return ECMD_OK; return ECMD_OK;
} }
@@ -5003,7 +5094,7 @@ doprtool(void)
if (!ct) if (!ct)
You("are not using any tools."); You("are not using any tools.");
else else
(void) display_inventory(lets, FALSE); (void) dispinv_with_action(lets, TRUE, NULL);
return ECMD_OK; return ECMD_OK;
} }
@@ -5014,41 +5105,18 @@ doprinuse(void)
{ {
struct obj *otmp; struct obj *otmp;
int ct = 0; int ct = 0;
#if 0 /* old doprinuse() */
char lets[52 + 1];
for (otmp = gi.invent; otmp; otmp = otmp->nobj)
if (is_inuse(otmp)) {
/* we could be carrying more than 52 items; theoretically they
might all be lit candles so avoid potential lets[] overflow */
if (ct >= (int) sizeof lets - 1)
break;
lets[ct++] = obj_to_let(otmp);
}
lets[ct] = '\0';
if (ct)
(void) display_inventory(lets, FALSE);
#else /* new */
/* no longer need to collect letters; sortloot() takes care of it, but /* no longer need to collect letters; sortloot() takes care of it, but
still need to count far enough to know whether anything is in use */ still want to count far enough to know whether anything is in use */
for (otmp = gi.invent; otmp; otmp = otmp->nobj) for (otmp = gi.invent; otmp; otmp = otmp->nobj)
if (is_inuse(otmp)) { if (is_inuse(otmp)) {
++ct; ++ct;
break; break;
} }
if (ct) { if (!ct)
char save_sortloot = flags.sortloot;
flags.sortloot = 'i';
/* bypass display_inventory() and go straight to display_pickinv() */
(void) display_pickinv((char *) 0, (char *) 0, (char *) 0,
/* 'want_reply' forces window other than WIN_INVENT */
TRUE, (long *) 0);
flags.sortloot = save_sortloot;
}
#endif
else
You("are not wearing or wielding anything."); You("are not wearing or wielding anything.");
else
(void) dispinv_with_action((char *) 0, TRUE, NULL);
return ECMD_OK; return ECMD_OK;
} }