]> granicus.if.org Git - nethack/commitdiff
Add sortloot -patch
authorPasi Kallinen <paxed@alt.org>
Thu, 9 Apr 2015 14:53:40 +0000 (17:53 +0300)
committerPasi Kallinen <paxed@alt.org>
Thu, 9 Apr 2015 15:23:54 +0000 (18:23 +0300)
Adds the "sortloot" compound option, with possible values
of "none", "loot", or "full".  It controls the sorting of
item pickup lists for inventory and looting.

doc/Guidebook.mn
doc/Guidebook.tex
doc/fixes35.0
include/extern.h
include/flag.h
src/end.c
src/invent.c
src/objnam.c
src/options.c
src/pickup.c

index 06db992479dfe1cbac984c32a956285a268b6d70..66b27f5bf3789d51d3765536622c352ab234503e 100644 (file)
@@ -2326,6 +2326,19 @@ the appearance of the display, not the way the game treats you.
 Show your approximate accumulated score on bottom line (default off).
 .lp "silent  "
 Suppress terminal beeps (default on).
+.lp sortloot
+Controls the sorting behavior of the pickup lists for inventory
+and #loot commands and some others.
+The possible values are:
+.PS full
+.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.
+.PE
 .lp sortpack
 Sort the pack contents by type when displaying inventory (default on).
 .lp sparkle
index 427d720a08a4ba554fcd25df87fe68a83e3f47c7..ae3cfa8a7b906d08c06d951ece35d733bf5b0448 100644 (file)
@@ -2792,6 +2792,20 @@ Show your approximate accumulated score on bottom line (default off).
 \item[\ib{silent}]
 Suppress terminal beeps (default on).
 %.lp
+\item[\ib{sortloot}]
+Controls the sorting behavior of pickup lists for inventory
+and \#loot commands and some others.
+
+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.
+%.ei
+%.ed
+%.lp
 \item[\ib{sortpack}]
 Sort the pack contents by type when displaying inventory (default on).
 %.lp
index 96dc67f9de6dfa63113f16658647951f8385e0e4..437ed23478006c207833c743fef5e7e8ff434a9c 100644 (file)
@@ -1152,6 +1152,7 @@ adopt/adapt/improve Dungeon Overview
 Aardvark Joe's Extended Logfile
 Michael Deutschmann's use_darkgray
 Clive Crous' dark_room
+Jukka Lahtinen's sortloot
 
 
 Code Cleanup and Reorganization
index f36f0243b4238c1ab02ae9deb4e2ba66d4674b3e..19f318b5568ea5faaa1e50c94747f1b99783863e 100644 (file)
@@ -857,6 +857,8 @@ E int NDECL(midnight);
 
 /* ### invent.c ### */
 
+E struct obj **FDECL(objarr_init, (int));
+E void FDECL(objarr_set, (struct obj *, int, struct obj **, BOOLEAN_P));
 E void FDECL(assigninvlet, (struct obj *));
 E struct obj *FDECL(merge_choice, (struct obj *,struct obj *));
 E int FDECL(merged, (struct obj **,struct obj **));
@@ -1523,6 +1525,7 @@ E char *FDECL(doname, (struct obj *));
 E boolean FDECL(not_fully_identified, (struct obj *));
 E char *FDECL(corpse_xname, (struct obj *,const char *,unsigned));
 E char *FDECL(cxname, (struct obj *));
+E char *FDECL(cxname_singular, (struct obj *));
 E char *FDECL(killer_xname, (struct obj *));
 E char *FDECL(short_oname, (struct obj *,char *(*)(OBJ_P),char *(*)(OBJ_P),
                            unsigned));
index 734806b382c85bc2b7cdb263380ae234c99c8031..dc58375e41fe0655797991ba0690d0e5b6fe09c3 100644 (file)
@@ -48,6 +48,7 @@ struct flag {
        boolean  showexp;       /* show experience points */
        boolean  showscore;     /* show score */
        boolean  silent;        /* whether the bell rings or not */
+       boolean  sortloot;      /* sort items alphabetically when looting */
        boolean  sortpack;      /* sorted inventory */
        boolean  sparkle;       /* show "resisting" special FX (Scott Bigham) */
        boolean  standout;      /* use standout for --More-- */
index 35a5393bd06665c7f3c0da7ac536d04c6b5d950e..42acca2bad520ffbae165338ba59e42793710f67 100644 (file)
--- a/src/end.c
+++ b/src/end.c
@@ -1237,6 +1237,9 @@ struct obj *list;
 boolean identified, all_containers, reportempty;
 {
        register struct obj *box, *obj;
+       struct obj **oarray;
+       int i,j,n;
+       char *invlet;
        char buf[BUFSZ];
        boolean cat, deadcat;
 
@@ -1256,10 +1259,30 @@ boolean identified, all_containers, reportempty;
                    continue;   /* wrong type of container */
                } else if (box->cobj) {
                    winid tmpwin = create_nhwindow(NHW_MENU);
+
+                   /* count the number of items */
+                   for (n = 0, obj = box->cobj; obj; obj = obj->nobj) n++;
+                   /* Make a temporary array to store the objects sorted */
+                   oarray = objarr_init(n);
+
+                   /* Add objects to the array */
+                   i = 0;
+                   invlet = flags.inv_order;
+               nextclass:
+                   for (obj = box->cobj; obj; obj = obj->nobj) {
+                       if (!flags.sortpack || obj->oclass == *invlet) {
+                           objarr_set(obj, i++, oarray, (flags.sortloot == 'f' || flags.sortloot == 'l') );
+                       }
+                   } /* for loop */
+                   if (flags.sortpack) {
+                       if (*++invlet) goto nextclass;
+                   }
+
                    Sprintf(buf, "Contents of %s:", the(xname(box)));
                    putstr(tmpwin, 0, buf);
                    putstr(tmpwin, 0, "");
-                   for (obj = box->cobj; obj; obj = obj->nobj) {
+                   for (i = 0; i < n; i++) {
+                       obj = oarray[i];
                        if (identified) {
                            makeknown(obj->otyp);
                            obj->known = obj->bknown =
@@ -1269,6 +1292,7 @@ boolean identified, all_containers, reportempty;
                        }
                        putstr(tmpwin, 0, doname(obj));
                    }
+                   free(oarray);
                    if (cat) putstr(tmpwin, 0, "Schroedinger's cat");
                    else if (deadcat) putstr(tmpwin, 0, "Schroedinger's dead cat");
                    display_nhwindow(tmpwin, TRUE);
index 2cc9e90044f66d7c5532dd90e12cc15d879fdfa0..8f45155519ee93c2c4c50e36b34fdccd90344d94 100644 (file)
@@ -8,6 +8,7 @@
 #define NOINVSYM       '#'
 #define CONTAINED_SYM  '>'     /* designator for inside a container */
 
+STATIC_DCL int FDECL(sortloot_cmp, (struct obj *, struct obj *));
 STATIC_DCL void NDECL(reorder_invent);
 STATIC_DCL boolean FDECL(mergable,(struct obj *,struct obj *));
 STATIC_DCL void FDECL(noarmor, (BOOLEAN_P));
@@ -42,6 +43,88 @@ static int lastinvnr = 51;   /* 0 ... 51 (never saved&restored) */
  */
 static char venom_inv[] = { VENOM_CLASS, 0 };  /* (constant) */
 
+
+int
+sortloot_cmp(obj1, obj2)
+struct obj *obj1;
+struct obj *obj2;
+{
+        int val1 = 0;
+        int val2 = 0;
+
+        /* Sort object names in lexicographical order, ignoring quantity. */
+        int name_cmp = strcmpi(cxname_singular(obj1), cxname_singular(obj2));
+
+        if (name_cmp != 0) {
+                return name_cmp;
+        }
+
+        /* Sort by BUC. Map blessed to 4, uncursed to 2, cursed to 1, and unknown to 0. */
+        val1 = obj1->bknown ? (obj1->blessed << 2) + ((!obj1->blessed && !obj1->cursed) << 1) + obj1->cursed : 0;
+        val2 = obj2->bknown ? (obj2->blessed << 2) + ((!obj2->blessed && !obj2->cursed) << 1) + obj2->cursed : 0;
+        if (val1 != val2) {
+                return val2 - val1; /* Because bigger is better. */
+        }
+
+        /* Sort by greasing. This will put the objects in degreasing order. */
+        val1 = obj1->greased;
+        val2 = obj2->greased;
+        if (val1 != val2) {
+                return val2 - val1; /* Because bigger is better. */
+        }
+
+        /* Sort by erosion. The effective amount is what matters. */
+        val1 = greatest_erosion(obj1);
+        val2 = greatest_erosion(obj2);
+        if (val1 != val2) {
+                return val1 - val2; /* Because bigger is WORSE. */
+        }
+
+        /* Sort by erodeproofing. Map known-invulnerable to 1, and both
+         * known-vulnerable and unknown-vulnerability to 0, because that's how they're displayed. */
+        val1 = obj1->rknown && obj1->oerodeproof;
+        val2 = obj2->rknown && obj2->oerodeproof;
+        if (val1 != val2) {
+                return val2 - val1; /* Because bigger is better. */
+        }
+
+        /* Sort by enchantment. Map unknown to -1000, which is comfortably below the range of ->spe. */
+        val1 = obj1->known ? obj1->spe : -1000;
+        val2 = obj2->known ? obj2->spe : -1000;
+        if (val1 != val2) {
+                return val2 - val1; /* Because bigger is better. */
+        }
+
+        return 0; /* They're identical, as far as we're concerned. */
+}
+
+struct obj **
+objarr_init(n)
+int n;
+{
+    return (struct obj **)alloc(n * sizeof(struct obj *));
+}
+
+void
+objarr_set(otmp, idx, oarray, dosort)
+struct obj *otmp;
+int idx;
+struct obj **oarray;
+boolean dosort;
+{
+    if (dosort) {
+       int j;
+       for (j = idx; j; j--) {
+           if (sortloot_cmp(otmp, oarray[j-1]) > 0) break;
+           oarray[j] = oarray[j-1];
+       }
+       oarray[j] = otmp;
+    } else {
+       oarray[idx] = otmp;
+    }
+}
+
+
 void
 assigninvlet(otmp)
 register struct obj *otmp;
@@ -1722,6 +1805,8 @@ long* out_cnt;
        static winid local_win = WIN_ERR;       /* window for partial menus */
        anything any;
        menu_item *selected;
+       struct obj **oarray;
+       int i, j;
 
        /* overriden by global flag */
        if (flags.perm_invent) {
@@ -1768,6 +1853,19 @@ long* out_cnt;
            return ret;
        }
 
+       /* count the number of items */
+       for (n = 0, otmp = invent; otmp; otmp = otmp->nobj)
+           if (!lets || !*lets || index(lets, otmp->invlet)) n++;
+
+       oarray = objarr_init(n);
+
+       /* Add objects to the array */
+       i = 0;
+       for (otmp = invent; otmp; otmp = otmp->nobj)
+           if (!lets || !*lets || index(lets, otmp->invlet)) {
+               objarr_set(otmp, i++, oarray, (flags.sortloot == 'f'));
+           }
+
        start_menu(win);
        if (wizard && iflags.override_ID) {
                char prompt[BUFSZ];
@@ -1782,22 +1880,21 @@ long* out_cnt;
 nextclass:
        classcount = 0;
        any = zeroany;          /* set all bits to zero */
-       for(otmp = invent; otmp; otmp = otmp->nobj) {
-               ilet = otmp->invlet;
-               if(!lets || !*lets || index(lets, ilet)) {
-                       any = zeroany;          /* zero */
-                       if (!flags.sortpack || otmp->oclass == *invlet) {
-                           if (flags.sortpack && !classcount) {
-                               add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
+       for(i = 0; i < n; i++) {
+           otmp = oarray[i];
+           ilet = otmp->invlet;
+           any = zeroany;              /* zero */
+           if (!flags.sortpack || otmp->oclass == *invlet) {
+               if (flags.sortpack && !classcount) {
+                   add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
                                         let_to_name(*invlet, FALSE, (want_reply && iflags.menu_head_objsym)), MENU_UNSELECTED);
-                               classcount++;
-                           }
-                           any.a_char = ilet;
-                           add_menu(win, obj_to_glyph(otmp),
-                                       &any, ilet, 0, ATR_NONE, doname(otmp),
-                                       MENU_UNSELECTED);
-                       }
+                   classcount++;
                }
+               any.a_char = ilet;
+               add_menu(win, obj_to_glyph(otmp),
+                        &any, ilet, 0, ATR_NONE, doname(otmp),
+                        MENU_UNSELECTED);
+           }
        }
        if (flags.sortpack) {
                if (*++invlet) goto nextclass;
@@ -1806,6 +1903,7 @@ nextclass:
                        goto nextclass;
                }
        }
+       free(oarray);
        end_menu(win, (char *) 0);
 
        n = select_menu(win, want_reply ? PICK_ONE : PICK_NONE, &selected);
index f40222c3b5dfbe4de805acc14d4aaa74b69862b8..25165a4b3c211c831f0d4f8eac7cf77e39bcd8b2 100644 (file)
@@ -19,6 +19,8 @@ STATIC_DCL void FDECL(add_erosion_words, (struct obj *, char *));
 STATIC_DCL boolean FDECL(singplur_lookup, (char *,char *,BOOLEAN_P,
                                           const char *const *));
 STATIC_DCL char *FDECL(singplur_compound, (char *));
+STATIC_DCL char *FDECL(xname_flags, (struct obj *, unsigned));
+
 
 struct Jitem {
        int item;
@@ -237,7 +239,15 @@ boolean juice;     /* whether or not to append " juice" to the name */
 
 char *
 xname(obj)
+struct obj *obj;
+{
+    return xname_flags(obj, CXN_NORMAL);
+}
+
+char *
+xname_flags(obj, cxn_flags)
 register struct obj *obj;
+unsigned cxn_flags;    /* bitmask of CXN_xxx values */
 {
        register char *buf;
        register int typ = obj->otyp;
@@ -246,7 +256,7 @@ register struct obj *obj;
        const char *actualn = OBJ_NAME(*ocl);
        const char *dn = OBJ_DESCR(*ocl);
        const char *un = ocl->oc_uname;
-       boolean pluralize = (obj->quan != 1L);
+       boolean pluralize = (obj->quan != 1L) && !(cxn_flags & CXN_SINGULAR);
        boolean known, dknown, bknown;
 
        buf = nextobuf() + PREFIX;      /* leave room for "17 -3 " */
@@ -1066,6 +1076,16 @@ struct obj *obj;
        return xname(obj);
 }
 
+/* like cxname, but ignores quantity */
+char *
+cxname_singular(obj)
+struct obj *obj;
+{
+       if (obj->otyp == CORPSE)
+           return corpse_xname(obj, (const char *)0, CXN_SINGULAR);
+       return xname_flags(obj, CXN_SINGULAR);
+}
+
 /* treat an object as fully ID'd when it might be used as reason for death */
 char *
 killer_xname(obj)
index 1b834e61694bbd9b73ea7a62123c847c4f03e6ed..c65f3db28cf2abc1756a258c2ddb95e4eff8954f 100644 (file)
@@ -347,6 +347,7 @@ static struct Comp_Opt
        { "scroll_amount", "amount to scroll map when scroll_margin is reached",
                                                20, DISP_IN_GAME }, /*WC*/
        { "scroll_margin", "scroll map when this far from the edge", 20, DISP_IN_GAME }, /*WC*/
+       { "sortloot", "sort object selection lists by description", 4, SET_IN_GAME },
 #ifdef MSDOS
        { "soundcard", "type of sound card to use", 20, SET_IN_FILE },
 #endif
@@ -659,6 +660,7 @@ initoptions_init()
                     (genericptr_t)def_inv_order, sizeof flags.inv_order);
        flags.pickup_types[0] = '\0';
        flags.pickup_burden = MOD_ENCUMBER;
+       flags.sortloot = 'l'; /* sort only loot by default */
 
        for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++)
                flags.end_disclose[i] = DISCLOSE_PROMPT_DEFAULT_NO;
@@ -2360,6 +2362,22 @@ goodfruit:
            return;
        }
 
+       fullname = "sortloot";
+       if (match_optname(opts, fullname, 4, TRUE)) {
+               op = string_for_env_opt(fullname, opts, FALSE);
+               if (op) {
+                       switch (tolower(*op)) {
+                        case 'n':
+                        case 'l':
+                        case 'f': flags.sortloot = tolower(*op);
+                               break;
+                        default:  badoption(opts);
+                               return;
+                       }
+               }
+               return;
+       }
+
        fullname = "suppress_alert";
        if (match_optname(opts, fullname, 4, TRUE)) {
                if (duplicate) complain_about_duplicate(opts,1);
@@ -2896,6 +2914,10 @@ static NEARDATA const char *runmodes[] = {
        "teleport", "run", "walk", "crawl"
 };
 
+static NEARDATA const char *sortltype[] = {
+       "none", "loot", "full"
+};
+
 /*
  * Convert the given string of object classes to a string of default object
  * symbols.
@@ -3204,7 +3226,8 @@ boolean setinitial,setfromfile;
     char buf[BUFSZ];
 
     /* Special handling of menustyle, pickup_burden, pickup_types,
-     * disclose, runmode, msg_window, menu_headings, and number_pad options.
+     * disclose, runmode, msg_window, menu_headings, sortloot,
+     * and number_pad options.
      * Also takes care of interactive autopickup_exception_handling changes.
      */
     if (!strcmp("menustyle", optname)) {
@@ -3385,6 +3408,23 @@ boolean setinitial,setfromfile;
        }
        destroy_nhwindow(tmpwin);
 #endif
+    } else if (!strcmp("sortloot", optname)) {
+       const char *sortl_name;
+       menu_item *sortl_pick = (menu_item *)0;
+       tmpwin = create_nhwindow(NHW_MENU);
+       start_menu(tmpwin);
+       for (i = 0; i < SIZE(sortltype); i++) {
+           sortl_name = sortltype[i];
+           any.a_char = *sortl_name;
+           add_menu(tmpwin, NO_GLYPH, &any, *sortl_name, 0,
+                    ATR_NONE, sortl_name, MENU_UNSELECTED);
+       }
+       end_menu(tmpwin, "Select loot sorting type:");
+       if (select_menu(tmpwin, PICK_ONE, &sortl_pick) > 0) {
+           flags.sortloot = sortl_pick->item.a_char;
+           free((genericptr_t)sortl_pick);
+       }
+       destroy_nhwindow(tmpwin);
     } else if (!strcmp("align_message", optname) ||
                !strcmp("align_status", optname)) {
        menu_item *window_pick = (menu_item *)0;
@@ -3974,6 +4014,15 @@ char *buf;
                if (iflags.wc_scroll_margin) Sprintf(buf, "%d",iflags.wc_scroll_margin);
                else Strcpy(buf, defopt);
        }
+       else if (!strcmp(optname, "sortloot")) {
+               char *sortname = (char *)NULL;
+               for (i=0; i < SIZE(sortltype) && sortname==(char *)NULL; i++) {
+                  if (flags.sortloot == sortltype[i][0])
+                    sortname = (char *)sortltype[i];
+               }
+               if (sortname != (char *)NULL)
+                  Sprintf(buf, "%s", sortname);
+       }
        else if (!strcmp(optname, "player_selection"))
                Sprintf(buf, "%s", iflags.wc_player_selection ? "prompts" : "dialog");
 #ifdef MSDOS
index bf5d0612918837e2f5a54c3483c0ebbb9ad24f7e..ff8bcff257f8e453a46e7b4ad6b9c2311950d2b8 100644 (file)
@@ -708,9 +708,10 @@ menu_item **pick_list;             /* return list of items picked */
 int how;                       /* type of query */
 boolean FDECL((*allow), (OBJ_P));/* allow function */
 {
-       int n;
+       int i, j, n;
        winid win;
        struct obj *curr, *last, fake_hero_object;
+       struct obj **oarray;
        char *pack;
        anything any;
        boolean printed_type_name,
@@ -743,6 +744,16 @@ boolean FDECL((*allow), (OBJ_P));/* allow function */
            return 1;
        }
 
+       oarray = objarr_init(n);
+       /* Add objects to the array */
+       i = 0;
+       for (curr = olist; curr; curr = FOLLOW(curr, qflags)) {
+           if ((*allow)(curr)) {
+               objarr_set(curr, i++, oarray, (flags.sortloot == 'f' ||
+                                              (flags.sortloot == 'l' && !(qflags & USE_INVLET))));
+           }
+       }
+
        win = create_nhwindow(NHW_MENU);
        start_menu(win);
        any = zeroany;
@@ -756,7 +767,8 @@ boolean FDECL((*allow), (OBJ_P));/* allow function */
        pack = flags.inv_order;
        do {
            printed_type_name = FALSE;
-           for (curr = olist; curr; curr = FOLLOW(curr, qflags)) {
+           for (i = 0; i < n; i++) {
+               curr = oarray[i];
                if ((qflags & FEEL_COCKATRICE) && curr->otyp == CORPSE &&
                     will_feel_cockatrice(curr, FALSE)) {
                        destroy_nhwindow(win);  /* stop the menu and revert */
@@ -784,6 +796,7 @@ boolean FDECL((*allow), (OBJ_P));/* allow function */
            }
            pack++;
        } while (sorted && *pack);
+       free(oarray);
 
        if (engulfer) {
            char buf[BUFSZ];