diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 4b760f787..b60534aad 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1084,6 +1084,7 @@ counting "just picked up" items when deciding what pseudo-classes should be changes to stair internals resulted in summoned Kops blockcading the stairs up rather than intended stairs down dumplog's list of "major events" showed all logged events, not just major ones +pickup via menu ignored player-specified count when picking up gold curses: 'msg_window' option wasn't functional for curses unless the binary also included tty support diff --git a/include/optlist.h b/include/optlist.h index b419909a0..f1a57cc32 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -313,7 +313,7 @@ opt_##a, NHOPTO("menu colors", o_menu_colors, BUFSZ, opt_in, set_in_game, No, Yes, No, NoAlias, "edit menu colors") NHOPTC(menuinvertmode, 5, opt_in, set_in_game, No, Yes, No, No, NoAlias, - "behaviour of menu iverts") + "experimental behaviour of menu inverts") NHOPTC(menustyle, MENUTYPELEN, opt_in, set_in_game, Yes, Yes, No, Yes, NoAlias, "user interface for object selection") NHOPTO("message types", o_message_types, BUFSZ, opt_in, set_in_game, diff --git a/src/invent.c b/src/invent.c index 4231c8dee..1ff07a2d4 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 invent.c $NHDT-Date: 1629409876 2021/08/19 21:51:16 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.339 $ */ +/* NetHack 3.7 invent.c $NHDT-Date: 1647472704 2022/03/16 23:18:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.355 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2674,7 +2674,7 @@ display_pickinv( Sprintf(eos(prompt), " (%s for all)", visctrl(iflags.override_ID)); add_menu(win, &nul_glyphinfo, &any, '_', iflags.override_ID, - ATR_NONE, prompt, MENU_ITEMFLAGS_NONE); + ATR_NONE, prompt, MENU_ITEMFLAGS_SKIPINVERT); gotsomething = TRUE; } } else if (xtra_choice) { diff --git a/src/options.c b/src/options.c index 9ddad7dbd..ee14ea5fa 100644 --- a/src/options.c +++ b/src/options.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 options.c $NHDT-Date: 1645000577 2022/02/16 08:36:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.540 $ */ +/* NetHack 3.7 options.c $NHDT-Date: 1647472681 2022/03/16 23:18:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.542 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2008. */ /* NetHack may be freely redistributed. See license for details. */ @@ -5838,6 +5838,19 @@ initoptions_init(void) /* only used by curses */ iflags.wc2_windowborders = 2; /* 'Auto' */ + /* + * A few menus have certain items (typically operate-on-everything or + * change-subset or sort or help entries) flagged as 'skip-invert' to + * control how whole-page and whole-menu operations affect them. + * 'menuinvertmode' controls how that functions: + * 0: ignore 'skip-invert' flag on menu items (used to be the default); + * 1: don't toggle 'skip-invert' items On for set-all/set-page/invert- + * all/invert-page but do toggle Off if already set (default); + * 2: don't toggle 'skip-invert' items either On of Off for set-all/ + * set-page/unset-all/unset-page/invert-all/invert-page. + */ + iflags.menuinvertmode = 1; + /* since this is done before init_objects(), do partial init here */ objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD; nmcpy(g.pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ); diff --git a/src/windows.c b/src/windows.c index 3ca1aee2a..fe6ca8e9d 100644 --- a/src/windows.c +++ b/src/windows.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 windows.c $NHDT-Date: 1612127121 2021/01/31 21:05:21 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.82 $ */ +/* NetHack 3.7 windows.c $NHDT-Date: 1647472699 2022/03/16 23:18:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.86 $ */ /* Copyright (c) D. Cohrs, 1993. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1493,21 +1493,32 @@ genl_putmixed(winid window, int attr, const char *str) * logic into one place instead of 7 different window-port routines. */ boolean -menuitem_invert_test(int mode, - unsigned itemflags, /* The itemflags for the item */ - boolean is_selected) /* The current selection status - of the item */ +menuitem_invert_test( + int mode UNUSED, /* 0: invert; 1: set; 2: unset */ + unsigned itemflags, /* itemflags for the item */ + boolean is_selected) /* current selection status of the item */ { boolean skipinvert = (itemflags & MENU_ITEMFLAGS_SKIPINVERT) != 0; - if ((iflags.menuinvertmode == 1 || iflags.menuinvertmode == 2) - && !mode && skipinvert && !is_selected) + if (!skipinvert) /* if not flagged SKIPINVERT, always pass test */ + return TRUE; + /* + * mode 0: inverting current on/off state; + * 1: unconditionally setting on; + * 2: unconditionally setting off. + * menuinvertmode 0: treat entries flagged with skipinvert as ordinary + * (same as if not flagged); + * menuinvertmode 1: don't toggle bulk invert or bulk set entries On + * (allow such toggling or setting to change to Off); + * menuinvertmode 2: don't toggle skipinvert entries either On or Off + * when a bulk change is performed. + */ + if (iflags.menuinvertmode == 2) { return FALSE; - else if (iflags.menuinvertmode == 2 - && !mode && skipinvert && is_selected) - return TRUE; - else - return TRUE; + } else if (iflags.menuinvertmode == 1) { + return is_selected ? TRUE : FALSE; + } + return TRUE; } /*windows.c*/ diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 534c3b2c4..594f9985b 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -204,8 +204,8 @@ static void set_item_state(winid, int, tty_menu_item *); static void set_all_on_page(winid, tty_menu_item *, tty_menu_item *); static void unset_all_on_page(winid, tty_menu_item *, tty_menu_item *); static void invert_all_on_page(winid, tty_menu_item *, tty_menu_item *, - char); -static void invert_all(winid, tty_menu_item *, tty_menu_item *, char); + char, long); +static void invert_all(winid, tty_menu_item *, tty_menu_item *, char, long); static void toggle_menu_attr(boolean, int, int); static void process_menu_window(winid, struct WinDesc *); static void process_text_window(winid, struct WinDesc *); @@ -1108,10 +1108,11 @@ reset_role_filtering(void) /* add entries a-Archeologist, b-Barbarian, &c to menu being built in 'win' */ static void -setup_rolemenu(winid win, - boolean filtering, /* True => exclude filtered roles; - False => filter reset */ - int race, int gend, int algn) /* all ROLE_NONE for !filtering case */ +setup_rolemenu( + winid win, + boolean filtering, /* True => exclude filtered roles; + * False => filter reset */ + int race, int gend, int algn) /* all ROLE_NONE for !filtering case */ { anything any; int i; @@ -1685,9 +1686,15 @@ tty_clear_nhwindow(winid window) RESTORE_WARNING_FORMAT_NONLITERAL +/* toggle a specific entry */ static boolean -toggle_menu_curr(winid window, tty_menu_item *curr, int lineno, - boolean in_view, boolean counting, long count) +toggle_menu_curr( + winid window, + tty_menu_item *curr, + int lineno, + boolean in_view, + boolean counting, + long count) { if (curr->selected) { if (counting && count > 0) { @@ -1721,8 +1728,9 @@ toggle_menu_curr(winid window, tty_menu_item *curr, int lineno, } static void -dmore(register struct WinDesc *cw, - const char *s) /* valid responses */ +dmore( + struct WinDesc *cw, + const char *s) /* valid responses */ { const char *prompt = cw->morestr ? cw->morestr : defmorestr; int offset = (cw->type == NHW_TEXT) ? 1 : 2; @@ -1740,8 +1748,13 @@ dmore(register struct WinDesc *cw, xwaitforspace(s); } +/* change screen display for selection state of an item; + not used or wanted for items that aren't shown by the current page */ static void -set_item_state(winid window, int lineno, tty_menu_item *item) +set_item_state( + winid window, + int lineno, + tty_menu_item *item) { char ch = item->selected ? (item->count == -1L ? '+' : '#') : '-'; @@ -1753,88 +1766,118 @@ set_item_state(winid window, int lineno, tty_menu_item *item) term_end_attr(item->attr); } +/* select all [ignores pending count, if any] */ static void -set_all_on_page(winid window, tty_menu_item *page_start, - tty_menu_item *page_end) -{ - tty_menu_item *curr; - int n; - - for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next) - if (curr->identifier.a_void && !curr->selected) { - curr->selected = TRUE; - set_item_state(window, n, curr); - } -} - -static void -unset_all_on_page(winid window, tty_menu_item *page_start, - tty_menu_item *page_end) -{ - tty_menu_item *curr; - int n; - - for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next) - if (curr->identifier.a_void && curr->selected) { - curr->selected = FALSE; - curr->count = -1L; - set_item_state(window, n, curr); - } -} - -static void -invert_all_on_page(winid window, tty_menu_item *page_start, - tty_menu_item *page_end, - char acc) /* group accelerator, 0 => all */ +set_all_on_page( + winid window, + tty_menu_item *page_start, + tty_menu_item *page_end) { tty_menu_item *curr; int n; for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next) { - if (!menuitem_invert_test(0, curr->itemflags, curr->selected)) + if (!curr->identifier.a_void /* not selectable */ + || curr->selected /* already selected */ + || !menuitem_invert_test(1, curr->itemflags, FALSE)) continue; - - if (curr->identifier.a_void && (acc == 0 || curr->gselector == acc)) { - if (curr->selected) { - curr->selected = FALSE; - curr->count = -1L; - } else - curr->selected = TRUE; - set_item_state(window, n, curr); - } + curr->selected = TRUE; + set_item_state(window, n, curr); } } -/* - * Invert all entries that match the give group accelerator (or all if zero). - */ +/* unselect all */ static void -invert_all(winid window, tty_menu_item *page_start, - tty_menu_item *page_end, - char acc) /* group accelerator, 0 => all */ +unset_all_on_page( + winid window, + tty_menu_item *page_start, + tty_menu_item *page_end) +{ + tty_menu_item *curr; + int n; + + for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next) { + if (!curr->identifier.a_void /* skip if not selectable */ + || !curr->selected /* skip if already de-selected */ + || !menuitem_invert_test(2, curr->itemflags, TRUE)) + continue; + curr->selected = FALSE; + curr->count = -1L; + set_item_state(window, n, curr); + } +} + +/* invert current page */ +static void +invert_all_on_page( + winid window, + tty_menu_item *page_start, + tty_menu_item *page_end, + char acc, /* group accelerator, 0 => all */ + long count) /* pending count; -1L for non-group toggling */ +{ + tty_menu_item *curr; + int n; + + for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next) { + if (!curr->identifier.a_void /* not selectable */ + || (acc != 0 && curr->gselector != acc) /* not group 'acc' */ + || !menuitem_invert_test(0, curr->itemflags, curr->selected)) + continue; + + if (curr->selected) { + curr->selected = FALSE; + curr->count = -1L; + } else { + curr->selected = TRUE; + if (count > 0) + curr->count = count; + } + set_item_state(window, n, curr); + } +} + +/* invert all entries that match given group accelerator (or all if zero) */ +static void +invert_all( + winid window, + tty_menu_item *page_start, + tty_menu_item *page_end, + char acc, /* group accelerator, 0 => all */ + long count) /* pending count; -1L for non-group toggling */ { tty_menu_item *curr; boolean on_curr_page; struct WinDesc *cw = wins[window]; - invert_all_on_page(window, page_start, page_end, acc); + /* handle current page separately (it will need screen updating) */ + invert_all_on_page(window, page_start, page_end, acc, count); - /* invert the rest */ + /* invert the rest (no screen updating for them) */ for (on_curr_page = FALSE, curr = cw->mlist; curr; curr = curr->next) { if (curr == page_start) on_curr_page = TRUE; else if (curr == page_end) on_curr_page = FALSE; - if (!on_curr_page && curr->identifier.a_void - && (acc == 0 || curr->gselector == acc)) { - if (menuitem_invert_test(0, curr->itemflags, curr->selected)) { - if (curr->selected) { - curr->selected = FALSE; - curr->count = -1; - } else - curr->selected = TRUE; - } + /* skip if on current page (already handled above) or not + selectable (header line or similar) or if group toggling + is taking place and this item isn't in specified group or + group toggling is not taking place and this item is off + limits to bulk toggling (assumes that an item won't be + both in a group and also subject to bulk restrictions) */ + if (on_curr_page || !curr->identifier.a_void + || (acc != 0 && curr->gselector != acc) + || !menuitem_invert_test(0, curr->itemflags, curr->selected)) + continue; + + if (curr->selected) { + curr->selected = FALSE; + curr->count = -1; + } else { + curr->selected = TRUE; + if (count > 0) + curr->count = count; } } } @@ -1919,7 +1962,7 @@ process_menu_window(winid window, struct WinDesc *cw) HUPSKIP(); if (reset_count) { counting = FALSE; - count = 0; + count = 0L; } else reset_count = TRUE; @@ -2045,7 +2088,7 @@ process_menu_window(winid window, struct WinDesc *cw) xwaitforspace(resp); } - really_morc = morc; /* (only used with MENU_EXPLICIT_CHOICE */ + really_morc = morc; /* (only used with MENU_EXPLICIT_CHOICE) */ if ((rp = index(resp, morc)) != 0 && rp < resp + resp_len) /* explicit menu selection; don't override it if it also happens to match a mapped menu command (such as ':' to @@ -2142,29 +2185,44 @@ process_menu_window(winid window, struct WinDesc *cw) break; case MENU_INVERT_PAGE: if (cw->how == PICK_ANY) - invert_all_on_page(window, page_start, page_end, 0); + invert_all_on_page(window, page_start, page_end, 0, -1L); break; case MENU_SELECT_ALL: if (cw->how == PICK_ANY) { + /* entries on the current page need screen updating */ set_all_on_page(window, page_start, page_end); - /* set the rest */ - for (curr = cw->mlist; curr; curr = curr->next) - if (curr->identifier.a_void && !curr->selected) - curr->selected = TRUE; - } + /* set the rest; entries on current page will be skipped + because all of those will be 'already selected' now + (some won't be if they failed menuitem_invert_test() + in set_all_on_page() but those will fail it again here) */ + for (curr = cw->mlist; curr; curr = curr->next) { + if (!curr->identifier.a_void /* not selectable */ + || curr->selected /* already selected */ + || !menuitem_invert_test(1, curr->itemflags, TRUE)) + continue; + curr->selected = TRUE; + } + } /* if PICK_ANY */ break; case MENU_UNSELECT_ALL: + /* entries on the current page need screen updating */ unset_all_on_page(window, page_start, page_end); - /* unset the rest */ - for (curr = cw->mlist; curr; curr = curr->next) - if (curr->identifier.a_void && curr->selected) { - curr->selected = FALSE; - curr->count = -1; - } + /* unset the rest; entries on current page will be skipped + because none of those will still be in 'selected' state + (unless they failed the menuitem_invert_test() in + unset_all_on_page() but those will fail it again here) */ + for (curr = cw->mlist; curr; curr = curr->next) { + if (!curr->identifier.a_void /* not selectable */ + || !curr->selected /* already de-selected */ + || !menuitem_invert_test(2, curr->itemflags, FALSE)) + continue; + curr->selected = FALSE; + curr->count = -1; + } break; case MENU_INVERT_ALL: if (cw->how == PICK_ANY) - invert_all(window, page_start, page_end, 0); + invert_all(window, page_start, page_end, 0, -1L); break; case MENU_SEARCH: if (cw->how == PICK_NONE) { @@ -2211,7 +2269,8 @@ process_menu_window(winid window, struct WinDesc *cw) group_accel: /* group accelerator; for the PICK_ONE case, we know that it matches exactly one item in order to be in gacc[] */ - invert_all(window, page_start, page_end, morc); + invert_all(window, page_start, page_end, morc, + counting ? count : -1L); if (cw->how == PICK_ONE) finished = TRUE; break;