From 2918026b58a7211393e47c78ec2426028c62f9ba Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 27 Jun 2023 00:48:51 -0700 Subject: [PATCH] redo config file paranoid_confirm:value handling Introduce OPTIONS=paranoid_confirm:+foo bar to add the 'foo' and 'bar' bits to any confirmation bits already set and OPTIONS=paranoid_confirm:-foo bar to clear the 'foo' and 'bar' bits without changing any others that are already set. Values '+foo +bar' or '+foo -bar' or '!foo bar' aren't accepted. OPTIONS=paranoid_confirm:foo bar still replaces existing settings with just the specified bits. OPTIONS=!paranoid_confirm:foo bar is treated like 'paranoid_confirm:-foo bar', although I'm wondering whether that was good idea. Negation combined with plus or minus is rejected. Replace the handling for 'prayconfirm' to use 'paranoid_confirm:+pray' and '!prayconfirm' with 'paranoid_confirm:-pray'. Issue a warning (which is described as "error" but it still works) about it being deprecated when that out of date boolean is used. Reorder swim and AutoAll in the paranoid_confirm menu so that the ones that add a new 'y' question (pray and AutoAll) are together and the two that change game play (swim and Remove) without asking questions are together. (The description of swim in its menu entry now mentions the 'm' prefix and should probably plug in the value instead since it might be bound to some other key.) Undefine the macros defined for option.c's use prior to end of file. --- src/options.c | 272 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 196 insertions(+), 76 deletions(-) diff --git a/src/options.c b/src/options.c index e94596d7a..c13c13a6e 100644 --- a/src/options.c +++ b/src/options.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 options.c $NHDT-Date: 1661218575 2022/08/23 01:36:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.601 $ */ +/* NetHack 3.7 options.c $NHDT-Date: 1687852124 2023/06/27 07:48:44 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.649 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2008. */ /* NetHack may be freely redistributed. See license for details. */ @@ -17,6 +17,7 @@ NEARDATA struct instance_flags iflags; /* provide linkage */ #endif #define BACKWARD_COMPAT +#define COMPLAIN_ABOUT_PRAYCONFIRM /* whether the 'msg_window' option is used to control ^P behavior */ #if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) @@ -76,7 +77,6 @@ static struct allopt_t allopt_init[] = { #define rolestring(val, array, field) \ ((val >= 0) ? array[val].field : (val == ROLE_RANDOM) ? randomrole : none) - enum window_option_types { MESSAGE_OPTION = 1, STATUS_OPTION, @@ -177,12 +177,11 @@ static const struct paranoia_opts { /* extra y/n questions rather than changing y/n to yes/n[o] */ { PARANOID_PRAY, "pray", 1, 0, 0, "y required to pray (supersedes old \"prayconfirm\" option)" }, - { PARANOID_SWIM, "swim", 1, 0, 0, - /* '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_SWIM, "swim", 1, 0, 0, + "'m' prefix necessary to deliberately walk into lava or water" }, { 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" }, @@ -2628,94 +2627,203 @@ optfn_palette( } #endif /* CHANGE_COLOR */ +/* for "paranoid_confirmation:foo" and alias "[!]prayconfirm" */ static int optfn_paranoid_confirmation( int optidx, int req, boolean negated, char *opts, char *op) { int i; + /* + * Player can change required response for some prompts (quit, die, + * attack, save-bones, continue-eating, break-wand, Were-change to + * need to be "yes" instead of just 'y' keystroke to accept. + * + * For paranoid_confirm:Confirm, these prompts also need "no" + * instead of 'n' or or to reject. ( always + * works as a way to reject.) + * + * Player can add an extra prompt (pray, AutoAll) that isn't + * ordinarily there. (They ask for 'y' keystroke unless Confirm is + * also set, then they'll switch to "yes", "no".) + * + * Player can also change game's behavior. paranoid_confirm:swim + * can be used to prevent accidentally stepping into water or lava; + * player must use the 'm' movement prefix to do that intentionally. + * paranoid_confirm:Remove [with synonym parnoid_confirm:Takeoff] + * changes the 'R' and 'T' commands [which have differing criteria + * for "only one candidate item"] to prompt for inventory item to + * remove/takeoff when there is only one candidate, so allows player + * a chance to cancel at the pick-an-item prompt or menu. + */ if (req == do_init) { return optn_ok; } if (req == do_set) { - /* user can change required response for some prompts (quit, die, - hit), or add an extra prompt (pray, Remove) that isn't - ordinarily there */ + char prayconfirm[1 + sizeof "pray"]; + char *pp; + boolean plus_or_minus = FALSE; - if (strncmpi(opts, "prayconfirm", 4) != 0) { /* not prayconfirm */ - /* at present we don't complain about duplicates for this - option, but we do throw away the old settings whenever - we process a new one [clearing old flags is essential - for handling default paranoid_confirm:pray sanely] */ - flags.paranoia_bits = 0; /* clear all */ + /* + * "prayconfirm" used to be a separate boolean option, + * now it is a synonym for paranoid_confirm:+pray and + * "!prayconfirm" has become one for paranoid_confirm:-pray. + */ + if (!strncmpi(opts, "prayconfirm", 4)) { + if (*op) { + /* presence of any value is treated as an error whether + complaining about the 'prayconfirm' deprecation or not; + this will erroneously reject "prayconfirm:true"; too + bad; back when prayconfirm was in active use, tacking on + an explicit value to a boolean option wasn't supported */ + config_error_add( + "deprecated %sprayconfirm option takes no parameters (found '%s')", + negated ? "!" : "", op); + return optn_silenterr; + } +#ifdef COMPLAIN_ABOUT_PRAYCONFIRM + /* config file summary of complaints includes this in the count + of errors; we'd prefer that it be described as a warning but + that isn't supported [not important since this is considered + temporary until 'prayconfirm' gets removed altogether] */ + config_error_add( + "%sprayconfirm option is deprecated; switching to %s:%cpray", + negated ? "!" : "", + allopt[optidx].name, + negated ? '-' : '+'); + /* keep going */ +#endif + /* convert prayconfirm to paranoid_confirm:+pray and + !prayconfirm to paranoid_confirm:-pray */ + Sprintf(prayconfirm, "%cpray", negated ? '-' : '+'); + op = prayconfirm; + /* possibly changing !prayconfirm to paranoid_confirm:-pray + which clears a paranoia bit but isn't a negated option */ + negated = FALSE; + /* + * end of 'prayconfirm' processing + */ + + } else if (!*op) { + /* "paranoid_confirm" without any arguments is disallowed; + "!paranoid_confirm" w/o args is same as paranoid_confirm:none; + "!paranoid_confirm:foo bar" won't get here so handled below */ if (negated) { - flags.paranoia_bits = 0; /* [now redundant...] */ - } else if (op != empty_optstr) { - char *pp, buf[BUFSZ]; - - strncpy(buf, op, sizeof buf - 1); - buf[sizeof buf - 1] = '\0'; - op = mungspaces(buf); - for (;;) { - /* We're looking to parse - "paranoid_confirm:whichone wheretwo whothree" - and "paranoid_confirm:" prefix has already - been stripped off by the time we get here */ - pp = strchr(op, ' '); - if (pp) - *pp = '\0'; - /* we aren't matching option names but match_optname() - does what we want once we've broken the space - delimited aggregate into separate tokens */ - for (i = 0; i < SIZE(paranoia); ++i) { - if (match_optname(op, paranoia[i].argname, - paranoia[i].argMinLen, FALSE) - || (paranoia[i].synonym - && match_optname(op, paranoia[i].synonym, - paranoia[i].synMinLen, - FALSE))) { - if (paranoia[i].flagmask) - flags.paranoia_bits |= paranoia[i].flagmask; - else /* 0 == "none", so clear all */ - flags.paranoia_bits = 0; - break; - } - } - if (i == SIZE(paranoia)) { - /* didn't match anything, so arg is bad; - any flags already set will stay set */ - config_error_add("Unknown %s parameter '%s'", - allopt[optidx].name, op); - return optn_err; - } - /* move on to next token */ - if (pp) - op = pp + 1; - else - break; /* no next token */ - } /* for(;;) */ - } else - return optn_err; - return optn_ok; - } else { /* prayconfirm */ - if (negated) - flags.paranoia_bits &= ~PARANOID_PRAY; - else - flags.paranoia_bits |= PARANOID_PRAY; + flags.paranoia_bits = 0; + return optn_ok; + } + return optn_err; } + + /* + * Multiple settings for paranoid_confirmation are allowed. + * When a new instance is processed, the behavior depends on the + * first character of its value: + * + * paranoid_confirm:foo bar + * clears all confirmation bits (from previous settings, including + * default), then sets the bits for foo and bar; + * + * paranoid_confirm:+foo bar + * existing bits are kept, plus those for foo and bar are set; + * + * paranoid_confirm:-foo bar + * existing bits are kept except those for foo and bar get cleared; + * + * !paranoid_confirm:foo bar + * is treated as if it was paranoid_confirm:-foo bar, probably + * ought to become deprecated. !paranoid_confirm:none is + * nonsensical and yields same result as paranoid_confirm:none + * and !paranoid_confirm:all. + * + * paranoid_confirm:+all is the same as paranoid_confirm:all; + * paranoid_confirm:-all is the same as paranoid_confirm:none; + * paranoid_confirm:+none and paranoid_confirm:-none are no-ops; + * !paranoid_confirm:[+|-]anything are disallowed. + */ + (void) mungspaces(op); + if (*op != '+' && *op != '-') { + /* new value; clear all bits unless negated */ + if (!negated) + flags.paranoia_bits = 0; + } else if (negated) { + /* first char is '+' or '-'; disallowed for !paranoid_confirm */ + config_error_add("invalid !%s parameter '%s'", + allopt[optidx].name, op); + return optn_silenterr; + } else { + /* augmenting existing value; keep old bits */ + plus_or_minus = TRUE; /* only used for "+none" and "-none" */ + negated = (*op == '-'); /* context is changed */ + ++op; /* skip '+' or '-' and possible whitespace after it */ + if (*op == ' ') + ++op; + } + + for (;;) { + /* We're looking to parse + "paranoid_confirm:whichone wheretwo whothree" + and "paranoid_confirm:" prefix has already + been stripped off by the time we get here */ + pp = strchr(op, ' '); + if (pp) + *pp = '\0'; + /* we aren't matching option names but match_optname() + does what we want once we've broken the space + delimited aggregate into separate tokens */ + for (i = 0; i < SIZE(paranoia); ++i) { + if (match_optname(op, paranoia[i].argname, + paranoia[i].argMinLen, FALSE) + || (paranoia[i].synonym + && match_optname(op, paranoia[i].synonym, + paranoia[i].synMinLen, FALSE))) { + if (!paranoia[i].flagmask) { + /* flagmask==0 is "none", clear all bits + but "+none" and "-none" are no-ops */ + if (!plus_or_minus) + flags.paranoia_bits = 0; /* clear all */ + } else if (negated) { + flags.paranoia_bits &= ~paranoia[i].flagmask; + } else { + flags.paranoia_bits |= paranoia[i].flagmask; + } + break; + } + } + if (i == SIZE(paranoia)) { + /* didn't match anything, so arg is bad; + any flags already modified will stay modified */ + config_error_add("Unknown %s parameter '%s'", + allopt[optidx].name, op); + return optn_silenterr; + } + /* move on to next token */ + if (pp) + op = pp + 1; + else + break; /* no next token */ + } /* for(;;) */ return optn_ok; } if (req == get_val || req == get_cnf_val) { - char tmpbuf[QBUFSZ]; + char tmpbuf[BUFSZ]; if (!opts) return optn_err; tmpbuf[0] = '\0'; - for (i = 0; paranoia[i].flagmask != 0; ++i) - if (flags.paranoia_bits & paranoia[i].flagmask) - Sprintf(eos(tmpbuf), " %s", paranoia[i].argname); - Strcpy(opts, tmpbuf[0] ? &tmpbuf[1] : "none"); + for (i = 0; paranoia[i].flagmask != 0; ++i) { + if ((flags.paranoia_bits & paranoia[i].flagmask) != 0 + /* hide paranoid_confirm:bones during play except for wizard + mode; keep it for any mode if rewriting the config file */ + && (paranoia[i].flagmask != PARANOID_BONES + || wizard || req == get_cnf_val)) + Snprintf(eos(tmpbuf), sizeof tmpbuf - strlen(tmpbuf), + " %s", paranoia[i].argname); + } + /* note: always leaves enough room for caller to tack on '\n' */ + opts[0] = '\0'; + (void) strncat(opts, tmpbuf[0] ? &tmpbuf[1] : "none", BUFSZ - 1); return optn_ok; } if (req == do_handler) { @@ -4440,7 +4548,10 @@ optfn_windowborders( #ifdef WINCHAIN static int -optfn_windowchain(int optidx, int req, boolean negated UNUSED, char *opts, char *op) +optfn_windowchain( + int optidx, int req, + boolean negated UNUSED, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -5677,7 +5788,8 @@ handler_whatis_coord(void) "screen: row is offset to accommodate tty interface's use of top line", MENU_ITEMFLAGS_NONE); #if COLNO == 80 -#define COL80ARG Verbose(2, handler_whatis_coord2) ? "; column 80 is not used" : "" +#define COL80ARG \ + (Verbose(2, handler_whatis_coord2) ? "; column 80 is not used" : "") #else #define COL80ARG "" #endif @@ -10025,7 +10137,7 @@ wc_set_window_colors(char *op) return 1; } -/* set up for wizard mode if player or save file has requested to it; +/* set up for wizard mode if player or save file has requested it; called from port-specific startup code to handle `nethack -D' or OPTIONS=playmode:debug, or from dorecover()'s restgamestate() if restoring a game which was saved in wizard mode */ @@ -10043,7 +10155,8 @@ set_playmode(void) } /* don't need to do anything special for explore mode or normal play */ } -void + +static void enhance_menu_text( char *buf, size_t sz, @@ -10071,7 +10184,14 @@ enhance_menu_text( return; } +#undef OPTIONS_HEADING +#undef CONFIG_SLOT #endif /* OPTION_LISTS_ONLY */ +#undef BACKWARD_COMPAT +#undef COMPLAIN_ABOUT_PRAYCONFIRM +#undef PREV_MSGS +#undef PILE_LIMIT_DFLT + /*options.c*/