From 1cfa9668713c3a6eb961d7270b2e2ec3445ba3dc Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 30 Dec 2023 16:33:27 -0800 Subject: [PATCH] pull request #1143 - menustyle:Full 'A' choice Pull request from entrez: in the class filtering menu for multi-drop and for loot in-or-out of a container, make choosing 'A' without any other filter choices (such as all, specific class(es), cursed, unpaid, just-picked-up, &c) become a no-op. I started with the pull request and then undid much of it. It would have been simpler to start from scratch. If you don't have option paranoid_confirmation:AutoAll set, when choosing 'A' for all-without- prompting as the only selection, operate as the PR has made things work: effectively, 'A' by itself is ignored and the operation ends with nothing happening. However, if you do have paranoid_confirm:A set, then continue treating 'A' by itself as if 'A'+'a': everything. Since paranoid confirmation is specified, that will be followed by a confirmation prompt. This also adds a context-sensitive hint to the menu about how the 'A' entry works, shown every time when 'cmdassist' is On or just once (per session) when that is Off. The documentation probably needs some updating. Closes #1143 --- include/decl.h | 2 + src/decl.c | 2 + src/do.c | 21 ++++++---- src/pickup.c | 106 ++++++++++++++++++++++++++++++++++--------------- 4 files changed, 93 insertions(+), 38 deletions(-) diff --git a/include/decl.h b/include/decl.h index b69a9c43d..2ca3cb705 100644 --- a/include/decl.h +++ b/include/decl.h @@ -147,6 +147,8 @@ struct instance_globals_a { int animal_list_count; /* pickup.c */ + int A_first_hint; /* menustyle:Full plus 'A' response + !paranoid:A */ + int A_second_hint; /* menustyle:Full plus 'A' response + paranoid:A */ boolean abort_looting; /* shk.c */ diff --git a/src/decl.c b/src/decl.c index 1eb3ece64..ae70739c5 100644 --- a/src/decl.c +++ b/src/decl.c @@ -205,6 +205,8 @@ const struct instance_globals_a g_init_a = { UNDEFINED_PTR, /* animal_list */ UNDEFINED_VALUE, /* animal_list_count */ /* pickup.c */ + 0, /* A_first_hint */ + 0, /* A_second_hint */ UNDEFINED_VALUE, /* abort_looting */ /* shk.c */ FALSE, /* auto_credit */ diff --git a/src/do.c b/src/do.c index 9a998309c..517f812dc 100644 --- a/src/do.c +++ b/src/do.c @@ -955,7 +955,7 @@ menu_drop(int retry) int n, i, n_dropped = 0; struct obj *otmp, *otmp2; menu_item *pick_list; - boolean all_categories = TRUE, autopick = FALSE; + boolean all_categories = TRUE, drop_everything = FALSE, autopick = FALSE; boolean drop_justpicked = FALSE; long justpicked_quan = 0; @@ -968,19 +968,26 @@ menu_drop(int retry) | BUC_BLESSED | BUC_CURSED | BUC_UNCURSED | BUC_UNKNOWN | JUSTPICKED | INCLUDE_VENOM), &pick_list, PICK_ANY); - if (!n || (n == 1 && pick_list[0].item.a_int == 'A')) + /* when paranoid_confirm:A is set, 'A' by itself implies + 'A'+'a' which will be followed by a confirmation prompt; + when that option isn't set, 'A' by itself is rejected + by query_categorry() and result here will be n==0 */ + if (!n) goto drop_done; /* no non-autopick category filters specified */ + for (i = 0; i < n; i++) { if (pick_list[i].item.a_int == ALL_TYPES_SELECTED) { all_categories = TRUE; } else if (pick_list[i].item.a_int == 'A') { - autopick = TRUE; + drop_everything = autopick = TRUE; } else if (pick_list[i].item.a_int == 'P') { justpicked_quan = max(0, pick_list[i].count); drop_justpicked = TRUE; + drop_everything = FALSE; add_valid_menu_class(pick_list[i].item.a_int); } else { add_valid_menu_class(pick_list[i].item.a_int); + drop_everything = FALSE; } } free((genericptr_t) pick_list); @@ -992,7 +999,7 @@ menu_drop(int retry) i = ggetobj("drop", drop, 0, TRUE, &ggoresults); if (i == -2) all_categories = TRUE; - if (ggoresults & ALL_FINISHED) { + if ((ggoresults & ALL_FINISHED) != 0) { n_dropped = i; goto drop_done; } @@ -1019,7 +1026,7 @@ menu_drop(int retry) */ bypass_objlist(gi.invent, FALSE); /* clear bypass bit for invent */ while ((otmp = nxt_unbypassed_obj(gi.invent)) != 0) { - if (all_categories || allow_category(otmp)) + if (drop_everything || all_categories || allow_category(otmp)) n_dropped += ((drop(otmp) & ECMD_TIME) != 0) ? 1 : 0; } /* we might not have dropped everything (worn armor, welded weapon, @@ -1029,8 +1036,8 @@ menu_drop(int retry) /* drop the just picked item automatically, if only one stack */ otmp = find_justpicked(gi.invent); if (otmp) - n_dropped += ((menudrop_split(otmp, justpicked_quan) & ECMD_TIME) - != 0) ? 1 : 0; + n_dropped += ((menudrop_split(otmp, justpicked_quan) + & ECMD_TIME) != 0) ? 1 : 0; } else { /* should coordinate with perm invent, maybe not show worn items */ n = query_objlist("What would you like to drop?", &gi.invent, diff --git a/src/pickup.c b/src/pickup.c index 79c84554d..4179758dd 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -504,9 +504,10 @@ allow_all(struct obj *obj UNUSED) boolean allow_category(struct obj *obj) { - /* If no filters are active, nothing will match. */ + /* If no filters are active, nothing will match unless + paranoid_confirm:A is set. */ if (!gc.class_filter && !gs.shop_filter && !gb.bucx_filter - && !gp.picked_filter) + && !gp.picked_filter && !ParanoidAutoAll) return FALSE; /* For coins, if any class filter is specified, accept if coins @@ -1216,41 +1217,54 @@ query_category( char invlet; int ccount; boolean (*ofilter)(OBJ_P) = (boolean (*)(OBJ_P)) 0; - boolean do_unpaid = FALSE, do_blessed = FALSE, do_cursed = FALSE, - do_uncursed = FALSE, do_buc_unknown = FALSE, verify_All = FALSE; + boolean show_a, + do_unpaid = FALSE, do_usedup = FALSE, + do_blessed = FALSE, do_cursed = FALSE, + do_uncursed = FALSE, do_buc_unknown = FALSE, + do_worn = FALSE, verify_All = FALSE; int num_buc_types = 0, num_justpicked = 0, clr = NO_COLOR; *pick_list = (menu_item *) 0; if (!olist) return 0; - if ((qflags & UNPAID_TYPES) && count_unpaid(olist)) + if ((qflags & UNPAID_TYPES) != 0 && count_unpaid(olist)) do_unpaid = TRUE; - if (qflags & WORN_TYPES) + /* caller only passes BILLED_TYPES when there are some used up items + on shop's bill */ + if ((qflags & BILLED_TYPES) != 0) + do_usedup = TRUE; + /* for the 'A' command to remove worn/wielded */ + if ((qflags & WORN_TYPES) != 0) { + do_worn = TRUE; ofilter = is_worn; - if ((qflags & BUC_BLESSED) && count_buc(olist, BUC_BLESSED, ofilter)) { + } + if ((qflags & BUC_BLESSED) != 0 + && count_buc(olist, BUC_BLESSED, ofilter)) { do_blessed = TRUE; num_buc_types++; } - if ((qflags & BUC_CURSED) && count_buc(olist, BUC_CURSED, ofilter)) { + if ((qflags & BUC_CURSED) != 0 + && count_buc(olist, BUC_CURSED, ofilter)) { do_cursed = TRUE; num_buc_types++; } - if ((qflags & BUC_UNCURSED) && count_buc(olist, BUC_UNCURSED, ofilter)) { + if ((qflags & BUC_UNCURSED) != 0 + && count_buc(olist, BUC_UNCURSED, ofilter)) { do_uncursed = TRUE; num_buc_types++; } - if ((qflags & BUC_UNKNOWN) && count_buc(olist, BUC_UNKNOWN, ofilter)) { + if ((qflags & BUC_UNKNOWN) != 0 + && count_buc(olist, BUC_UNKNOWN, ofilter)) { do_buc_unknown = TRUE; num_buc_types++; } - if (qflags & JUSTPICKED) { + if ((qflags & JUSTPICKED) != 0) { num_justpicked = count_justpicked(olist); } ccount = count_categories(olist, qflags); /* no point in actually showing a menu for a single category */ - if (ccount == 1 && !do_unpaid && num_buc_types <= 1 - && !(qflags & BILLED_TYPES)) { + if (ccount == 1 && !do_unpaid && !do_usedup && num_buc_types <= 1) { for (curr = olist; curr; curr = FOLLOW(curr, qflags)) { if (ofilter && !(*ofilter)(curr)) continue; @@ -1272,29 +1286,41 @@ query_category( start_menu(win, MENU_BEHAVE_STANDARD); pack = strcpy(packbuf, flags.inv_order); - if (qflags & INCLUDE_VENOM) + if ((qflags & INCLUDE_VENOM) != 0) (void) strkitten(pack, VENOM_CLASS); /* venom is not in inv_order */ - if (qflags & CHOOSE_ALL) { + show_a = ((qflags & ALL_TYPES) != 0 && ccount > 1); + + if ((qflags & CHOOSE_ALL) != 0) { invlet = 'A'; any = cg.zeroany; any.a_int = 'A'; add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE, clr, - (qflags & WORN_TYPES) ? "Auto-select every item being worn" - : "Auto-select every relevant item", + /* note: menu_remarm() doesn't pass the CHOOSE_ALL flag, + so do_worn handling here is moot */ + do_worn ? "Auto-select every item being worn or wielded" + : "Auto-select every relevant item", MENU_ITEMFLAGS_SKIPINVERT); verify_All = (how == PICK_ANY) && ParanoidAutoAll; - + if (!verify_All) { + if (!ga.A_first_hint++ || iflags.cmdassist) + add_menu_str(win, + " (ignored unless some other choices are also picked)"); + } else if (show_a) { + if (!ga.A_second_hint++ || iflags.cmdassist) + add_menu_str(win, + " (if no other choices are picked, 'a' is implied)"); + } /* blank separator */ add_menu_str(win, ""); } invlet = 'a'; - if ((qflags & ALL_TYPES) && (ccount > 1)) { + if (show_a) { any = cg.zeroany; any.a_int = ALL_TYPES_SELECTED; add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE, clr, - (qflags & WORN_TYPES) ? "All worn types" : "All types", + do_worn ? "All worn and wielded types" : "All types", MENU_ITEMFLAGS_SKIPINVERT); ++invlet; /* invlet = 'b'; */ } @@ -1328,8 +1354,9 @@ query_category( } } while (*pack); - if (do_unpaid || (qflags & BILLED_TYPES) || do_blessed || do_cursed - || do_uncursed || do_buc_unknown || num_justpicked) { + if (do_unpaid || do_usedup + || do_blessed || do_cursed || do_uncursed || do_buc_unknown + || num_justpicked) { add_menu_str(win, ""); } @@ -1342,7 +1369,7 @@ query_category( ATR_NONE, clr, "Unpaid items", MENU_ITEMFLAGS_SKIPINVERT); } /* billed items: checked by caller, so always include if BILLED_TYPES */ - if (qflags & BILLED_TYPES) { + if (do_usedup) { invlet = 'x'; any = cg.zeroany; any.a_int = 'x'; @@ -1398,6 +1425,9 @@ query_category( } end_menu(win, qstr); n = select_menu(win, how, pick_list); + if (n > 0) { + assert(*pick_list != NULL); + } /* handle ParanoidAutoAll by confirming 'A' choice if present */ if (n > 0 && verify_All) { @@ -1439,10 +1469,17 @@ query_category( } break; /* from for => goto query_done; */ } + } else if (n == 1 && !verify_All && (*pick_list)[0].item.a_int == 'A') { + /* without paranoid_confirm:A, choosing 'A' by itself is rejected */ + n = 0; + free((genericptr_t) *pick_list), *pick_list = 0; + /* the menu entry description is "Auto-select every relevant item" + [not sure whether issuing a message here is a good idea...] */ + pline("No relevant items selected."); } query_done: destroy_nhwindow(win); - if (n < 0) + if (n < 0) /* closed menu with ESC */ n = 0; /* callers don't expect -1 */ return n; } @@ -1454,15 +1491,15 @@ count_categories(struct obj *olist, int qflags) boolean counted_category; int ccount = 0; struct obj *curr; + boolean do_worn = (qflags & WORN_TYPES) != 0; pack = flags.inv_order; do { counted_category = FALSE; for (curr = olist; curr; curr = FOLLOW(curr, qflags)) { if (curr->oclass == *pack) { - if ((qflags & WORN_TYPES) - && !(curr->owornmask & (W_ARMOR | W_ACCESSORY - | W_WEAPONS))) + if (do_worn && !(curr->owornmask + & (W_ARMOR | W_ACCESSORY | W_WEAPONS))) continue; if (!counted_category) { ccount++; @@ -3171,7 +3208,7 @@ static int menu_loot(int retry, boolean put_in) { int n, i, n_looted = 0; - boolean all_categories = TRUE, autopick = FALSE; + boolean all_categories = TRUE, loot_everything = FALSE, autopick = FALSE; char buf[BUFSZ]; boolean loot_justpicked = FALSE; const char *action = put_in ? "Put in" : "Take out"; @@ -3192,19 +3229,26 @@ menu_loot(int retry, boolean put_in) n = query_category(buf, put_in ? gi.invent : gc.current_container->cobj, mflags, &pick_list, PICK_ANY); - if (!n || (n == 1 && pick_list[0].item.a_int == 'A')) + /* when paranoid_confirm:A is set, 'A' by itself implies + 'A'+'a' which will be followed by a confirmation prompt; + when that option isn't set, 'A' by itself is rejected + by query_categorry() and result here will be n==0 */ + if (!n) return ECMD_OK; /* no non-autopick category filters specified */ + for (i = 0; i < n; i++) { if (pick_list[i].item.a_int == 'A') { - autopick = TRUE; + loot_everything = autopick = TRUE; } else if (put_in && pick_list[i].item.a_int == 'P') { loot_justpicked = TRUE; count = max(0, pick_list[i].count); add_valid_menu_class(pick_list[i].item.a_int); + loot_everything = FALSE; } else if (pick_list[i].item.a_int == ALL_TYPES_SELECTED) { all_categories = TRUE; } else { add_valid_menu_class(pick_list[i].item.a_int); + loot_everything = FALSE; } } free((genericptr_t) pick_list); @@ -3231,7 +3275,7 @@ menu_loot(int retry, boolean put_in) */ for (otmp = firstobj; otmp && gc.current_container; otmp = otmp2) { otmp2 = otmp->nobj; - if (all_categories || allow_category(otmp)) { + if (loot_everything || all_categories || allow_category(otmp)) { res = (*inout_func)(otmp); if (res < 0) break;