]> granicus.if.org Git - nethack/commitdiff
Accessibility: Pick travel/cursor targets from a menu
authorPasi Kallinen <paxed@alt.org>
Thu, 6 Oct 2016 10:03:56 +0000 (13:03 +0300)
committerPasi Kallinen <paxed@alt.org>
Thu, 6 Oct 2016 10:10:13 +0000 (13:10 +0300)
Adds two new configurable keys to the cursor targeting: 'A' (getpos.menu)
and 'a' (getpos.menu.cansee). First one shows a menu of all interesting
glyphs on the map, second one shows only those in sight.

Travel command also now obeys the "request menu" -prefix, showing
the menu with interesting targets in sight, and then traveling there.

Idea via the NetHack accessibility research by Alexei Pepers.

doc/Guidebook.mn
doc/Guidebook.tex
include/extern.h
include/flag.h
include/rm.h
src/cmd.c
src/do_name.c

index 87cfb992f6788e5a7b579d686b8ffb016459cd76..171388b6ee7f3adfcc2b5a265ae4329f6b72219b 100644 (file)
@@ -586,7 +586,8 @@ A few other commands (eat food, offer sacrifice, apply tinning-kit) use
 the `m' prefix to skip checking for applicable objects on the floor
 and go straight to checking inventory,
 or (for ``#loot'' to remove a saddle),
-skip containers and go straight to adjacent monsters.
+skip containers and go straight to adjacent monsters. The prefix will
+make ``#travel'' command show a menu of interesting targets in sight.
 .lp F[yuhjklbn]
 Prefix:  fight a monster (even if you only guess one is there).
 .lp M[yuhjklbn]
@@ -1119,6 +1120,8 @@ Tip over a container (bag or box) to pour out its contents.
 Autocompletes. Default key is 'M-T'.
 .lp #travel
 Travel to a specific location on the map. Default key is '_'.
+Using the ``request menu'' prefix shows a menu of interesting targets in sight
+without asking to move the cursor.
 .lp #turn
 Turn undead away. Autocompletes. Default key is 'M-t'.
 .lp #twoweapon
@@ -3212,6 +3215,10 @@ When asked for a location, the key to go to previous closest monster. Default is
 When asked for a location, the key to go to next closest object. Default is 'o'.
 .lp getpos.obj.prev
 When asked for a location, the key to go to previous closest object. Default is 'O'.
+.lp getpos.menu
+When asked for a location, show a menu of all interesting targets. Default is 'A'.
+.lp getpos.menu.cansee
+When asked for a location, show a menu of interesting targets in view. Default is 'a'.
 .lp getpos.pick
 When asked for a location, the key to choose the location, and possibly ask for more info. Default is '.'.
 .lp getpos.pick.once
index 12db38c20aaf4054eb3f68b73f72275cf4a91c46..5ea6a5679561aee59ab650554ff79feaeae2e4e1 100644 (file)
@@ -706,7 +706,8 @@ A few other commands (eat food, offer sacrifice, apply tinning-kit) use
 the `{\tt m}' prefix to skip checking for applicable objects on the floor
 and go straight to checking inventory,
 or (for ``{\tt \#loot}'' to remove a saddle),
-skip containers and go straight to adjacent monsters.
+skip containers and go straight to adjacent monsters. The prefix will
+make ``{\tt \#travel}'' command show a menu of interesting targets in sight.
 %.lp
 \item[\tb{F[yuhjklbn]}]
 Prefix:  fight a monster (even if you only guess one is there).
@@ -1386,6 +1387,8 @@ Autocompletes. Default key is '{\tt M-T}'.
 %.lp
 \item[\tb{\#travel}]
 Travel to a specific location on the map. Default key is '{\tt _}'.
+Using the ``request menu'' prefix shows a menu of interesting targets in sight
+without asking to move the cursor.
 %.lp
 \item[\tb{\#turn}]
 Turn undead away. Autocompletes. Default key is '{\tt M-t}'.
@@ -3935,6 +3938,12 @@ When asked for a location, the key to go to next closest object. Default is ``{\
 \item{\bb{getpos.obj.prev}}
 When asked for a location, the key to go to previous closest object. Default is ``{\tt O}''.
 %.lp
+\item{\bb{getpos.menu}}
+When asked for a location, show a menu of all interesting targets. Default is '{\tt A}'.
+%.lp
+\item{\bb{getpos.menu.cansee}}
+When asked for a location, show a menu of interesting targets in view. Default is '{\tt a}'.
+%.lp
 \item{\bb{getpos.pick}}
 When asked for a location, the key to choose the location, and possibly ask for more info. Default is ``{\tt .}''.
 %.lp
index 451b8b1d218e6a7489dc99de0975b64c6d33985c..a0781a876a01b25f9d42519749b5a7e44911b220 100644 (file)
@@ -379,6 +379,7 @@ E void NDECL(heal_legs);
 /* ### do_name.c ### */
 
 E char *FDECL(coord_desc, (int, int, char *, CHAR_P));
+E boolean FDECL(getpos_menu, (coord *, boolean));
 E int FDECL(getpos, (coord *, BOOLEAN_P, const char *));
 E void FDECL(getpos_sethilite, (void (*f)(int)));
 E void FDECL(new_mname, (struct monst *, int));
index bd34fed496dc27da4fd32bb2a6b4c21dbeb82a4d..642c680be03fb2baadcec6dd31e8fc971dcc33af 100644 (file)
@@ -468,6 +468,8 @@ enum nh_keyfunc {
     NHKF_GETPOS_UNEX_NEXT,
     NHKF_GETPOS_UNEX_PREV,
     NHKF_GETPOS_HELP,
+    NHKF_GETPOS_MENU,
+    NHKF_GETPOS_MENU_FOV,
 
     NUM_NHKF
 };
index 21f5036f79cbc0f8f6199f9facc9d24f6751489a..3e85f2e2dbd6045007bb3fe028965e0edb39e38c 100644 (file)
 #define is_cmap_trap(i) ((i) >= S_arrow_trap && (i) <= S_polymorph_trap)
 #define is_cmap_drawbridge(i) ((i) >= S_vodbridge && (i) <= S_hcdbridge)
 #define is_cmap_door(i) ((i) >= S_vodoor && (i) <= S_hcdoor)
+#define is_cmap_wall(i) ((i) >= S_stone && (i) <= S_trwall)
 
 struct symdef {
     uchar sym;
index e2c89b6b1a07c8059faafad3c2df7daba28c885f..3735b95d04266bdd4fa4e6d589fcc614e07bc60a 100644 (file)
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -3500,7 +3500,9 @@ struct {
     { NHKF_GETPOS_DOOR_PREV, 'D', "getpos.door.prev" },
     { NHKF_GETPOS_UNEX_NEXT, 'x', "getpos.unexplored.next" },
     { NHKF_GETPOS_UNEX_PREV, 'X', "getpos.unexplored.prev" },
-    { NHKF_GETPOS_HELP,      '?', "getpos.help" }
+    { NHKF_GETPOS_HELP,      '?', "getpos.help" },
+    { NHKF_GETPOS_MENU,      'A', "getpos.menu" },
+    { NHKF_GETPOS_MENU_FOV,  'a', "getpos.menu.cansee" }
 };
 
 boolean
@@ -3763,6 +3765,8 @@ int NDECL((*cmd_func));
         /* 'm' for removing saddle from adjacent monster without checking
            for containers at <u.ux,u.uy> */
         || cmd_func == doloot
+        /* travel: pop up a menu of interesting targets in view */
+        || cmd_func == dotravel
         /* 'm' prefix allowed for some extended commands */
         || cmd_func == doextcmd || cmd_func == doextlist)
         return TRUE;
@@ -4665,11 +4669,18 @@ dotravel(VOID_ARGS)
         cc.y = u.uy;
     }
     iflags.getloc_travelmode = TRUE;
-    pline("Where do you want to travel to?");
-    if (getpos(&cc, TRUE, "the desired destination") < 0) {
-        /* user pressed ESC */
-        iflags.getloc_travelmode = FALSE;
-        return 0;
+    if (iflags.menu_requested) {
+        if (!getpos_menu(&cc, TRUE)) {
+            iflags.getloc_travelmode = FALSE;
+            return 0;
+        }
+    } else {
+        pline("Where do you want to travel to?");
+        if (getpos(&cc, TRUE, "the desired destination") < 0) {
+            /* user pressed ESC */
+            iflags.getloc_travelmode = FALSE;
+            return 0;
+        }
     }
     iflags.getloc_travelmode = FALSE;
     iflags.travelcc.x = u.tx = cc.x;
index 5c470265ab2e754e23f8f161df356a91c371f0f9..9560071a01d1b7b5231e871f4094b8aa0257b799 100644 (file)
@@ -88,6 +88,12 @@ const char *goal;
                 visctrl(Cmd.spkeys[NHKF_GETPOS_UNEX_PREV]));
         putstr(tmpwin, 0, sbuf);
     }
+    Sprintf(sbuf, "Use '%s' for a menu of interesting targets in view.",
+            visctrl(Cmd.spkeys[NHKF_GETPOS_MENU_FOV]));
+    putstr(tmpwin, 0, sbuf);
+    Sprintf(sbuf, "Use '%s' for a menu of all interesting targets.",
+            visctrl(Cmd.spkeys[NHKF_GETPOS_MENU]));
+    putstr(tmpwin, 0, sbuf);
     if (!iflags.terrainmode) {
         char kbuf[BUFSZ];
         if (getpos_hilitefunc) {
@@ -174,7 +180,10 @@ enum gloctypes {
     GLOC_DOOR,
     GLOC_EXPLORE,
 
-    NUM_GLOCS
+    NUM_GLOCS,
+
+    GLOC_INTERESTING,
+    GLOC_INTERESTING_FOV
 };
 
 
@@ -222,6 +231,23 @@ int x,y, gloc;
                     || IS_UNEXPLORED_LOC(x - 1, y)
                     || IS_UNEXPLORED_LOC(x, y + 1)
                     || IS_UNEXPLORED_LOC(x, y - 1)));
+    case GLOC_INTERESTING_FOV:
+        if (!cansee(x,y))
+            return FALSE;
+    case GLOC_INTERESTING:
+        return gather_locs_interesting(x,y, GLOC_DOOR)
+            || !(glyph_is_cmap(glyph)
+                 && (is_cmap_wall(glyph_to_cmap(glyph))
+                     || glyph_to_cmap(glyph) == S_tree
+                     || glyph_to_cmap(glyph) == S_ice
+                     || glyph_to_cmap(glyph) == S_air
+                     || glyph_to_cmap(glyph) == S_cloud
+                     || (glyph_to_cmap(glyph) == S_water && Is_waterlevel(&u.uz))
+                     || glyph_to_cmap(glyph) == S_ndoor
+                     || glyph_to_cmap(glyph) == S_room
+                     || glyph_to_cmap(glyph) == S_darkroom
+                     || glyph_to_cmap(glyph) == S_corr
+                     || glyph_to_cmap(glyph) == S_litcorr));
     }
     /*NOTREACHED*/
     return FALSE;
@@ -370,6 +396,61 @@ int cx, cy;
     }
 }
 
+boolean
+getpos_menu(ccp, fovonly)
+coord *ccp;
+boolean fovonly;
+{
+    coord *garr = DUMMY;
+    int gcount = 0;
+    winid tmpwin;
+    anything any;
+    int i, pick_cnt;
+    menu_item *picks = (menu_item *) 0;
+    char tmpbuf[BUFSZ];
+
+    gather_locs(&garr, &gcount,
+                fovonly ? GLOC_INTERESTING_FOV : GLOC_INTERESTING);
+    if (gcount < 2) { /* gcount always includes the hero */
+        You("cannot %s anything interesting.", fovonly ? "see" : "detect");
+        return FALSE;
+    }
+
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin);
+    any = zeroany;
+
+    for (i = 0; i < gcount; i++) {
+        char fullbuf[BUFSZ];
+        coord tmpcc;
+        const char *firstmatch = "unknown";
+        int sym = 0;
+        any.a_int = i + 1;
+        tmpcc.x = garr[i].x;
+        tmpcc.y = garr[i].y;
+        if (do_screen_description(tmpcc, TRUE, sym, tmpbuf, &firstmatch)) {
+            (void) coord_desc(garr[i].x, garr[i].y, tmpbuf, iflags.getpos_coords);
+            Sprintf(fullbuf, "%s%s%s", firstmatch, (*tmpbuf ? " " : ""), tmpbuf);
+            add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, fullbuf,
+                     MENU_UNSELECTED);
+        }
+    }
+
+    Sprintf(tmpbuf, "Pick a target%s%s",
+            fovonly ? " in view" : "",
+            iflags.getloc_travelmode ? " for travel" : "");
+    end_menu(tmpwin, tmpbuf);
+    pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
+    destroy_nhwindow(tmpwin);
+    if (pick_cnt > 0) {
+        ccp->x = garr[picks->item.a_int - 1].x;
+        ccp->y = garr[picks->item.a_int - 1].y;
+        free((genericptr_t) picks);
+    }
+    free((genericptr_t) garr);
+    return (pick_cnt > 0);
+}
+
 int
 getpos(ccp, force, goal)
 coord *ccp;
@@ -533,6 +614,14 @@ const char *goal;
                 show_goal_msg = TRUE;
             msg_given = TRUE;
             goto nxtc;
+        } else if (c == Cmd.spkeys[NHKF_GETPOS_MENU]
+                   || c == Cmd.spkeys[NHKF_GETPOS_MENU_FOV]) {
+            coord tmpcrd;
+            if (getpos_menu(&tmpcrd, (c == Cmd.spkeys[NHKF_GETPOS_MENU_FOV]))) {
+                cx = tmpcrd.x;
+                cy = tmpcrd.y;
+            }
+            goto nxtc;
         } else if (c == Cmd.spkeys[NHKF_GETPOS_SELF]) {
             /* reset 'm&M', 'o&O', &c; otherwise, there's no way for player
                to achieve that except by manually cycling through all spots */