From: Pasi Kallinen Date: Thu, 6 Oct 2016 10:03:56 +0000 (+0300) Subject: Accessibility: Pick travel/cursor targets from a menu X-Git-Tag: NetHack-3.6.1_RC01~588 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=efd7526194b972b0db5886e0f9e7bd8084e95c52;p=nethack Accessibility: Pick travel/cursor targets from a menu 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. --- diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 87cfb992f..171388b6e 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -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 diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 12db38c20..5ea6a5679 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -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 diff --git a/include/extern.h b/include/extern.h index 451b8b1d2..a0781a876 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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)); diff --git a/include/flag.h b/include/flag.h index bd34fed49..642c680be 100644 --- a/include/flag.h +++ b/include/flag.h @@ -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 }; diff --git a/include/rm.h b/include/rm.h index 21f5036f7..3e85f2e2d 100644 --- a/include/rm.h +++ b/include/rm.h @@ -230,6 +230,7 @@ #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; diff --git a/src/cmd.c b/src/cmd.c index e2c89b6b1..3735b95d0 100644 --- 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 */ || 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; diff --git a/src/do_name.c b/src/do_name.c index 5c470265a..9560071a0 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -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 */