]> granicus.if.org Git - nethack/commitdiff
re-implement pull req #334 - sorting discoveries
authorPatR <rankin@nethack.org>
Sun, 20 Dec 2020 01:45:49 +0000 (17:45 -0800)
committerPatR <rankin@nethack.org>
Sun, 20 Dec 2020 01:45:49 +0000 (17:45 -0800)
The pull request changed \ and ` output to unconditionally show
discoveries in alphabetical order.  That's nearly useless except
when looking at prediscovered weapons and armor that fighter
types start out knowing.

This allows the player to choose sorting order via the new
'sortdiscoveries' option.  In addition to setting it via
config file or 'O', it can be set via 'm' prefix for \ and `.
Choices are:
 o - sort by class, by order of discovery in class (default);
 s - sort by 'sortloot' classification which groups sub-class
     items (so all helmets before any other armor, then all
     gloves, then boots, and so on); within each sub-class, or
     whole class for classes which don't subdivide so usefully,
     partly-discovered types (where a name has been assigned)
     come before fully ID'd types;
 c - sort by class, alphabetically within class;
 a - sort alphabetically across all classes.

Turned out to be a large amount of work for fairly little gain,
although I suspect that 'sortdiscoveries:s' will eventually be
more popular than the default.

Invalidates existing save files so that current sort setting can
persist across save/restore cycles.

Closes #334

12 files changed:
dat/opthelp
doc/Guidebook.mn
doc/Guidebook.tex
doc/fixes37.0
include/extern.h
include/flag.h
include/optlist.h
include/patchlevel.h
src/cmd.c
src/invent.c
src/o_init.c
src/options.c

index f3debdc5dcc5ae48aa39bfc31aaeb16ec3e61833..c752a8d2ef0a345b23e8812159635cf87145700d 100644 (file)
@@ -205,6 +205,15 @@ scores        the parts of the score list you wish    [!own/3 top/2 around]
               to see when the game ends.  You choose a combination of
               top scores, scores around the top scores, and all of your
               own scores.
+sortdiscoveries preferred order when viewing list of discovered objects [o]
+              o -- in order of discovery within each class
+              s -- sortloot's "loot" order
+              c -- alphabetized within each class
+              a -- alphabetized across all classes
+sortloot      preferred order when examining a set of objects           [n]
+              none -- no sorting
+              loot -- sort piles of objects on floor and in containers
+              full -- 'loot' plus objects in inventory
 statushilites whether to display status highlights (when non-zero) and  [0]
               also how many turns to display temporary highlights (for
               'up', 'down', and 'changed' hilite_status rules)
index 4fab00cd72af3be70d5ebb3e9b87b116fbad786b..dab9e06daeca9dc687274899ef58f831e18ef016 100644 (file)
@@ -35,7 +35,7 @@
 .ds vr "NetHack 3.7
 .ds f0 "\*(vr
 .ds f1
-.ds f2 "December 16, 2020
+.ds f2 "December 19, 2020
 .
 .\" A note on some special characters:
 .\" \(lq = left double quote
@@ -1055,8 +1055,12 @@ be added to the end of the list rather than be inserted into the sorted
 ordering.)
 .lp \\\\
 Show what types of objects have been discovered.
+.lp ""
+May be preceded by \(oq\f(CRm\fP\(cq to select preferred display order.
 .lp \`
 Show discovered types for one class of objects.
+.lp ""
+May be preceded by \(oq\f(CRm\fP\(cq to select preferred display order.
 .lp !
 Escape to a shell.
 See \(lq#shell\(rq below for more details.
@@ -1258,10 +1262,16 @@ Default key is \(oq\(haD\(cq, and \(oqk\(cq if
 is on.
 .lp "#known   "
 Show what object types have been discovered.
-Default key is \(oq\\\(cq.
+Default key is \(oq\f(CR\\\fP\(cq.
+.lp ""
+The \(oq\f(CRm\fP\(cq prefix allows assigning a new value to the
+.op sortdiscoveries
+option to control the order in which the discoveries are displayed.
 .lp #knownclass
 Show discovered types for one class of objects.
-Default key is \(oq\`\(cq.
+Default key is \(oq\f(CR\`\fP\(cq.
+.lp ""
+The \(oq\f(CRm\fP\(cq prefix operates the same as for \(lq#known\(rq.
 .lp #levelchange
 Change your experience level.
 Autocompletes.
@@ -4109,19 +4119,47 @@ Persistent.
 Show your approximate accumulated score on bottom line (default off).
 Persistent.
 .lp "silent  "
-Suppress terminal beeps (default on).  Persistent.
+Suppress terminal beeps (default on).
+Persistent.
+.lp sortdiscoveries
+Controls the sorting behavior for the output of the \(oq\f(CR\\\fP\(cq
+and \(oq\f(CR\`\fP\(cq commands.
+Persistent.
+.lp ""
+The possible values are:
+.PS o
+.PL o
+list object types by class, in discovery order within each class;
+default;
+.PL s
+list object types by
+.op sortloot
+classification: by class, by sub-class within class for classes which
+have substantial groupings (like helmets, boots, gloves, and so forth
+for armor), with object types partly-discovered via assigned name coming
+before fully identified types;
+.PL c
+list by class, alphabetically within each class;
+.PL a
+list alphabetically across all classes.
+.PE
+Can be interactively set via the \(oq\f(CRO\fP\(cq command or via using
+the \(oq\f(CRm\fP\(cq prefix before the \(oq\f(CR\\\fP\(cq
+or \(oq\f(CR\`\fP\(cq command.
 .lp sortloot
 Controls the sorting behavior of the pickup lists for inventory
 and #loot commands and some others.  Persistent.
+.lp ""
 The possible values are:
-.PS full
+.PS none \" note: with proportional font, "none" is wider than "full" or "loot"
 .PL full
 always sort the lists;
 .PL loot
 only sort the lists that don't use inventory letters, like with
 the #loot and pickup commands;
 .PL none
-show lists the traditional way without sorting.
+show lists the traditional way without sorting;
+default.
 .PE
 .lp sortpack
 Sort the pack contents by type when displaying inventory (default on).
index a3dd3fecc94376005cd2e59ff618a8d259020e6a..b1606a34cf046f1591246a943e58fe4de1a869fb 100644 (file)
@@ -45,7 +45,7 @@
 %.au
 \author{Original version - Eric S. Raymond\\
 (Edited and expanded for 3.7 by Mike Stephenson and others)}
-\date{December 16, 2020}
+\date{December 19, 2020}
 
 \maketitle
 
@@ -1154,9 +1154,15 @@ ordering.)
 %.lp
 \item[\tb{$\backslash$}]
 Show what types of objects have been discovered.
+\\
+%.lp ""
+May be preceded by `{\tt m}' to select preferred display order.
 %.lp
 \item[\tb{\`}]
 Show discovered types for one class of objects.
+\\
+.lp ""
+May be preceded by `{\tt m}' to select preferred display order.
 %.lp
 \item[\tb{!}]
 Escape to a shell.
@@ -1340,10 +1346,18 @@ and also `{\tt k}' if {\it number\verb+_+pad\/} is on.
 \item[\tb{\#known}]
 Show what object types have been discovered.
 Default key is `{\tt $\backslash$}'.
+\\
+%.lp ""
+The `{\tt m}' prefix allows assigning a new value to the
+{\it sortdiscoveries\/}
+option to control the order in which the discoveries are displayed.
 %.lp
 \item[\tb{\#knownclass}]
 Show discovered types for one class of objects.
 Default key is `{\tt `}'.
+\\
+%.lp ""
+The `{\tt m}' prefix operates the same as for \(lq#known\(rq.
 %.lp
 \item[\tb{\#levelchange}]
 Change your experience level.
@@ -4450,17 +4464,44 @@ Persistent.
 \item[\ib{silent}]
 Suppress terminal beeps (default on).  Persistent.
 %.lp
+\item[\ib{sortdiscoveries}]
+Controls the sorting behavior for the output of the `{\tt $\backslash$}'
+and `{\tt \`}' commands.
+Persistent.
+\\
+%.lp ""
+The possible values are:
+%.PS o
+%.PL o
+(\tt o} --- list object types by class, in discovery order within each class;
+default;
+%.PL s
+{\tt s} --- list object types by {\it sortloot\/}
+classification: by class, by sub-class within class for classes which
+have substantial groupings (like helmets, boots, gloves, and so forth
+for armor), with object types partly-discovered via assigned name coming
+before fully identified types;
+\\
+%.PL c
+{\tt c} --- list by class, alphabetically within each class;\\
+%.PL a
+{\tt a} --- list alphabetically across all classes.\\
+%.PE
+Can be interactively set via the `{\tt O}' command or via using
+the `{\tt m}' prefix before the `{\tt $\backslash$}'
+or `{\tt \`}' command.
+%.lp
 \item[\ib{sortloot}]
 Controls the sorting behavior of pickup lists for inventory
 and \#loot commands and some others.  Persistent.
-
+\\
 The possible values are:
 %.sd
 %.si
 {\tt full} --- always sort the lists;\\
 {\tt loot} --- only sort the lists that don't use inventory
        letters, like with the \#loot and pickup commands;\\
-{\tt none} --- show lists the traditional way without sorting.
+{\tt none} --- show lists the traditional way without sorting; default.
 %.ei
 %.ed
 %.lp
index 9b619329b052775e7ecb501906c1a9fcc935fec2..23868980b7e067aec1d67ec08f0a9fa217ac97dc 100644 (file)
@@ -681,6 +681,7 @@ when "?i" (show key bindings) displays commands and their keys, also show
 assign default key binding for <del> or <delete> to execute #terrain
 assign M-X to #exploremode
 make #herecmdmenu and #therecmdmenu autocomplete
+add 'sortdiscoveries' option to control output of '\' and '`' commands
 
 
 Platform- and/or Interface-Specific New Features
index f771e0766af76b9ec1a1dbc53107af6d0f5e23dd..cc7fd8be974f4c9fdd2fd2785f6455758f5c44ab 100644 (file)
@@ -972,6 +972,7 @@ E void NDECL(ustatusline);
 
 /* ### invent.c ### */
 
+E void FDECL(loot_classify, (Loot *, struct obj *));
 E Loot *FDECL(sortloot, (struct obj **, unsigned, BOOLEAN_P,
                          boolean (*)(OBJ_P)));
 E void FDECL(unsortloot, (Loot **));
@@ -1743,6 +1744,7 @@ E void FDECL(savenames, (NHFILE *));
 E void FDECL(restnames, (NHFILE *));
 E void FDECL(discover_object, (int, BOOLEAN_P, BOOLEAN_P));
 E void FDECL(undiscover_object, (int));
+E int FDECL(choose_disco_sort, (int));
 E int NDECL(dodiscovered);
 E int NDECL(doclassdisco);
 E void NDECL(rename_disco);
index 5a0f0213e0fad116a668bd2f2b0f9c492999b549..4122dd046c715eb0078a48675ad2a1587930d546 100644 (file)
@@ -80,6 +80,7 @@ struct flag {
 #define PARANOID_EATING     0x0200
     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 */
     char sortloot; /* 'n'=none, 'l'=loot (pickup), 'f'=full ('l'+invent) */
     char inv_order[MAXOCLASSES];
     char pickup_types[MAXOCLASSES];
@@ -93,8 +94,7 @@ struct flag {
     char end_disclose[NUM_DISCLOSURE_OPTIONS + 1]; /* disclose various
                                                       info upon exit */
     char menu_style;    /* User interface style setting */
-    boolean made_fruit; /* don't easily let the user overflow the number of
-                           fruits */
+    boolean made_fruit; /* don't easily let user overflow fruit limit */
 
     /* KMH, role patch -- Variables used during startup.
      *
index 7ae20db6edc470adc02ca2de8e9f94e10d35d7be..9bf4a057db142df47c1ae39fc24ee4e76e97a8b2 100644 (file)
@@ -406,6 +406,8 @@ pfx_##a,
                 &flags.silent)
     NHOPTB(softkeyboard, 0, opt_in, set_in_config, Off, Yes, No, No, NoAlias,
                 &iflags.wc2_softkeyboard)
+    NHOPTC(sortdiscoveries, 0, opt_in, set_in_game, Yes, Yes, No, Yes,
+                NoAlias, "preferred order when displaying discovered objects")
     NHOPTC(sortloot, 4, opt_in, set_in_game, No, Yes, No, Yes, NoAlias,
                 "sort object selection lists by description")
     NHOPTB(sortpack, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
index 47f5d52359388cbc2ba250c2f402730d203987d9..d6969198bbe889efba6eac1f52f15ec055e50a07 100644 (file)
@@ -17,7 +17,7 @@
  * Incrementing EDITLEVEL can be used to force invalidation of old bones
  * and save files.
  */
-#define EDITLEVEL 28
+#define EDITLEVEL 29
 
 /*
  * Development status possibilities.
index 65c05546b0bc0e750df4c25e469314adf9022625..94f5a006876e0998600ffca8546fd255877b7348 100644 (file)
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -3236,6 +3236,8 @@ int NDECL((*cmd_func));
         /* 'm' for removing saddle from adjacent monster without checking
            for containers at <u.ux,u.uy> */
         || cmd_func == doloot
+        /* offer menu to choose discoveries sort order */
+        || cmd_func == dodiscovered || cmd_func == doclassdisco
         /* travel: pop up a menu of interesting targets in view */
         || cmd_func == dotravel
         /* wait and search: allow even if next to a hostile monster */
index f7be4f6691e851a3e33f4d2489e71264a6c478c5..63f7904a96707cacbdb3f6cc1965f5091b3e9900 100644 (file)
@@ -13,7 +13,6 @@
 #define CONTAINED_SYM '>' /* designator for inside a container */
 #define HANDS_SYM '-'
 
-static void FDECL(loot_classify, (Loot *, struct obj *));
 static char *FDECL(loot_xname, (struct obj *));
 static int FDECL(invletter_value, (CHAR_P));
 static int FDECL(CFDECLSPEC sortloot_cmp, (const genericptr,
@@ -54,7 +53,7 @@ static char FDECL(obj_to_let, (struct obj *));
 static const char venom_inv[] = { VENOM_CLASS, 0 }; /* (constant) */
 
 /* sortloot() classification; called at most once [per sort] for each object */
-static void
+void
 loot_classify(sort_item, obj)
 Loot *sort_item;
 struct obj *obj;
index 0ef30c222f1cf7420cd0f70a289d731c5a31a3ee..5806e9ee305cbd13ef3a855b55fe3e730cf00d63 100644 (file)
@@ -9,6 +9,8 @@ static void FDECL(setgemprobs, (d_level *));
 static void FDECL(shuffle, (int, int, BOOLEAN_P));
 static void NDECL(shuffle_all);
 static boolean FDECL(interesting_to_discover, (int));
+static int FDECL(CFDECLSPEC discovered_cmp, (const genericptr,
+                                             const genericptr));
 static char *FDECL(oclass_to_name, (CHAR_P, char *));
 
 #ifdef USE_TILES
@@ -465,37 +467,167 @@ static const short uniq_objs[] = {
     BELL_OF_OPENING,
 };
 
+/* discoveries qsort comparison function */
+static int CFDECLSPEC
+discovered_cmp(v1, v2)
+const genericptr v1;
+const genericptr v2;
+{
+    const char *s1 = *(const char **) v1;
+    const char *s2 = *(const char **) v2;
+    /* each element starts with "* " or "  " but we don't sort by those */
+    int res = strcmpi(s1 + 2, s2 + 2);
+
+    if (res == 0) {
+        ; /* no tie-breaker needed */
+    }
+    return res;
+}
+
+static char *
+sortloot_descr(otyp, outbuf)
+int otyp;
+char *outbuf;
+{
+    Loot sl_cookie;
+    struct obj o;
+
+    o = cg.zeroobj;
+    o.otyp = otyp;
+    o.oclass = objects[otyp].oc_class;
+    o.dknown = 1;
+    o.known = (objects[otyp].oc_name_known || !objects[otyp].oc_uses_known)
+              ? 1 : 0;
+    o.corpsenm = NON_PM; /* suppress statue and figurine details */
+    /* but suppressing fruit details leads to "bad fruit #0" */
+    if (otyp == SLIME_MOLD)
+        o.spe = g.context.current_fruit;
+
+    (void) memset((genericptr_t) &sl_cookie, 0, sizeof sl_cookie);
+    sl_cookie.obj = (struct obj *) 0;
+    sl_cookie.str = (char *) 0;
+
+    loot_classify(&sl_cookie, &o);
+    Sprintf(outbuf, "%02d%02d%1d ",
+            sl_cookie.orderclass, sl_cookie.subclass, sl_cookie.disco);
+    return outbuf;
+}
+
+#define DISCO_BYCLASS      0 /* by discovery order within each class */
+#define DISCO_SORTLOOT     1 /* by discovery order within each subclass */
+#define DISCO_ALPHABYCLASS 2 /* alphabetized within each class */
+#define DISCO_ALPHABETIZED 3 /* alphabetized across all classes */
+/* also used in options.c (optfn_sortdiscoveries) */
+const char disco_order_let[] = "osca";
+const char *const disco_orders_descr[] = {
+    "by order of discovery within each class",
+    "sortloot order (by class with some sub-class groupings)",
+    "alphabetical within each class",
+    "alphabetical across all classes",
+    (char *) 0
+};
+
+int
+choose_disco_sort(mode)
+int mode; /* 0 => 'O' cmd, 1 => full discoveries; 2 => class discoveries */
+{
+    winid tmpwin;
+    menu_item *selected;
+    anything any;
+    int i, n, choice;
+
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
+    any = cg.zeroany; /* zero out all bits */
+    for (i = 0; disco_orders_descr[i]; ++i) {
+        any.a_int = disco_order_let[i];
+        add_menu(tmpwin, NO_GLYPH, &any, (char) any.a_int, 0, ATR_NONE,
+                 disco_orders_descr[i],
+                 (disco_order_let[i] == flags.discosort)
+                    ? MENU_ITEMFLAGS_SELECTED
+                    : MENU_ITEMFLAGS_NONE);
+    }
+    if (mode == 2) {
+        /* called via 'm `' where full alphabetize doesn't make sense
+           (only showing one class so can't span all classes) but the
+           chosen sort will stick and also apply to '\' usage */
+        any = cg.zeroany;
+        add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
+                 "", MENU_ITEMFLAGS_NONE);
+        add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
+                 "Note: full alphabetical and alphabetical within class",
+                 MENU_ITEMFLAGS_NONE);
+        add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
+                 "      are equivalent for single class discovery, but",
+                 MENU_ITEMFLAGS_NONE);
+        add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
+                 "      will matter for future use of total discoveries.",
+                 MENU_ITEMFLAGS_NONE);
+    }
+    end_menu(tmpwin, "Ordering of discoveries");
+
+    n = select_menu(tmpwin, PICK_ONE, &selected);
+    destroy_nhwindow(tmpwin);
+    if (n > 0) {
+        choice = selected[0].item.a_int;
+        /* skip preselected entry if we have more than one item chosen */
+        if (n > 1 && choice == (int) flags.discosort)
+            choice = selected[1].item.a_int;
+        free((genericptr_t) selected);
+        flags.discosort = choice;
+    }
+    return n;
+}
+
 /* the '\' command - show discovered object types */
 int
 dodiscovered() /* free after Robert Viduya */
 {
-    register int i, dis;
-    int ct = 0;
-    char *s, oclass, prev_class, classes[MAXOCLASSES], buf[BUFSZ];
     winid tmpwin;
+    char *s, *p, oclass, prev_class,
+         classes[MAXOCLASSES], buf[BUFSZ],
+         *sorted_lines[NUM_OBJECTS]; /* overkill */
+    int i, j, sortindx, dis, ct, uniq_ct, arti_ct, sorted_ct;
+    boolean alphabetized, alphabyclass, lootsort;
+
+    if (!flags.discosort || !(p = index(disco_order_let, flags.discosort)))
+        flags.discosort = 'o';
+
+    if (iflags.menu_requested) {
+        if (choose_disco_sort(1) < 0)
+            return 0;
+    }
+    alphabyclass = (flags.discosort == 'c');
+    alphabetized = (flags.discosort == 'a' || alphabyclass);
+    lootsort = (flags.discosort == 's');
+    sortindx = index(disco_order_let, flags.discosort) - disco_order_let;
 
     tmpwin = create_nhwindow(NHW_MENU);
-    putstr(tmpwin, 0, "Discoveries");
+    Sprintf(buf, "Discoveries, %s", disco_orders_descr[sortindx]);
+    putstr(tmpwin, 0, buf);
     putstr(tmpwin, 0, "");
 
     /* gather "unique objects" into a pseudo-class; note that they'll
        also be displayed individually within their regular class */
+    uniq_ct = 0;
     for (i = dis = 0; i < SIZE(uniq_objs); i++)
         if (objects[uniq_objs[i]].oc_name_known) {
             if (!dis++)
                 putstr(tmpwin, iflags.menu_headings, "Unique items");
+            ++uniq_ct;
             Sprintf(buf, "  %s", OBJ_NAME(objects[uniq_objs[i]]));
             putstr(tmpwin, 0, buf);
-            ++ct;
         }
     /* display any known artifacts as another pseudo-class */
-    ct += disp_artifact_discoveries(tmpwin);
+    arti_ct = disp_artifact_discoveries(tmpwin);
 
     /* several classes are omitted from packorder; one is of interest here */
     Strcpy(classes, flags.inv_order);
     if (!index(classes, VENOM_CLASS))
         (void) strkitten(classes, VENOM_CLASS); /* append char to string */
 
+    ct = uniq_ct + arti_ct;
+    sorted_ct = 0;
     for (s = classes; *s; s++) {
         oclass = *s;
         prev_class = oclass + 1; /* forced different from oclass */
@@ -504,21 +636,63 @@ dodiscovered() /* free after Robert Viduya */
             if ((dis = g.disco[i]) != 0 && interesting_to_discover(dis)) {
                 ct++;
                 if (oclass != prev_class) {
-                    putstr(tmpwin, iflags.menu_headings,
-                           let_to_name(oclass, FALSE, FALSE));
-                    prev_class = oclass;
+                    if ((alphabyclass || lootsort) && sorted_ct) {
+                        /* output previous class */
+                        qsort(sorted_lines, sorted_ct, sizeof (char *),
+                              discovered_cmp);
+                        for (j = 0; j < sorted_ct; ++j) {
+                            p = sorted_lines[j];
+                            if (lootsort) {
+                                p[6] = p[0]; /* '*' or ' ' */
+                                p += 6;
+                            }
+                            putstr(tmpwin, 0, p);
+                            free(sorted_lines[j]), sorted_lines[j] = 0;
+                        }
+                        sorted_ct = 0;
+                    }
+                    if (!alphabetized || alphabyclass) {
+                        /* header for new class */
+                        putstr(tmpwin, iflags.menu_headings,
+                               let_to_name(oclass, FALSE, FALSE));
+                        prev_class = oclass;
+                    }
                 }
-                Sprintf(buf, "%s %s",
-                        (objects[dis].oc_pre_discovered ? "*" : " "),
-                        obj_typename(dis));
-                putstr(tmpwin, 0, buf);
+                Strcpy(buf,  objects[dis].oc_pre_discovered ? "* " : "  ");
+                if (lootsort)
+                    (void) sortloot_descr(dis, &buf[2]);
+                Strcat(buf, obj_typename(dis));
+
+                if (!alphabetized && !lootsort)
+                    putstr(tmpwin, 0, buf);
+                else
+                    sorted_lines[sorted_ct++] = dupstr(buf);
             }
         }
     }
     if (ct == 0) {
         You("haven't discovered anything yet...");
-    } else
+    } else {
+        if (sorted_ct) {
+            /* if we're alphabetizing by class, we've already shown the
+               relevant header above; if we're alphabetizing across all
+               classes, we normally don't need a header; but it we showed
+               any unique items or any artifacts then we do need one */
+            if ((uniq_ct || arti_ct) && !alphabyclass)
+                putstr(tmpwin, iflags.menu_headings, "Discovered items");
+            qsort(sorted_lines, sorted_ct, sizeof (char *), discovered_cmp);
+            for (j = 0; j < sorted_ct; ++j) {
+                p = sorted_lines[j];
+                if (lootsort) {
+                    p[6] = p[0]; /* '*' or ' ' */
+                    p += 6;
+                }
+                putstr(tmpwin, 0, p);
+                free(sorted_lines[j]), sorted_lines[j] = 0;
+            }
+        }
         display_nhwindow(tmpwin, TRUE);
+    }
     destroy_nhwindow(tmpwin);
 
     return 0;
@@ -547,13 +721,24 @@ doclassdisco()
         havent_discovered_any[] = "haven't discovered any %s yet.",
         unique_items[] = "unique items",
         artifact_items[] = "artifacts";
-    char *s, c, oclass, menulet, allclasses[MAXOCLASSES],
-        discosyms[2 + MAXOCLASSES + 1], buf[BUFSZ];
-    int i, ct, dis, xtras;
-    boolean traditional;
     winid tmpwin = WIN_ERR;
-    anything any;
     menu_item *pick_list = 0;
+    anything any;
+    char *p, *s, c, oclass, menulet, allclasses[MAXOCLASSES],
+         discosyms[2 + MAXOCLASSES + 1], buf[BUFSZ],
+         *sorted_lines[NUM_OBJECTS]; /* overkill */
+    int i, ct, dis, xtras, sorted_ct;
+    boolean traditional, alphabetized, lootsort;
+
+    if (!flags.discosort || !(p = index(disco_order_let, flags.discosort)))
+        flags.discosort = 'o';
+
+    if (iflags.menu_requested) {
+        if (choose_disco_sort(2) < 0)
+            return 0;
+    }
+    alphabetized = (flags.discosort == 'a' || flags.discosort == 'c');
+    lootsort = (flags.discosort == 's');
 
     discosyms[0] = '\0';
     traditional = (flags.menu_style == MENU_TRADITIONAL
@@ -670,9 +855,9 @@ doclassdisco()
                upstart(strcpy(buf, unique_items)));
         for (i = 0; i < SIZE(uniq_objs); i++)
             if (objects[uniq_objs[i]].oc_name_known) {
+                ++ct;
                 Sprintf(buf, "  %s", OBJ_NAME(objects[uniq_objs[i]]));
                 putstr(tmpwin, 0, buf);
-                ++ct;
             }
         if (!ct)
             You(havent_discovered_any, unique_items);
@@ -685,20 +870,40 @@ doclassdisco()
         break;
     default:
         oclass = def_char_to_objclass(c);
-        Sprintf(buf, "Discovered %s", let_to_name(oclass, FALSE, FALSE));
-        putstr(tmpwin, iflags.menu_headings, buf);
-        for (i = g.bases[(int) oclass];
-             i < NUM_OBJECTS && objects[i].oc_class == oclass; ++i) {
+        Sprintf(buf, "Discovered %s in %s", let_to_name(oclass, FALSE, FALSE),
+                (flags.discosort == 'o') ? "order of discovery"
+                : (flags.discosort == 's') ? "'sortloot' order"
+                  : "alphabetical order");
+        putstr(tmpwin, 0, buf); /* skip iflags.menu_headings */
+        sorted_ct = 0;
+        for (i = g.bases[(int) oclass]; i < g.bases[oclass + 1] - 1; ++i) {
             if ((dis = g.disco[i]) != 0 && interesting_to_discover(dis)) {
-                Sprintf(buf, "%s %s",
-                        objects[dis].oc_pre_discovered ? "*" : " ",
-                        obj_typename(dis));
-                putstr(tmpwin, 0, buf);
                 ++ct;
+                Strcpy(buf,  objects[dis].oc_pre_discovered ? "* " : "  ");
+                if (lootsort)
+                    (void) sortloot_descr(dis, &buf[2]);
+                Strcat(buf, obj_typename(dis));
+
+                if (!alphabetized && !lootsort)
+                    putstr(tmpwin, 0, buf);
+                else
+                    sorted_lines[sorted_ct++] = dupstr(buf);
             }
         }
-        if (!ct)
+        if (!ct) {
             You(havent_discovered_any, oclass_to_name(oclass, buf));
+        } else if (sorted_ct) {
+            qsort(sorted_lines, sorted_ct, sizeof (char *), discovered_cmp);
+            for (i = 0; i < sorted_ct; ++i) {
+                p = sorted_lines[i];
+                if (lootsort) {
+                    p[6] = p[0]; /* '*' or ' ' */
+                    p += 6;
+                }
+                putstr(tmpwin, 0, p);
+                free(sorted_lines[i]), sorted_lines[i] = 0;
+            }
+        }
         break;
     }
     if (ct)
index 90d2c5b97724d32ec34f29e01528e801aaff5bff..c1ce9eefb3a2cc925f9e836b028bc27f0de027f4 100644 (file)
@@ -3256,6 +3256,66 @@ char *op;
     return optn_ok;
 }
 
+int
+optfn_sortdiscoveries(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        flags.discosort = 'o';
+        return optn_ok;
+    }
+    if (req == do_set) {
+        op = string_for_env_opt(allopt[optidx].name, opts, FALSE);
+        if (negated) {
+            flags.discosort = 'o';
+        } else if (op != empty_optstr) {
+            switch (lowc(*op)) {
+            case '0':
+            case 'o': /* order of discovery */
+                flags.discosort = 'o';
+                break;
+            case '1':
+            case 's': /* sortloot order (subclasses for some classes) */
+                flags.discosort = 's';
+                break;
+            case '2':
+            case 'c': /* alphabetical within each class */
+                flags.discosort = 'c';
+                break;
+            case '3':
+            case 'a': /* alphabetical across all classes */
+                flags.discosort = 'a';
+                break;
+            default:
+                config_error_add("Unknown %s parameter '%s'",
+                                 allopt[optidx].name, op);
+                return optn_silenterr;
+            }
+        } else
+            return optn_err;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        extern const char *const disco_orders_descr[]; /* o_init.c */
+        extern const char disco_order_let[];
+        const char *p = index(disco_order_let, flags.discosort);
+
+        if (!p)
+            flags.discosort = 'o', p = disco_order_let;
+        Strcpy(opts, disco_orders_descr[p - disco_order_let]);
+        return optn_ok;
+    }
+    if (req == do_handler) {
+        /* return handler_sortdiscoveries(); */
+        (void) choose_disco_sort(0); /* o_init.c */
+    }
+    return optn_ok;
+}
+
 int
 optfn_sortloot(optidx, req, negated, opts, op)
 int optidx;