Skip to content

NetHackW.exe and selecting count of gold #805

Closed

Description

@qazmlpok

Submitting this issue based on a stack exchange post: https://gaming.stackexchange.com/questions/397778/how-do-you-take-only-some-gold-out-of-a-sack-in-nethack-with-numpad-movement-on

In NetHackW.exe, using $ to select gold from either the D Drop Objects command or the container interface screen does not allow entering a count, like it does with normal objects, or like in the non-gui interface. The work around is to explicitly select gold from the list, either with the mouse or the arrow keys.

I took a brief look at the code, however I'm not familiar with NetHackW or how the menus work. I think the issue is that $ is a group accelerator as well as a standard accelerator, and group accelerators don't allow counts. I don't think $ needs to be a group accelerator, because there should only ever be one item, but I have no idea if this would have other effects.

Activity

  1. entrez commented on Jun 18, 2022

    @entrez
    Contributor

    I'm not sure, because I don't have access to a Windows machine and I haven't dug into the code that carefully, but it looks like onListChar(mhmenu.c) may contain an implicit assumption that all invlets will be alphabetical (which would mean '$' doesn't work as an invlet):

    NetHack/win/win32/mhmenu.c

    Lines 1512 to 1514 in 304888c

    if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
    || is_accelerator) {
    if (data->how == PICK_ANY || data->how == PICK_ONE) {

    There doesn't seem to be such an assumption for group accelerators, but it looks as though when a group accelerator is used in the MSWin windowport the count is ignored/reset (group accelerators accept counts in TTY afaik, but I guess not in this windowport):

    NetHack/win/win32/mhmenu.c

    Lines 1463 to 1469 in 304888c

    accelerator:
    default:
    if (strchr(data->menui.menu.gacc, ch)
    && !(ch == '0' && data->menui.menu.counting)) {
    /* matched a group accelerator */
    if (data->how == PICK_ANY || data->how == PICK_ONE) {
    reset_menu_count(hwndList, data);

    The reset_menu_count function that's called for a group accelerator zeroes out data->menui.menu.counting:

    NetHack/win/win32/mhmenu.c

    Lines 1605 to 1615 in 304888c

    void
    reset_menu_count(HWND hwndList, PNHMenuWindow data)
    {
    int i;
    data->menui.menu.counting = FALSE;
    if (IsWindow(hwndList)) {
    i = ListView_GetNextItem((hwndList), -1, LVNI_FOCUSED);
    if (i >= 0)
    ListView_RedrawItems(hwndList, i, i);
    }
    }

    I think the combination of the two things causes the problem.

    By the way, the use of '$' as a group accelerator in TTY allows you to select it even when it's offscreen (due to some special handling added in efea46a that allows the group accelerator to take precedence over the invlet), so the dual use of '$' isn't pointless. If NetHackW doesn't have paginated menus maybe that wouldn't provide any benefit, though. :)

  2. pat-rankin commented on Jun 18, 2022

    @pat-rankin

    I don't think $ needs to be a group accelerator, because there should
    only ever be one item, but I have no idea if this would have other effects.

    For tty (also curses), menus are paginated. Regular selector characters
    only work on the current page, but group accelerators ignore page
    boundaries and affect the entire menu.

    Treating $ as both the selector 'letter' for gold pieces and as the group
    accelerator for coins was done so that gold can be chosen regardless
    of what page you're looking at, like group picks of other object classes.

    The development code has been changed for tty (also curses, not sure
    offhand about any of the other interfaces) to honor a pending count
    whenever any group accelerator matches a single item. I think that that
    was done primarily to benefit '$'.

    Maintenance for the Windows GUI interface is lagging and I don't think
    it has had any updates--except for untested attempts to match some
    changes that other interfaces have undergone--in quite a while.

  3. ostrosablin commented on Jun 18, 2022

    @ostrosablin

    @pat-rankin Maybe it's reasonable to allow selector characters to work from any page, too? (Perhaps, on opt-in basis via a new option?). In xNetHack, curses interface allows to select characters from any page and it's IMO a great idea, since with force_invmenu you may already remember the character you want, and if it's not on current page - you're forced to switch pages. This would also allow to get rid of $ as group accelerator.

  4. entrez commented on Jun 18, 2022

    @entrez
    Contributor

    In xNetHack, curses interface allows to select characters from any page and it's IMO a great idea, since with force_invmenu you may already remember the character you want, and if it's not on current page - you're forced to switch pages.

    I'm pretty sure this is behavior from the curses windowport in vanilla NetHack too, not unique to xNetHack -- if you enter an invlet from a different page it scrolls over to that page and selects it.

  5. ostrosablin commented on Jun 18, 2022

    @ostrosablin

    Oh, I see. Well, it's still relevant for tty windowport because it doesn't allow to pick items from different pages at all and arguably, it's even more important in tty (because if game is played in low-height terminal, say 80x24 - menu paging is much more harsher than in large terminal with curses, often reaching 3 pages).

  6. nhmall commented on Jul 31, 2026

    @nhmall
    Contributor

    Does this give you the behaviour that you're seeking, @qazmlpok ?

    diff --git a/win/win32/mhmenu.c b/win/win32/mhmenu.c
    index 908284e16..7579976a6 100644
    --- a/win/win32/mhmenu.c
    +++ b/win/win32/mhmenu.c
    @@ -35,6 +35,7 @@ typedef struct mswin_menu_item {
         int color;
         char str[NHMENU_STR_SIZE];
         boolean presel;
    +    boolean group_accelerator_collision;
         unsigned int itemflags;
         int count;
         BOOL has_focus;
    @@ -191,7 +192,18 @@ mswin_menu_window_select_menu(HWND hWnd, int how, MENU_ITEM_P **_selected,
             /* collect group accelerators */
             for (i = 0; i < data->menui.menu.size; i++) {
                 if (data->how != PICK_NONE) {
    -                if (data->menui.menu.items[i].group_accel
    +                /* Check for conflict between a specific selectable
    +                 * menu item and its group accelerator.
    +                 */
    +                if (NHMENU_IS_SELECTABLE(data->menui.menu.items[i])
    +                    && data->menui.menu.items[i].group_accel
    +                           == data->menui.menu.items[i].accelerator) {
    +                    /* collision between specific menu item
    +                       and its group accelerator */
    +                    data->menui.menu.items[i].group_accelerator_collision = TRUE;
    +                }
    +                if (!data->menui.menu.items[i].group_accelerator_collision
    +                    && data->menui.menu.items[i].group_accel
                         && !strchr(data->menui.menu.gacc,
                                    data->menui.menu.items[i].group_accel)) {
                         *ap++ = data->menui.menu.items[i].group_accel;
    @@ -651,6 +663,7 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
                 /* prevent & being interpreted as a mnemonic start */
                 strNsubst(data->menui.menu.items[new_item].str, "&", "&&", 0);
                 data->menui.menu.items[new_item].presel = msg_data->presel;
    +            data->menui.menu.items[new_item].group_accelerator_collision = FALSE;
                 data->menui.menu.items[new_item].itemflags = msg_data->itemflags;
     
                 /* calculate tabstop size */
    
  7. self-assigned this
    on Jul 31, 2026
  8. added a commit that references this issue on Aug 2, 2026
    29cb8b7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions