merge new use_menu_glyphs option with menu_objsyms

The two options are very similar but probably mutually exclusive
except when using look-here and look-into-container (both via ':')
with the default setting for 'sortloot', or with inventory when
'sortpack' has been toggled off.

This removes 'use_menu_glyphs' and changes 'menu_objsyms' from a
boolean to a compound taking six possible values:
| 0: no object symbols in menus,
| 1: append object class symbol to object header lines (same as old
|menu_objsyms boolean),
| 2: include object symbol in menu entry lines for objects (same as
|recently added use_menu_glyphs),
| 3: both 1 and 2,
| 4: display as #2 but only if the menu lacks class header lines,
| 5: if header lines are present, display as #1; if headers are not
|present, then display as #4 (which will implicitly be #2).
Default is #4.

Effectively replaces the options portion of pull request #1406 and
retains the functionality, but not as default for normal menus.

Guidebook.tex is only partially updated.  Someone else will need to
finish that.
This commit is contained in:
PatR
2025-04-28 18:12:02 -07:00
parent ba4f90eefb
commit a587ccaa26
8 changed files with 281 additions and 33 deletions

View File

@@ -40,7 +40,6 @@ mail enable the mail daemon [True]
mention_decor give feedback when walking across stairs, altars, [False]
fountains, and such even when not obscured by objects
mention_walls give feedback when walking against a wall [False]
menu_objsyms show object symbols in menus if it is selectable [False]
menu_overlay overlay menus on the screen and align to right [True]
menucolors enable MENUCOLOR pattern matching to highlight [False]
lines in object menus like inventory; might highlight with
@@ -80,7 +79,6 @@ travel enables travelling via mouse click if supported; [True]
attempting to move the hero; does not affect travel via '_'
use_darkgray use bold black instead of blue for black glyphs. [True]
use_inverse display detected monsters in highlighted manner [False]
use_menu_glyphs show object glyphs in menu items (tty, curses) [True]
verbose print more commentary during the game [True]
whatis_menu show menu when getting a map location [False]
whatis_moveskip skip same glyphs when getting a map location [False]
@@ -191,6 +189,16 @@ menustyle user interface for selection of multiple objects: [Full]
only the first letter ('T','C','F','P') matters
(With Traditional, many actions allow pseudo-class 'm' to
request a menu for choosing items: one-shot Combination.)
menu_objsyms whether to include object class symbols in menus: [5]
0 - none -- don't add object symbols to menus;
1 - headers -- append object class symbol to menu header lines;
2 - entries -- show object glyphs (same as class symbol
for ASCII interfaces) on each menu entry line;
3 - both -- 1 and 2 combined;
4 - conditional -- as 2 but only if no headers are present;
5 - one-or-other -- 1 and 4 combined;
choices 0 and 1 should work with any interface; 2 through 5
are supported by tty and curses
msg_window behavior of ^P message recall for tty interface: [s]
single -- one message at a time
full -- full window with all saved top line messages

View File

@@ -4169,8 +4169,38 @@ Default \(oq|\(cq.
Key to go to the next menu page.
Default \(oq>\(cq.
.lp menu_objsyms
Show object symbols in menu headings in menus where
the object symbols act as menu accelerators (default off).
." [originally menu_objsyms was a boolean]
." Show object symbols in menu headings in menus where
." the object symbols act as menu accelerators (default off).
Inventory and other object menus are normally separated by object class
(weapons, armor, and so forth), with a menu header line at the beginning
of each group.
You can have menus add the display symbol for the class of objects for
each header line.
You can also add the display symbol for the individual item in each menu
entry.
For a tiles map, that would be a small rendition of an object's tile.
For a text map, it is the same character as is used for the object's
class, which would be most useful when there are no headers separating
objects among classes.
Possible values are
.PS "5\ -\ One-or-other"
.PL "0\ -\ None"
no symbols for either header lines or menu entries;
.PL "1\ -\ Headers"
show symbols on header lines but not entries;
.PL "2\ -\ Entries"
show symbols on menu entry lines but not headers;
.PL "3\ -\ Both"
show symbols on headers and entries;
.PL "4\ -\ Conditional"
only show symbols for entries if there are no headers;
.PL "5\ -\ One-or-other"
show symbols on headers, or on entries if no headers.
.PE
Supported by tty and curses.
When setting the value, it can be specified by digit or keyword.
The default value is \f(CRConditional\fP (4).
.lp menu_overlay
Do not clear the screen before drawing menus, and align
menus to the right edge of the screen. Only for the tty port.
@@ -4988,9 +5018,6 @@ to be \fIFalse\fP.
Use bold black instead of blue for black glyphs (TTY only).
.lp use_inverse
If NetHack can, it should display inverse when the game specifies it.
.lp use_menu_glyphs
If NetHack can, it should display glyphs next to objects in the
inventory.
.lp vary_msgcount
If NetHack can, it should display this number of messages at a time in
the message window.

View File

@@ -4576,8 +4576,40 @@ Key to go to the next menu page.
Implemented by the Amiga, Gem and tty ports.
Default `\verb+>+'.
\item[\ib{menu\verb+_+objsyms}]
Show object symbols in menu headings in menus where
the object symbols act as menu accelerators (default off).
% [originally menu_objsyms was a boolean]
% Show object symbols in menu headings in menus where
% the object symbols act as menu accelerators (default off).
Inventory and other object menus are normally separated by object class
(weapons, armor, and so forth), with a menu header line at the beginning
of each group.
You can have menus add the display symbol for the class of objects for
each header line.
You can also add the display symbol for the individual item in each menu
entry.
For a tiles map, that would be a small rendition of an object's tile.
For a text map, it is the same character as is used for the object's
class, which would be most useful when there are no headers separating
objects among classes.
% FIXME! -- three column table may be simpler than mashing first two together
% Possible values are
% .PS "5\ -\ One-or-other"
% .PL "0\ -\ None"
% no symbols for either header lines or menu entries;
% .PL "1\ -\ Headers"
% show symbols on header lines but not entries;
% .PL "2\ -\ Entries"
% show symbols on menu entry lines but not headers;
% .PL "3\ -\ Both"
% show symbols on headers and entries;
% .PL "4\ -\ Conditional"
% only show symbols for entries if there are no headers;
% .PL "5\ -\ One-or-other"
% show symbols on headers, or on entries if no headers.
% .PE
Supported by tty and curses.
When setting the value, it can be specified by digit or keyword.
The default value is {\tt Conditional} (4).
\item[\ib{menu\verb+_+overlay}]
Do not clear the screen before drawing menus, and align
menus to the right edge of the screen. Only for the tty port.

View File

@@ -264,6 +264,8 @@ struct instance_flags {
int getloc_filter; /* GFILTER_foo */
int in_lava_effects; /* hack for Boots_off() */
int last_msg; /* indicator of last message player saw */
int menuobjsyms; /* value of 'menu_objsyms' option;
* ought to be in flags rather than iflags */
int override_ID; /* true to force full identification of objects */
int parse_config_file_src; /* hack for parse_config_line() */
int purge_monsters; /* # of dead monsters still on fmon list */
@@ -301,12 +303,12 @@ struct instance_flags {
unsigned msg_history; /* hint: # of top lines to save */
int getpos_coords; /* show coordinates when getting cursor position */
int menuinvertmode; /* 0 = invert toggles every item;
1 = invert skips 'all items' item */
* 1 = invert skips 'all items' item */
color_attr menu_headings; /* CLR_ and ATR_ for menu headings */
uint32_t colorcount; /* store how many colors terminal is capable of */
boolean use_truecolor; /* force use of truecolor */
#ifdef ALTMETA
boolean altmeta; /* Alt-c sends ESC c rather than M-c */
boolean altmeta; /* Alt+c sends ESC c rather than M-c */
#endif
boolean autodescribe; /* autodescribe mode in getpos() */
boolean cbreak; /* in cbreak mode, rogue format */
@@ -315,7 +317,8 @@ struct instance_flags {
boolean echo; /* 1 to echo characters */
boolean force_invmenu; /* always menu when handling inventory */
boolean hilite_pile; /* mark piles of objects with a hilite */
boolean menu_head_objsym; /* Show obj symbol in menu headings */
boolean menu_head_objsym; /* Show obj symbol in menu headings; controlled
* by 'menuobjsyms' */
boolean menu_overlay; /* Draw menus over the map */
boolean menu_requested; /* Flag for overloaded use of 'm' prefix
* on some non-move commands */
@@ -334,7 +337,8 @@ struct instance_flags {
boolean tux_penalty; /* True iff hero is a monk and wearing a suit */
boolean use_background_glyph; /* use background glyph when appropriate */
boolean use_menu_color; /* use color in menus; only if wc_color */
boolean use_menu_glyphs; /* use object glyphs in menus, if the port supports it */
boolean use_menu_glyphs; /* use object glyphs in menus, if the port
* supports it; controlled by 'menuobjsyms' */
#ifdef STATUS_HILITES
long hilite_delta; /* number of moves to leave a temp hilite lit */
long unhilite_deadline; /* time when oldest temp hilite should be unlit */

View File

@@ -442,9 +442,9 @@ static int optfn_##a(int, int, boolean, char *, char *);
No, Yes, No, No, NoAlias, "jump to the last page in a menu")
NHOPTC(menu_next_page, Advanced, 4, opt_in, set_in_config,
No, Yes, No, No, NoAlias, "go to the next menu page")
NHOPTB(menu_objsyms, Advanced, 0, opt_in, set_in_game,
Off, Yes, No, No, NoAlias, &iflags.menu_head_objsym,
Term_False, "show object symbols in menus")
NHOPTC(menu_objsyms, Advanced, 12, opt_in, set_in_game,
Yes, Yes, No, Yes, "use_menu_glyphs",
"show object symbols in menus")
#ifdef TTY_GRAPHICS
NHOPTB(menu_overlay, Advanced, 0, opt_in, set_in_game,
On, Yes, No, No, NoAlias, &iflags.menu_overlay, Term_False,
@@ -789,9 +789,6 @@ static int optfn_##a(int, int, boolean, char *, char *);
NHOPTB(use_inverse, Advanced, 0, opt_out, set_in_game,
On, Yes, No, No, NoAlias, &iflags.wc_inverse, Term_False,
"display detected monsters in inverse")
NHOPTB(use_menu_glyphs, Advanced, 0, opt_out, set_in_game,
On, Yes, No, No, NoAlias, &iflags.use_menu_glyphs,
Term_False, "show object glyphs in menu items")
NHOPTB(use_truecolor, Advanced, 0, opt_in, set_in_config,
Off, Yes, No, No, "use_truecolour",
&iflags.use_truecolor, Term_False,

View File

@@ -257,6 +257,46 @@ static NEARDATA const char *perminv_modes[][3] = {
/*8*/ { "in-use", "inuse-only", "subset: items currently in use" },
};
struct objsymopt {
int num;
const char *nam;
const char *descr;
};
/*
* menuobjsyms:
* Inventory display for the various values of menuobjsyms.
* 4' and 5' represent !sortpack which lacks headers; they
* produce the same result.
*
* 0: 1:
* Weapons Weapons (')')
* a - 15 darts a - 15 darts
* Armor Armor ('[')
* b - Hawaiian shirt b - Hawaiian shirt
* 2: 3:
* Weapons Weapons (')')
* a ) 15 darts a ) 15 darts
* Armor Armor ('[')
* b [ Hawaiian shirt b [ Hawaiian shirt
* 4: 5:
* Weapons Weapons (')')
* a - 15 darts a - 15 darts
* Armor Armor ('[')
* b - Hawaiian shirt b - Hawaiian shirt
* 4': 5':
* a ) 15 darts a ) 15 darts
* b [ Hawaiian shirt b [ Hawaiian shirt
*/
static const struct objsymopt objsymvals[] = {
{ 0, "none", "don't show object symbols in menus" },
{ 1, "headers", "show object symbols in menu header lines" },
{ 2, "entries", "show object symbols in individual menu entries" },
{ 3, "both", "show object symbols in headers and menu entries" },
{ 4, "conditional", "show objsyms in entries if no headers are shown" },
{ 5, "one-or-other", "show objsyms in header, in entries if no header" },
};
/*
* Default menu manipulation command accelerators. These may _not_ be:
*
@@ -323,6 +363,7 @@ staticfn void rejectoption(const char *);
staticfn char *string_for_opt(char *, boolean);
staticfn char *string_for_env_opt(const char *, char *, boolean);
staticfn void bad_negation(const char *, boolean);
staticfn void set_menuobjsyms_flags(int);
staticfn int change_inv_order(char *);
staticfn boolean warning_opts(char *, const char *);
staticfn int feature_alert_opts(char *, const char *);
@@ -368,6 +409,7 @@ staticfn int handler_align_misc(int);
staticfn int handler_autounlock(int);
staticfn int handler_disclose(void);
staticfn int handler_menu_headings(void);
staticfn int handler_menu_objsyms(void);
staticfn int handler_menustyle(void);
staticfn int handler_msg_window(void);
staticfn int handler_number_pad(void);
@@ -2187,6 +2229,71 @@ optfn_menu_headings(
return optn_ok;
}
staticfn int
optfn_menu_objsyms(
int optidx, int req,
boolean negated,
char *opts, char *op)
{
if (req == do_init) {
/* set iflags.menu_objsyms to 4, "conditional"; also sets
iflags.menu_head_objsym to False and
iflags.use_menu_glyphs True */
set_menuobjsyms_flags(4);
return optn_ok;
}
if (req == do_set) {
unsigned k, l;
int i, osyms;
if (negated) {
/* allow '!menu_objsyms' (and '!use_menu_glyphs') as
'menu_objsyms:none' (0) */
osyms = 0;
} else if (op == empty_optstr) {
/* treat boolean 'menu_objsyms' as 'menu_objsyms:headers' (1)
accept obsolete boolean 'use_menu_glyphs' as a synonym
for 'menu_objsyms:entries' (2) */
osyms = !strncmp(opts, "use_menu_glyphs", 15) ? 2 : 1;
} else if (digit(*op)) {
i = atoi(op);
if (i >= SIZE(objsymvals)) {
config_error_add("Illegal %s parameter '%s'",
allopt[optidx].name, op);
return optn_err;
}
osyms = i;
} else {
/* stilted "one-or-other" is used to compress the menu width */
static const char alt5[] = "one-or-the-other";
unsigned l5 = (unsigned) (sizeof alt5 - sizeof "");
osyms = 0;
k = (unsigned) strlen(op);
for (i = 0; i < SIZE(objsymvals); ++i) {
l = (unsigned) strlen(objsymvals[i].nam);
if (k >= 4)
l = k;
if (!strncmpi(objsymvals[i].nam, op, l)
|| (i == 5 && !strncmpi(alt5, op, l5))) {
osyms = i;
break;
}
}
}
set_menuobjsyms_flags(osyms);
return optn_ok;
}
if (req == get_val || req == get_cnf_val) {
Sprintf(opts, "%s", objsymvals[iflags.menuobjsyms].nam);
return optn_ok;
}
if (req == do_handler) {
return handler_menu_objsyms();
}
return optn_ok;
}
staticfn int
optfn_menuinvertmode(
int optidx, int req,
@@ -5671,6 +5778,43 @@ handler_menu_headings(void)
return optn_ok;
}
staticfn int
handler_menu_objsyms(void)
{
winid tmpwin;
anything any;
char buf[BUFSZ];
menu_item *picklist = (menu_item *) 0;
const char sep = iflags.menu_tab_sep ? '\t' : ' ';
int i, j, n, clr = NO_COLOR;
tmpwin = create_nhwindow(NHW_MENU);
start_menu(tmpwin, MENU_BEHAVE_STANDARD);
any = cg.zeroany;
for (i = 0; i < SIZE(objsymvals); ++i) {
Snprintf(buf, sizeof buf, "%-12.12s%c%.60s",
objsymvals[i].nam, sep, objsymvals[i].descr);
any.a_int = i + 1;
j = objsymvals[i].num;
add_menu(tmpwin, &nul_glyphinfo, &any, '0' + i, *buf,
ATR_NONE, clr, buf,
(j == iflags.menuobjsyms) ? MENU_ITEMFLAGS_SELECTED
: MENU_ITEMFLAGS_NONE);
}
end_menu(tmpwin, "Set object symbols in menus to what?");
n = select_menu(tmpwin, PICK_ONE, &picklist);
if (n > 0) {
i = picklist[0].item.a_int - 1;
/* if there are two picks, use the one that wasn't pre-selected */
if (n > 1 && i == iflags.menuobjsyms)
i = picklist[1].item.a_int - 1;
set_menuobjsyms_flags(i);
free((genericptr_t) picklist);
}
destroy_nhwindow(tmpwin);
return optn_ok;
}
staticfn int
handler_msg_window(void)
{
@@ -7033,6 +7177,7 @@ initoptions_init(void)
flags.pile_limit = PILE_LIMIT_DFLT; /* 5 */
flags.runmode = RUN_LEAP;
iflags.msg_history = 20;
/* msg_window has conflicting defaults for multi-interface binary */
#ifdef TTY_GRAPHICS
iflags.prevmsg_window = 's';
@@ -7310,6 +7455,16 @@ initoptions_finish(void)
*******************************************
*/
/* iflags.menuobjsyms also controls iflags.menu_head_objsym, and
iflags.use_menu_glyphs; they affect execution but are no longer options */
staticfn void
set_menuobjsyms_flags(int newobjsyms)
{
iflags.menuobjsyms = newobjsyms;
iflags.menu_head_objsym = ((newobjsyms & 1) != 0) ? TRUE : FALSE;
iflags.use_menu_glyphs = ((newobjsyms & (2 | 4)) != 0) ? TRUE : FALSE;
}
/*
* Change the inventory order, using the given string as the new order.
* Missing characters in the new order are filled in at the end from

View File

@@ -60,6 +60,7 @@ typedef struct nhm {
unsigned long mbehavior; /* menu flags */
boolean reuse_accels; /* Non-unique accelerators per page */
boolean bottom_heavy; /* display multi-page menu starting at end */
boolean show_obj_syms; /* handle iflags.menuobjsyms */
struct nhm *prev_menu; /* Pointer to previous menu */
struct nhm *next_menu; /* Pointer to next menu */
} nhmenu;
@@ -552,6 +553,7 @@ curses_create_nhmenu(winid wid, unsigned long mbehavior)
new_menu->mbehavior = mbehavior;
new_menu->reuse_accels = FALSE;
new_menu->bottom_heavy = FALSE;
new_menu->show_obj_syms = FALSE;
return;
}
@@ -565,6 +567,7 @@ curses_create_nhmenu(winid wid, unsigned long mbehavior)
new_menu->mbehavior = mbehavior;
new_menu->reuse_accels = FALSE;
new_menu->bottom_heavy = FALSE;
new_menu->show_obj_syms = FALSE;
new_menu->next_menu = NULL;
if (nhmenus == NULL) { /* no menus in memory yet */
@@ -1029,6 +1032,18 @@ menu_win_size(nhmenu *menu)
int maxentrywidth = 0;
int maxheaderwidth = menu->prompt ? (int) strlen(menu->prompt) : 0;
nhmenu_item *menu_item_ptr, *last_item_ptr = NULL;
boolean only_if_no_headers = (iflags.menuobjsyms & 4) != 0;
/* check entire menu rather than one page at a time */
menu->show_obj_syms = iflags.use_menu_glyphs;
if (only_if_no_headers) {
for (menu_item_ptr = menu->entries; menu_item_ptr != NULL;
menu_item_ptr = menu_item_ptr->next_item)
if (menu_item_ptr->identifier.a_void == 0) {
menu->show_obj_syms = FALSE;
break;
}
}
#if 0 /* maxwidth is set below, so the value calculated here isn't used */
if (program_state.gameover) {
@@ -1061,7 +1076,7 @@ menu_win_size(nhmenu *menu)
/* Add space for accelerator (selector letter) */
curentrywidth += 4;
if (menu_item_ptr->glyphinfo.glyph != NO_GLYPH
&& iflags.use_menu_glyphs)
&& menu->show_obj_syms)
curentrywidth += 2;
}
if (curentrywidth > maxentrywidth) {
@@ -1282,10 +1297,11 @@ menu_display_page(
start_col += 4;
}
if (menu_item_ptr->glyphinfo.glyph != NO_GLYPH
&& iflags.use_menu_glyphs) {
&& menu->show_obj_syms) {
color = menu_item_ptr->glyphinfo.gm.sym.color;
curses_toggle_color_attr(win, color, NONE, ON);
mvwaddch(win, menu_item_ptr->line_num + 1, start_col, menu_item_ptr->glyphinfo.ttychar);
mvwaddch(win, menu_item_ptr->line_num + 1, start_col,
menu_item_ptr->glyphinfo.ttychar);
curses_toggle_color_attr(win, color, NONE, OFF);
mvwaddch(win, menu_item_ptr->line_num + 1, start_col + 1, ' ');
entry_cols -= 2;

View File

@@ -1330,7 +1330,8 @@ process_menu_window(winid window, struct WinDesc *cw)
tty_menu_item *page_start, *page_end, *curr;
long count;
int n, attr_n, curr_page, page_lines, resp_len, previous_page_lines;
boolean finished, counting, reset_count;
boolean finished, counting, reset_count, show_obj_syms,
only_if_no_headers = (iflags.menuobjsyms & 4) != 0;
char *cp, *rp, resp[QBUFSZ], gacc[QBUFSZ], *msave, *morestr, really_morc;
#define MENU_EXPLICIT_CHOICE 0x7f /* pseudo menu manipulation char */
@@ -1378,6 +1379,15 @@ process_menu_window(winid window, struct WinDesc *cw)
}
resp_len = 0; /* lint suppression */
show_obj_syms = iflags.use_menu_glyphs;
if (only_if_no_headers) {
for (curr = cw->mlist; curr; curr = curr->next)
if (curr->identifier.a_void == 0) {
show_obj_syms = FALSE;
break;
}
}
/* loop until finished */
while (!finished) {
HUPSKIP();
@@ -1432,7 +1442,7 @@ process_menu_window(winid window, struct WinDesc *cw)
if (curr->str[0] && curr->str[1] == ' '
&& curr->str[2] && strchr("-+#", curr->str[2])
&& curr->str[3] == ' ')
/* [0]=letter, [1]==space, [2]=[-+#], [3]=space */
/* [0]=letter, [1]=space, [2]=[-+#], [3]=space */
attr_n = 4; /* [4:N]=entry description */
/*
@@ -1454,15 +1464,14 @@ process_menu_window(winid window, struct WinDesc *cw)
if (n == attr_n && (color != NO_COLOR
|| attr != ATR_NONE))
toggle_menu_attr(TRUE, color, attr);
if (n == 2
&& curr->identifier.a_void != 0
if (n == 2 && curr->identifier.a_void != 0
&& curr->selected) {
if (curr->count == -1L)
(void) putchar('+'); /* all selected */
else
(void) putchar('#'); /* count selected */
} else if (iflags.use_menu_glyphs && n == 2
&& curr->identifier.a_void != 0
char c = (curr->count == -1L) ? '*' : '#';
/* all selected: '*' vs count selected: '#' */
(void) putchar(c);
} else if (n == 2 && curr->identifier.a_void != 0
&& show_obj_syms
&& curr->glyphinfo.glyph != NO_GLYPH) {
int gcolor = curr->glyphinfo.gm.sym.color;