From 0cd337c78bad8b4bcfc0cbdfa0da5eb129b811d1 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 24 Jun 2023 01:15:44 -0700 Subject: [PATCH] address issue #1065 - Auto-select every item Issue most recently reported by Xdminsy (previously reported by others): it is too easy to accidentally pick choice 'A' in object class selection menus for menustyle:Full. Previous change relevant to this was to exclude choices 'A' and 'a' from being set by '.' (choose all entries) and '~' (toggle all entries). That was an improvement but doesn't help with pressing shift when meaning to type 'a' by those who type faster than they cogitate. This implements a suggestion by janne-hmp: add new choice for the paranoid_confirmation option, 'Autoall' (synonym 'Autoselect-all'). If the player sets this and includes 'A' among the choices for class selection, prompt to confirm whether to honor it. Like confirmation for praying, it adds an extra y/n prompt rather than change an existing y/n prompt into a yes/n or yes/no one. If the player declines, then nothing is selected and the operation is cancelled rather than putting the menu back up to choose again. OPTIONS=paranoid_confirm:autoall requires at least two letters ('au') even if the 'a' is capitalized. paranoid_confirm:a means confirm attacking peaceful monsters. And it should be OPTIONS=paranoid_confirm:autoall pray swim if someone just wants to add autoall to the default paranoid bits. The Guidebook hasn't been updated to describe the new choice since it seems likely that it might undergo adjustments. Closes #1065 --- dat/opthelp | 4 ++++ include/flag.h | 3 +++ src/options.c | 21 ++++++++++++++------- src/pickup.c | 38 ++++++++++++++++++++++++++++++++++---- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/dat/opthelp b/dat/opthelp index f542d9da5..b241ab670 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -223,6 +223,10 @@ paranoid_confirmation space separated list [paranoid_confirmation:pray] Were-change -- yes vs y to confirm changing form due to lycanthropy when hero has polymorph control; pray -- y to confirm an attempt to pray; on by default + swim -- y to confirm an attempt to move into water or lava + when hero can see it and isn't impaired; on by default + AutoAll -- y to confirm if using menustyle:Full and choice 'A' + in the object class filtering menu is selected Remove -- always pick from inventory for 'R' and 'T' even when wearing just one applicable item to remove or take off pickup_burden when you pick up an item that exceeds this encumbrance [S] diff --git a/include/flag.h b/include/flag.h index 87202603d..d4eda3a3d 100644 --- a/include/flag.h +++ b/include/flag.h @@ -85,6 +85,7 @@ struct flag { #define PARANOID_WERECHANGE 0x0100 #define PARANOID_EATING 0x0200 #define PARANOID_SWIM 0x0400 +#define PARANOID_AUTOALL 0x0800 int pickup_burden; /* maximum burden before prompt */ int pile_limit; /* controls feedback when walking over objects */ char discosort; /* order of dodiscovery/doclassdisco output: o,s,c,a */ @@ -481,6 +482,8 @@ enum runmode_types { #define ParanoidEating ((flags.paranoia_bits & PARANOID_EATING) != 0) /* Prevent going into lava or water without explicitly forcing it */ #define ParanoidSwim ((flags.paranoia_bits & PARANOID_SWIM) != 0) +/* Require confirmation for choosing 'A' in class menu for menustyle:Full */ +#define ParanoidAutoAll ((flags.paranoia_bits & PARANOID_AUTOALL) != 0U) /* command parsing, mainly dealing with number_pad handling; not saved and restored */ diff --git a/src/options.c b/src/options.c index a1280f2f6..e94596d7a 100644 --- a/src/options.c +++ b/src/options.c @@ -148,8 +148,9 @@ static const struct paranoia_opts { int synMinLen; const char *explain; /* for interactive menu */ } paranoia[] = { - /* there are some initial-letter conflicts: "a"ttack vs "a"ll, "attack" - takes precedence and "all" isn't present in the interactive menu, + /* there are some initial-letter conflicts: "a"ttack vs "A"utoall vs + "a"ll, "attack" takes precedence and "all" isn't present in the + interactive menu with "Autoall" capitalized there, and "d"ie vs "d"eath, synonyms for each other so doesn't matter; (also "p"ray vs "P"aranoia, "pray" takes precedence since "Paranoia" is just a synonym for "Confirm"); "b"ones vs "br"eak-wand, the @@ -173,12 +174,18 @@ static const struct paranoia_opts { "yes vs y to continue eating after first bite when satiated" }, { PARANOID_WERECHANGE, "Were-change", 2, (const char *) 0, 0, "yes vs y to change form when lycanthropy is controllable" }, + /* extra y/n questions rather than changing y/n to yes/n[o] */ { PARANOID_PRAY, "pray", 1, 0, 0, - "y to pray (supersedes old \"prayconfirm\" option)" }, - { PARANOID_REMOVE, "Remove", 1, "Takeoff", 1, - "always pick from inventory for Remove and Takeoff" }, + "y required to pray (supersedes old \"prayconfirm\" option)" }, { PARANOID_SWIM, "swim", 1, 0, 0, - "avoid walking into lava or water" }, + /* 'm' movement prefix overrides this prompt */ + "y required to deliberately walk into lava or water" }, + { PARANOID_AUTOALL, "Autoall", 2, "autoselect-all", 2, + "y required to pick filter choice 'A' for menustyle:Full" }, + /* not a yes/n[o] vs y/n change nor a y/n addition */ + { PARANOID_REMOVE, "Remove", 1, "Takeoff", 1, + /* normally when there is only 1 candidate it's chosen automatically */ + "always pick from inventory for Remove and Takeoff" }, /* for config file parsing; interactive menu skips these */ { 0, "none", 4, 0, 0, 0 }, /* require full word match */ { ~0, "all", 3, 0, 0, 0 }, /* ditto */ @@ -6613,7 +6620,7 @@ initoptions_init(void) flags.end_own = FALSE; flags.end_top = 3; flags.end_around = 2; - flags.paranoia_bits = PARANOID_PRAY|PARANOID_SWIM; + flags.paranoia_bits = PARANOID_PRAY | PARANOID_SWIM; flags.pile_limit = PILE_LIMIT_DFLT; /* 5 */ flags.runmode = RUN_LEAP; iflags.msg_history = 20; diff --git a/src/pickup.c b/src/pickup.c index 61d0b6bcf..296c9de74 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -115,6 +115,8 @@ collect_obj_classes(char ilets[], struct obj *otmp, boolean here, } /* + * For menustyle:Traditional and menustyle:Combination. + * * Suppose some '?' and '!' objects are present, but '/' objects aren't: * "a" picks all items without further prompting; * "A" steps through all items, asking one by one; @@ -125,11 +127,22 @@ collect_obj_classes(char ilets[], struct obj *otmp, boolean here, * (bug fix: 3.1.0 thru 3.1.3 treated it as "a"); * "?/a" or "a?/" or "/a?",&c picks all '?' even though no '/' * (ie, treated as if it had just been "?a"). + * + * Note: the behavior and meaning of 'a' vs 'A' is effectively reversed + * when using menustyle:Full. For Traditional, the choice is based on + * ease of typing (using 'a' is much more common than 'A'); for Full, + * it was changed to enhance menu entry ordering ('A' stands out, but + * some players complain that it is too easy to choose accidentally). */ static boolean -query_classes(char oclasses[], boolean *one_at_a_time, boolean *everything, - const char *action, struct obj *objs, boolean here, - int *menu_on_demand) +query_classes( + char oclasses[], /* selected classes */ + boolean *one_at_a_time, /* to tell caller that user picked 'A' */ + boolean *everything, /* to tell caller that user picked 'a' */ + const char *action, /* verb for what activity needs objects */ + struct obj *objs, /* invent or container->cobj or level.objects[x][y] */ + boolean here, /* True: traverse by obj->nexthere; False: by obj->nobj */ + int *menu_on_demand) /* to tell caller that user picked 'm' */ { char ilets[36], inbuf[BUFSZ] = DUMMY; /* FIXME: hardcoded ilets[] length */ int iletct, oclassct; @@ -1133,8 +1146,11 @@ query_objlist(const char *qstr, /* query string */ } /* + * For menustyle:Full. + * * allow menu-based category (class) selection (for Drop,take off etc.) * + * If ParanoidAutoAll, requires confirmation when 'A' has been picked. */ int query_category( @@ -1154,7 +1170,7 @@ query_category( 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; + do_uncursed = FALSE, do_buc_unknown = FALSE, verify_All = FALSE; int num_buc_types = 0, num_justpicked = 0, clr = 0; *pick_list = (menu_item *) 0; @@ -1220,7 +1236,9 @@ query_category( (qflags & WORN_TYPES) ? "Auto-select every item being worn" : "Auto-select every relevant item", MENU_ITEMFLAGS_SKIPINVERT); + verify_All = (how == PICK_ANY) && ParanoidAutoAll; + /* blank separator */ any = cg.zeroany; add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, NO_COLOR, "", MENU_ITEMFLAGS_NONE); @@ -1337,6 +1355,18 @@ query_category( } end_menu(win, qstr); n = select_menu(win, how, pick_list); + if (n > 0 && verify_All) { + int i; + + for (i = 0; i < n; ++i) + if (pick_list[i]->item.a_int == 'A') { + if (y_n("Really autoselect All?") != 'y') { + n = 0; + free((genericptr_t) *pick_list); + } + break; /* goto query_done; */ + } + } query_done: destroy_nhwindow(win); if (n < 0)