diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index aab1169f9..b69d9daf1 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -2140,6 +2140,20 @@ lets you refine how it behaves. Here are the valid prefixes: .CC - "do not disclose it and do not prompt." .ei .ed +The listing of vanquished monsters can be sorted, +so there are two additional choices for `v': +.sd +.si +.CC ? "prompt you and default to ask on the prompt;" +.CC # "disclose it without prompting, ask for sort order." +.ei +.ed +Asking refers to picking one of the orderings from a menu. +The `+' disclose without prompting choice, +or being prompted and answering `y' rather than `a', +will default to showing monsters in the traditional order, +from high level to low level. +.lp "" Omitted categories are implicitly added with `n' prefix. Specified categories with omitted prefix implicitly use `+' prefix. Order of the disclosure categories does not matter, program display for diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 8f2e601b4..6f667bbfd 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -636,7 +636,7 @@ will give a brief reminder about how it works. If the {\it autodescribe\/} option is on, a short description of what you see at each location is -shown as you move the cursor. Typing `{\tt #}' while picking a location will +shown as you move the cursor. Typing `{\tt \#}' while picking a location will toggle that option on or off. The {\it whatis\verb+_+coord\/} @@ -953,10 +953,11 @@ the exchange still takes place. \item[\tb{X}] Toggle two-weapon combat, if your character can do it. Also available via the ``{\tt \#twoweapon}'' extended command.\\ -+.lp "" -+(In versions prior to 3.6 this was the command to switch from normal -+play to ``explore mode'', also known as ``discovery mode'', which has now -+been moved to ``{\tt \#explore}''.) +%.lp "" + +(In versions prior to 3.6 this was the command to switch from normal +play to ``explore mode'', also known as ``discovery mode'', which has now +been moved to ``{\tt \#explore}''.) %.lp \item[\tb{\^{}X}] Display basic information about your character.\\ @@ -2569,6 +2570,22 @@ lets you refine how it behaves. Here are the valid prefixes: %.ei %.ed +The listing of vanquished monsters can be sorted, +so there are two additional choices for `{\tt v}': +%.sd +%.si +{\tt ?} --- prompt you and default to ask on the prompt;\\ +{\tt\#} --- disclose it without prompting, ask for sort order. +%.ei +%.ed + +Asking refers to picking one of the orderings from a menu. +The `{\tt +}' disclose without prompting choice, +or being prompted and answering `{\tt y}' rather than `{\tt a}', +will default to showing monsters in the traditional order, +from high level to low level. +.lp "" + %.lp "" (ex.\ ``{\tt disclose:yi na +v -g o}'') The example sets @@ -3083,15 +3100,15 @@ The possibile settings are: %.sd %.si -{\tt c} --- \verb#compass ('east' or '3s' or '2n,4w')#; -{\tt m} --- \verb#map (map column x=0 is not used)#; -{\tt s} --- \verb#screen [row,column] (row is offset to match tty usage)#; +{\tt c} --- \verb#compass ('east' or '3s' or '2n,4w')#;\\ +{\tt m} --- \verb#map (map column x=0 is not used)#;\\ +{\tt s} --- \verb#screen [row,column] (row is offset to match tty usage)#;\\ {\tt n} --- \verb#none (no coordinates shown) [default]#. %.ei %.ed %.lp "" -+The +The {\it whatis\verb+_+coord\/} option is also used with the `{\tt /m}', `{\tt /M}', `{\tt /o}', and `{\tt /O}' sub-commands diff --git a/doc/fixes36.1 b/doc/fixes36.1 index cf8ca0510..c5ded686b 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -323,6 +323,8 @@ extend wizard-mode '#stats' command stack and leave 1 wielded, otherwise it requires confirmation 'Q' will accept an item count to manually split a stack as part of it being quivered, provided the stack isn't already in the quiver slot +during end of game disclosure, the vanquished monsters list can be sorted in + one of several ways by answering 'a' to "disclose vanquished monsters?" Platform- and/or Interface-Specific New Features diff --git a/include/flag.h b/include/flag.h index f34dfa71f..9f611bfe9 100644 --- a/include/flag.h +++ b/include/flag.h @@ -76,8 +76,10 @@ struct flag { #define NUM_DISCLOSURE_OPTIONS 6 /* i,a,v,g,c,o (decl.c) */ #define DISCLOSE_PROMPT_DEFAULT_YES 'y' #define DISCLOSE_PROMPT_DEFAULT_NO 'n' +#define DISCLOSE_PROMPT_DEFAULT_SPECIAL '?' /* v, default a */ #define DISCLOSE_YES_WITHOUT_PROMPT '+' #define DISCLOSE_NO_WITHOUT_PROMPT '-' +#define DISCLOSE_SPECIAL_WITHOUT_PROMPT '#' /* v, use a */ char end_disclose[NUM_DISCLOSURE_OPTIONS + 1]; /* disclose various info upon exit */ char menu_style; /* User interface style setting */ diff --git a/src/end.c b/src/end.c index 39105578d..b02df47d8 100644 --- a/src/end.c +++ b/src/end.c @@ -52,6 +52,7 @@ STATIC_DCL boolean FDECL(odds_and_ends, (struct obj *, int)); STATIC_DCL void FDECL(savelife, (int)); STATIC_PTR int FDECL(CFDECLSPEC vanqsort_cmp, (const genericptr, const genericptr)); +STATIC_DCL void NDECL(set_vanq_order); STATIC_DCL void FDECL(list_vanquished, (CHAR_P, BOOLEAN_P)); STATIC_DCL void FDECL(list_genocided, (CHAR_P, BOOLEAN_P)); STATIC_DCL boolean FDECL(should_query_disclose_option, (int, char *)); @@ -629,7 +630,7 @@ int category; char *defquery; { int idx; - char *dop; + char disclose, *dop; *defquery = 'n'; if ((dop = index(disclosure_options, category)) != 0) { @@ -641,15 +642,22 @@ char *defquery; *defquery = DISCLOSE_PROMPT_DEFAULT_YES; return TRUE; } - if (flags.end_disclose[idx] == DISCLOSE_YES_WITHOUT_PROMPT) { + disclose = flags.end_disclose[idx]; + if (disclose == DISCLOSE_YES_WITHOUT_PROMPT) { *defquery = 'y'; return FALSE; - } else if (flags.end_disclose[idx] == DISCLOSE_NO_WITHOUT_PROMPT) { + } else if (disclose == DISCLOSE_SPECIAL_WITHOUT_PROMPT) { + *defquery = 'a'; + return FALSE; + } else if (disclose == DISCLOSE_NO_WITHOUT_PROMPT) { *defquery = 'n'; return FALSE; - } else if (flags.end_disclose[idx] == DISCLOSE_PROMPT_DEFAULT_YES) { + } else if (disclose == DISCLOSE_PROMPT_DEFAULT_YES) { *defquery = 'y'; return TRUE; + } else if (disclose == DISCLOSE_PROMPT_DEFAULT_SPECIAL) { + *defquery = 'a'; + return TRUE; } else { *defquery = 'n'; return TRUE; @@ -1426,23 +1434,140 @@ int status; nethack_exit(status); } +extern const int monstr[]; + +static const char *vanqorders[] = { + "traditional: by monster level, by internal monster index", +#define VANQ_MLVL_MNDX 0 + "by monster toughness, by internal monster index", +#define VANQ_MSTR_MNDX 1 + "alphabetically, first unique monsters, then others", +#define VANQ_ALPHA_SEP 2 + "alphabetically, unique monsters and others intermixed", +#define VANQ_ALPHA_MIX 3 + "by monster class, high to low level within class", +#define VANQ_MCLS_HTOL 4 + "by monster class, low to high level within class", +#define VANQ_MCLS_LTOH 5 + "by count, high to low, by internal index within tied count", +#define VANQ_COUNT_H_L 6 + "by count, low to high, by internal index within tied count", +#define VANQ_COUNT_L_H 7 +}; +static int vanq_sortmode = VANQ_MLVL_MNDX; + STATIC_PTR int CFDECLSPEC vanqsort_cmp(vptr1, vptr2) const genericptr vptr1; const genericptr vptr2; { int indx1 = *(short *) vptr1, indx2 = *(short *) vptr2, - mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel, - res; + mlev1, mlev2, mstr1, mstr2, uniq1, uniq2, died1, died2, res; + const char *name1, *name2, *punct; + schar mcls1, mcls2; - /* sort by monster level */ - res = mlev2 - mlev1; /* mlevel high to low */ + switch (vanq_sortmode) { + default: + case VANQ_MLVL_MNDX: + /* sort by monster level */ + mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel; + res = mlev2 - mlev1; /* mlevel high to low */ + break; + case VANQ_MSTR_MNDX: + /* sort by monster toughness */ + mstr1 = monstr[indx1], mstr2 = monstr[indx2]; + res = mstr2 - mstr1; /* monstr high to low */ + break; + case VANQ_ALPHA_SEP: + uniq1 = ((mons[indx1].geno & G_UNIQ) && indx1 != PM_HIGH_PRIEST); + uniq2 = ((mons[indx2].geno & G_UNIQ) && indx2 != PM_HIGH_PRIEST); + if (uniq1 ^ uniq2) { /* one or other uniq, but not both */ + res = uniq2 - uniq1; + break; + } /* else both unique or neither unique */ + /*FALLTHRU*/ + case VANQ_ALPHA_MIX: + name1 = mons[indx1].mname, name2 = mons[indx2].mname; + res = strcmpi(name1, name2); /* caseblind alhpa, low to high */ + break; + case VANQ_MCLS_HTOL: + case VANQ_MCLS_LTOH: + /* mons[].mlet is a small integer, 1..N, of type plain char; + if 'char' happens to be unsigned, (mlet1 - mlet2) would yield + an inappropriate result when mlet2 is greater than mlet1, + so force our copies (mcls1, mcls2) to be signed */ + mcls1 = (schar) mons[indx1].mlet, mcls2 = (schar) mons[indx2].mlet; + /* S_ANT through S_ZRUTY correspond to lowercase monster classes, + S_ANGEL through S_ZOMBIE correspond to uppercase, and various + punctuation characters are used for classes beyond those */ + if (mcls1 > S_ZOMBIE && mcls2 > S_ZOMBIE) { + /* force a specific order to the punctuation classes that's + different from the internal order; + internal order is ok if neither or just one is punctuation + since letters have lower values so come out before punct */ + static const char punctclasses[] = { + S_LIZARD, S_EEL, S_GOLEM, S_GHOST, S_DEMON, S_HUMAN, '\0' + }; + + if ((punct = index(punctclasses, mcls1)) != 0) + mcls1 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses)); + if ((punct = index(punctclasses, mcls2)) != 0) + mcls2 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses)); + } + res = mcls1 - mcls2; /* class */ + if (res == 0) { + mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel; + res = mlev1 - mlev2; /* mlevel low to high */ + if (vanq_sortmode == VANQ_MCLS_HTOL) + res = -res; /* mlevel high to low */ + } + break; + case VANQ_COUNT_H_L: + case VANQ_COUNT_L_H: + died1 = mvitals[indx1].died, died2 = mvitals[indx2].died; + res = died2 - died1; /* dead count high to low */ + if (vanq_sortmode == VANQ_COUNT_L_H) + res = -res; /* dead count low to high */ + break; + } + /* tiebreaker: internal mons[] index */ if (res == 0) - res = indx1 - indx2; /* tiebreaker, mons[] index low to high */ - + res = indx1 - indx2; /* mndx low to high */ return res; } +STATIC_OVL void +set_vanq_order() +{ + winid tmpwin; + menu_item *selected; + anything any; + int i, n, choice; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + any = zeroany; /* zero out all bits */ + for (i = 0; i < SIZE(vanqorders); i++) { + if (i == VANQ_ALPHA_MIX || i == VANQ_MCLS_HTOL) /* skip these */ + continue; + any.a_int = i + 1; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, vanqorders[i], + (i == vanq_sortmode) ? MENU_SELECTED : MENU_UNSELECTED); + } + end_menu(tmpwin, "Sort order for vanquished monster counts"); + + n = select_menu(tmpwin, PICK_ONE, &selected); + destroy_nhwindow(tmpwin); + if (n > 0) { + choice = selected[0].item.a_int - 1; + /* skip preselected entry if we have more than one item chosen */ + if (n > 1 && choice == vanq_sortmode) + choice = selected[1].item.a_int - 1; + free((genericptr_t) selected); + vanq_sortmode = choice; + } +} + /* #vanquished command */ int dovanquished() @@ -1477,9 +1602,12 @@ boolean ask; * includes all dead monsters, not just those killed by the player */ if (ntypes != 0) { + char mlet, prev_mlet = 0; /* used as small integer, not character */ + boolean class_header, uniq_header, was_uniq = FALSE; + c = ask ? yn_function( "Do you want an account of creatures vanquished?", - ynqchars, defquery) + ynaqchars, defquery) : defquery; if (c == 'q') done_stopprint++; @@ -1488,10 +1616,23 @@ boolean ask; putstr(klwin, 0, "Vanquished creatures:"); putstr(klwin, 0, ""); + if (c == 'a') + set_vanq_order(); /* menu to choose value for vanq_sortmode */ + uniq_header = (vanq_sortmode == VANQ_ALPHA_SEP); + class_header = (vanq_sortmode == VANQ_MCLS_LTOH + || vanq_sortmode == VANQ_MCLS_HTOL); + qsort((genericptr_t) mindx, ntypes, sizeof *mindx, vanqsort_cmp); for (ni = 0; ni < ntypes; ni++) { i = mindx[ni]; nkilled = mvitals[i].died; + mlet = mons[i].mlet; + if (class_header && mlet != prev_mlet) { + Strcpy(buf, def_monsyms[(int) mlet].explain); + putstr(klwin, ask ? 0 : iflags.menu_headings, + upstart(buf)); + prev_mlet = mlet; + } if ((mons[i].geno & G_UNIQ) && i != PM_HIGH_PRIEST) { Sprintf(buf, "%s%s", !type_is_pname(&mons[i]) ? "the " : "", @@ -1509,7 +1650,12 @@ boolean ask; break; } } + was_uniq = TRUE; } else { + if (uniq_header && was_uniq) { + putstr(klwin, 0, ""); + was_uniq = FALSE; + } /* trolls or undead might have come back, but we don't keep track of that */ if (nkilled == 1) @@ -1523,6 +1669,8 @@ boolean ask; : !strncmpi(buf, "an ", 3) ? 1 : !strncmpi(buf, "a ", 2) ? 2 : !isdigit(buf[2]) ? 4 : 0; + if (class_header) + ++pfx; Sprintf(buftoo, "%*s%s", pfx, "", buf); putstr(klwin, 0, buftoo); } diff --git a/src/options.c b/src/options.c index 8cfb4e3fd..bfcbba289 100644 --- a/src/options.c +++ b/src/options.c @@ -2815,7 +2815,9 @@ boolean tinitial, tfrom_file; while (*op && num < sizeof flags.end_disclose - 1) { static char valid_settings[] = { DISCLOSE_PROMPT_DEFAULT_YES, DISCLOSE_PROMPT_DEFAULT_NO, - DISCLOSE_YES_WITHOUT_PROMPT, DISCLOSE_NO_WITHOUT_PROMPT, '\0' + DISCLOSE_PROMPT_DEFAULT_SPECIAL, + DISCLOSE_YES_WITHOUT_PROMPT, DISCLOSE_NO_WITHOUT_PROMPT, + DISCLOSE_SPECIAL_WITHOUT_PROMPT, '\0' }; register char c, *dop; @@ -2832,6 +2834,12 @@ boolean tinitial, tfrom_file; continue; } if (prefix_val != -1) { + if (*dop != 'v') { + if (prefix_val == DISCLOSE_PROMPT_DEFAULT_SPECIAL) + prefix_val = DISCLOSE_PROMPT_DEFAULT_YES; + if (prefix_val == DISCLOSE_SPECIAL_WITHOUT_PROMPT) + prefix_val = DISCLOSE_YES_WITHOUT_PROMPT; + } flags.end_disclose[idx] = prefix_val; prefix_val = -1; } else @@ -4016,6 +4024,7 @@ boolean setinitial, setfromfile; }; int disc_cat[NUM_DISCLOSURE_OPTIONS]; int pick_cnt, pick_idx, opt_idx; + char c; menu_item *disclosure_pick = (menu_item *) 0; tmpwin = create_nhwindow(NHW_MENU); @@ -4043,6 +4052,7 @@ boolean setinitial, setfromfile; for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) { if (disc_cat[i]) { + c = flags.end_disclose[i]; Sprintf(buf, "Disclosure options for %s:", disclosure_names[i]); tmpwin = create_nhwindow(NHW_MENU); @@ -4050,24 +4060,41 @@ boolean setinitial, setfromfile; any = zeroany; /* 'y','n',and '+' work as alternate selectors; '-' doesn't */ any.a_char = DISCLOSE_NO_WITHOUT_PROMPT; - add_menu(tmpwin, NO_GLYPH, &any, 'a', any.a_char, ATR_NONE, + add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE, "Never disclose, without prompting", - MENU_UNSELECTED); + (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED); any.a_char = DISCLOSE_YES_WITHOUT_PROMPT; - add_menu(tmpwin, NO_GLYPH, &any, 'b', any.a_char, ATR_NONE, + add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE, "Always disclose, without prompting", - MENU_UNSELECTED); + (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED); + if (*disclosure_names[i] == 'v') { + any.a_char = DISCLOSE_SPECIAL_WITHOUT_PROMPT; /* '#' */ + add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE, + "Always disclose, pick sort order from menu", + (c == any.a_char) ? MENU_SELECTED + : MENU_UNSELECTED); + } any.a_char = DISCLOSE_PROMPT_DEFAULT_NO; - add_menu(tmpwin, NO_GLYPH, &any, 'c', any.a_char, ATR_NONE, + add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE, "Prompt, with default answer of \"No\"", - MENU_UNSELECTED); + (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED); any.a_char = DISCLOSE_PROMPT_DEFAULT_YES; - add_menu(tmpwin, NO_GLYPH, &any, 'd', any.a_char, ATR_NONE, + add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE, "Prompt, with default answer of \"Yes\"", - MENU_UNSELECTED); + (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED); + if (*disclosure_names[i] == 'v') { + any.a_char = DISCLOSE_PROMPT_DEFAULT_SPECIAL; /* '?' */ + add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE, + "Prompt, with default answer of \"Ask\" to request sort menu", + (c == any.a_char) ? MENU_SELECTED + : MENU_UNSELECTED); + } end_menu(tmpwin, buf); - if (select_menu(tmpwin, PICK_ONE, &disclosure_pick) > 0) { - flags.end_disclose[i] = disclosure_pick->item.a_char; + n = select_menu(tmpwin, PICK_ONE, &disclosure_pick); + if (n > 0) { + flags.end_disclose[i] = disclosure_pick[0].item.a_char; + if (n > 1 && flags.end_disclose[i] == c) + flags.end_disclose[i] = disclosure_pick[1].item.a_char; free((genericptr_t) disclosure_pick); } destroy_nhwindow(tmpwin);