diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index eeb18760d..d778e72cb 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -46,7 +46,7 @@ .ds f0 \*(vr .ds f1 \" empty .\"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: .\" \(lq = left double quote @@ -1576,21 +1576,44 @@ Default key is \(oqs\(cq. .lp "#seeall " Show all equipment in use. Default key is \(oq*\(cq. +.lp "" +Will display in-use items in a menu even when there is only one. .lp #seeamulet Show the amulet currently worn. 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 Show the armor currently worn. Default key is \(oq[\(cq. +.lp "" +Will display worn armor in a menu even when there is only thing worn. .lp #seerings Show the ring(s) currently worn. 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 Show the tools currently in use. 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 Show the weapon currently wielded. 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 " Do a shell escape, switching from NetHack to a subprocess. Can be disabled at the time the program is built. diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 5e9424daf..116ef004f 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -46,7 +46,7 @@ \author{Original version - Eric S. Raymond\\ (Edited and expanded for 3.7.0 by Mike Stephenson and others)} %DO NOT REMOVE NH_DATESUB \date{DATE(%B %-d, %Y)} -\date{November 13, 2023} +\date{November 27, 2023} \maketitle @@ -1697,21 +1697,47 @@ Search for traps and secret doors around you. Default key is `{\tt s}'. %.lp \item[\tb{\#seeall}] 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 \item[\tb{\#seeamulet}] 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 \item[\tb{\#seearmor}] 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 \item[\tb{\#seerings}] 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 \item[\tb{\#seetools}] 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 \item[\tb{\#seeweapon}] 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 \item[\tb{\#shell}] Do a shell escape, switching from NetHack to a subprocess. diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index c4997d8de..f2d29c1a6 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -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 instead of by object class (sortpack) or inventory letter (!sortpack); 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 diff --git a/src/cmd.c b/src/cmd.c index 0c7f23b59..268224887 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -2698,17 +2698,17 @@ struct ext_func_tab extcmdlist[] = { { 's', "search", "search for traps and secret doors", dosearch, IFBURIED | CMD_M_PREFIX, "searching" }, { '*', "seeall", "show all equipment in use", - doprinuse, IFBURIED, NULL }, + doprinuse, IFBURIED | CMD_M_PREFIX, NULL }, { 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", - doprarm, IFBURIED, NULL }, + doprarm, IFBURIED | CMD_M_PREFIX, NULL }, { 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", - doprtool, IFBURIED, NULL }, + doprtool, IFBURIED | CMD_M_PREFIX, NULL }, { 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)", dosh_core, (IFBURIED | GENERALCMD | NOFUZZERCMD #ifndef SHELL @@ -2717,7 +2717,7 @@ struct ext_func_tab extcmdlist[] = { ), NULL }, /* $ is like ),=,&c but is not included with *, so not called "seegold" */ { 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", dovspell, IFBURIED, NULL }, { '^', "showtrap", "describe an adjacent, discovered trap", diff --git a/src/invent.c b/src/invent.c index dcb934d97..5cae0e56c 100644 --- a/src/invent.c +++ b/src/invent.c @@ -24,6 +24,7 @@ static char *cinv_ansimpleoname(struct obj *); static boolean only_here(struct obj *); static void compactify(char *); static boolean taking_off(const char *); +static void mime_action(const char *); static int ckvalidcat(struct obj *); static int ckunpaid(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 void ia_addmenu(winid, int, char, const char *); 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 */ 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) */ -/* menu heading lines used instead of object classes when sorting by in-use */ -static const char *const inuse_headers[] = { /* [4] shown first, [1] last */ +/* menu heading lines used instead of object classes when sorting by in-use; + 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", "Wielded/Readied Weapons", "Accessories", }; @@ -3362,13 +3366,40 @@ itemactions(struct obj *otmp) return ECMD_OK; } - -/* the #inventory command */ -int -ddoinv(void) +/* show some or all of inventory while allowing the picking of an item in + order to preform context-sensitive item action on it; always returns 'ok'; + invent subsets specified by the ')', '[', '(', '=', '"', or '*' commands + 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; - 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') { for (otmp = gi.invent; otmp; otmp = otmp->nobj) @@ -3378,6 +3409,13 @@ ddoinv(void) return ECMD_OK; } +/* the #inventory command (not much left...) */ +int +ddoinv(void) +{ + return dispinv_with_action((char *) 0, FALSE, NULL); +} + /* * find_unpaid() * @@ -3515,7 +3553,7 @@ display_pickinv( if (!flags.invlet_constant) 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; we actually use a fake message-line menu in order to allow the user to perform selection at the --More-- prompt for tty */ @@ -4860,6 +4898,14 @@ doprgold(void) You("have no money."); } 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; } @@ -4869,10 +4915,24 @@ doprwep(void) { if (!uwep) { You("are %s.", empty_handed()); - } else { + } else if (!iflags.menu_requested) { prinv((char *) 0, uwep, 0L); if (u.twoweap) 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; } @@ -4904,8 +4964,6 @@ noarmor(boolean report_uskin) int doprarm(void) { - char lets[8]; - int ct = 0; /* * Note: players sometimes get here by pressing a function key which * transmits ''ESC [ '' rather than by pressing '['; @@ -4915,22 +4973,30 @@ doprarm(void) if (!wearing_armor()) { noarmor(TRUE); } else { - if (uarmu) - lets[ct++] = obj_to_let(uarmu); + char lets[8]; /* 8: up to 7 pieces of armor plus 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, 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) lets[ct++] = obj_to_let(uarm); if (uarmc) lets[ct++] = obj_to_let(uarmc); - if (uarmh) - lets[ct++] = obj_to_let(uarmh); if (uarms) lets[ct++] = obj_to_let(uarms); + if (uarmh) + lets[ct++] = obj_to_let(uarmh); if (uarmg) lets[ct++] = obj_to_let(uarmg); if (uarmf) lets[ct++] = obj_to_let(uarmf); + if (uarmu) + lets[ct++] = obj_to_let(uarmu); lets[ct] = 0; - (void) display_inventory(lets, FALSE); + + (void) dispinv_with_action(lets, TRUE, NULL); } return ECMD_OK; } @@ -4942,15 +5008,32 @@ doprring(void) if (!uleft && !uright) { You("are not wearing any rings."); } else { - char lets[3]; + char lets[3]; /* 3: uright, uleft, terminator */ + boolean use_inuse_mode = FALSE; int ct = 0; - if (uleft) - lets[ct++] = obj_to_let(uleft); - if (uright) + /* if either ring is a meat ring, switch to use_inuse_mode in order + to label it/them as "Rings" rather than "Comestibles" */ + if (uright) { lets[ct++] = obj_to_let(uright); - lets[ct] = 0; - (void) display_inventory(lets, FALSE); + if (uright->oclass != RING_CLASS) + 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; } @@ -4959,10 +5042,18 @@ doprring(void) int dopramulet(void) { - if (!uamul) + if (!uamul) { You("are not wearing an amulet."); - else - prinv((char *) 0, uamul, 0L); + } else { + 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; } @@ -5003,7 +5094,7 @@ doprtool(void) if (!ct) You("are not using any tools."); else - (void) display_inventory(lets, FALSE); + (void) dispinv_with_action(lets, TRUE, NULL); return ECMD_OK; } @@ -5014,41 +5105,18 @@ doprinuse(void) { struct obj *otmp; 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 - 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) if (is_inuse(otmp)) { ++ct; break; } - 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 + if (!ct) You("are not wearing or wielding anything."); + else + (void) dispinv_with_action((char *) 0, TRUE, NULL); return ECMD_OK; }