]> granicus.if.org Git - nethack/commitdiff
Change special keys into extended commands
authorPasi Kallinen <paxed@alt.org>
Sat, 15 Jan 2022 23:22:17 +0000 (01:22 +0200)
committerPasi Kallinen <paxed@alt.org>
Sun, 16 Jan 2022 12:48:24 +0000 (14:48 +0200)
Changes most of the special keys used in the main input loop
into extended commands:

- movement keys are now bound to extended commands, eg.
  #movewest and so on.

- m-prefix is now #reqmenu extended command, still bound to
  the 'm' key.

- run, rush, and fight are now extended commands, still bound
  to the same keys as previously.

- nopickup and runnopickup keys are removed.
  Nopickup was using 'm' key, the same as the m-prefix, so
  allow #reqmenu to modify movement commands to disable pickup.

- multiple prefix commands are allowed. This lets user to
  use #reqmenu, followed by #run, followed by movement to simulate
  runnopickup behaviour. (If necessary, adding runnopickup back
  as an extended command would be easy)

doc/Guidebook.mn
doc/Guidebook.tex
doc/fixes37.0
include/decl.h
include/extern.h
include/func_tab.h
src/cmd.c
src/dig.c
src/do.c
src/do_name.c

index 10acbf05c0f84fafe929bc9790425f7f93ba53e9..2058f33a5d7ebd9043b42d55db7972d1d1bf478b 100644 (file)
@@ -1245,6 +1245,11 @@ To really switch to explore mode, respond with \f(CRy\fP.
 You can set the
 .op paranoid_confirmation:quit
 option to require a response of \f(CRyes\fP instead.
+.lp "#fight   "
+Prefix key to force fight a direction, even if you see nothing
+to fight there.
+Default key is \(oqF\(cq, or \(oq\-\(cq with
+.op number_pad
 .lp "#fire    "
 Fire ammunition from quiver, possibly autowielding a launcher,
 or hit with a wielded polearm.
@@ -1439,6 +1444,10 @@ is on.
 .lp "#remove  "
 Remove an accessory (ring, amulet, etc).
 Default key is \(oqR\(cq.
+.lp "#reqmenu "
+Prefix key to modify the behaviour or request menu from some commands.
+Prevents autopickup when used with movement commands.
+Default key is \(oqm\(cq.
 .lp "#retravel"
 Travel to a previously selected travel destination.
 Default key is \(oqC-_\(cq.
@@ -1451,6 +1460,24 @@ Default key is \(oqM-R\(cq.
 Rub a lamp or a stone.
 Autocompletes.
 Default key is \(oqM-r\(cq.
+.lp "#run     "
+Prefix key to run towards a direction.
+Default key is \(oqG\(cq when
+.op number_pad
+is off,
+\(oq5\(cq when
+.op number_pad
+is set to 1 or 3,
+otherwise \(oqM-5\(cq when it is set to 2 or 4.
+.lp "#rush    "
+Prefix key to rush towards a direction.
+Default is \(oqg\(cq when
+.op number_pad
+is off,
+\(oqM-5\(cq when
+.op number_pad
+is set to 1 or 3,
+otherwise \(oq5\(cq when it is set to 2 or 4.
 .lp "#save    "
 Save the game and exit the program.
 Default key is \(oqS\(cq.
@@ -4775,15 +4802,6 @@ With
 .op number_pad
 only.
 Default is \(oqn\(cq.
-.lp fight
-Prefix key to force fight a direction.
-Default is \(oqF\(cq.
-.lp fight.numpad
-Prefix key to force fight a direction.
-With
-.op number_pad
-only.
-Default is \(oq\-\(cq.
 .lp getdir.help
 When asked for a direction, the key to show the help.
 Default is \(oq?\(cq.
@@ -4874,49 +4892,9 @@ Default is \(oqz\(cq.
 .lp getpos.valid.prev
 When asked for a location, the key to go to previous closest valid location.
 Default is \(oqZ\(cq.
-.lp nopickup
-Prefix key to move without picking up items.
-Default is \(oqm\(cq.
-.lp redraw
-Key to redraw the screen.
-Default is \(oq\(haR\(cq.
-.lp redraw.numpad
-Key to redraw the screen.
-With
-.op number_pad
-only.
-Default is \(oq\(haL\(cq.
 .lp repeat
 Key to repeat previous command.
 Default is \(oq\(haA\(cq.
-.lp reqmenu
-Prefix key to request menu from some commands.
-Default is \(oqm\(cq.
-.lp run
-Prefix key to run towards a direction.
-Default is \(oqG\(cq.
-.lp run.nopickup
-Prefix key to run towards a direction without picking up items on the way.
-Default is \(oqM\(cq.
-.lp run.numpad
-Prefix key to run towards a direction.
-With
-.op number_pad
-only.
-.lp ""
-Default is \(oq5\(cq when number_pad is set to 1 or 3,
-otherwise \(oqM-5\(cq when it is set to 2 or 4.
-.lp rush
-Prefix key to rush towards a direction.
-Default is \(oqg\(cq.
-.lp rush.numpad
-Prefix key to rush towards a direction.
-With
-.op number_pad
-only.
-.lp ""
-Default is \(oqM-5\(cq when number_pad is set to 1 or 3,
-otherwise \(oq5\(cq when it is set to 2 or 4.
 .hn 2
 Configuring Message Types
 .pg
index 5b7d7a6a3141bf6404967c0c6656643bfcad07de..84fde4b6e506e4a0d2415d34caf514fb063e4c99 100644 (file)
@@ -1349,6 +1349,12 @@ You can set the
 {\it paranoid\verb+_+confirmation:quit\/}
 option to require a response of ``{\tt yes}'' instead.
 %.lp
+\item[\tb{\#fight}]
+Prefix key to force fight a direction, even if you see nothing
+to fight there.
+Default key is `{\tt F}', or `{\tt -}' with
+{\it number\verb+_+pad\/}
+%.lp
 \item[\tb{\#fire}]
 Fire ammunition from quiver, possibly autowielding a launcher,
 or hit with a wielded polearm.
@@ -1546,6 +1552,11 @@ and also `{\tt \^{}L}' if {\it number\verb+_+pad\/} is on.
 \item[\tb{\#remove}]
 Remove an accessory (ring, amulet, etc). Default key is `{\tt R}'.
 %.lp
+\item[\tb{\#reqmenu}]
+Prefix key to modify the behaviour or request menu from some commands.
+Prevents autopickup when used with movement commands.
+Default key is `{\tt m}'.
+%.lp
 \item[\tb{\#retravel}]
 Travel to a previously selected travel destination.
 Default key is `{\tt C-_}'.
@@ -1558,6 +1569,26 @@ Default key is `{\tt M-R}'.
 \item[\tb{\#rub}]
 Rub a lamp or a stone. Autocompletes. Default key is `{\tt M-r}'.
 %.lp
+\item[\tb{\#run}]
+Prefix key to run towards a direction.
+Default key is `{\tt G}' when
+{\it number\verb+_+pad\/}
+is off,
+`{\tt 5}' when
+{\it number\verb+_+pad\/}
+is set to 1~or~3,
+otherwise `{\tt M-5}' when it is set to 2~or~4.
+%.lp
+\item[\tb{\#rush}]
+Prefix key to rush towards a direction.
+Default key is `{\tt g}' when
+{\it number\verb+_+pad\/}
+is off,
+`{\tt M-5}' when
+{\it number\verb+_+pad\/}
+is set to 1~or~3,
+otherwise `{\tt 5}' when it is set to 2~or~4.
+%.lp
 \item[\tb{\#save}]
 Save the game and exit the program.
 Default key is `{\tt S}'.
@@ -5265,13 +5296,6 @@ can only be bound to a single key.
 Prefix key to start a count, to repeat a command this many times.
 With {\it number\verb+_+pad\/} only. Default is~`{\tt n}'.
 %.lp
-\item[{\bb{fight}}]
-Prefix key to force fight a direction. Default is~`{\tt F}'.
-%.lp
-\item[{\bb{fight.numpad}}]
-Prefix key to force fight a direction. With {\it number\verb+_+pad\/} only.
-Default is~`{\tt -}'.
-%.lp
 \item[{\bb{getdir.help}}]
 When asked for a direction, the key to show the help. Default is~`{\tt ?}'.
 %.lp
@@ -5375,47 +5399,8 @@ Default is~`{\tt z}'.
 When asked for a location, the key to go to previous closest valid location.
 Default is~`{\tt Z}'.
 %.lp
-\item[{\bb{nopickup}}]
-Prefix key to move without picking up items. Default is~`{\tt m}'.
-%.lp
-\item[{\bb{redraw}}]
-Key to redraw the screen. Default is~`{\tt \^{}R}'.
-%.lp
-\item[{\bb{redraw.numpad}}]
-Key to redraw the screen. With {\it number\verb+_+pad\/} only.
-Default is~`{\tt \^{}L}'.
-%.lp
 \item[{\bb{repeat}}]
 Key to repeat previous command. Default is~`{\tt \^{}A}'.
-%.lp
-\item[{\bb{reqmenu}}]
-Prefix key to request menu from some commands. Default is~`{\tt m}'.
-%.lp
-\item[{\bb{run}}]
-Prefix key to run towards a direction. Default is~`{\tt G}'.
-%.lp
-\item[{\bb{run.nopickup}}]
-Prefix key to run towards a direction without picking up items on the way.
-Default is~`{\tt M}'.
-%.lp
-\item[{\bb{run.numpad}}]
-Prefix key to run towards a direction.
-With {\it number\verb+_+pad\/} only.
-
-%.lp ""
-Default is `{\tt 5}' when number_pad is set to 1~or~3,
-otherwise `{\tt M-5}' when it is set to 2~or~4.
-%.lp
-\item[{\bb{rush}}]
-Prefix key to rush towards a direction. Default is~`{\tt g}'.
-%.lp
-\item[{\bb{rush.numpad}}]
-Prefix key to rush towards a direction.
-With {\it number\verb+_+pad\/} only.
-
-.lp ""
-Default is `{\tt M-5}' when number_pad is set to 1~or~3,
-otherwise `{\tt 5}' when it is set to 2~or~4.
 \elist
 
 
index 852e277be064a10915f2832f66e2e78fe3a601b0..03e2e32122ba510d18c113e23c02dd4808ca1f6e 100644 (file)
@@ -747,6 +747,7 @@ when two or more shops share a wall and hero uses Passes_walls to carry an
        unpaid item from the first shop wasn't noticed
 redo the unpaid_cost fix to handle shop items inside hero-owned container
 flyers shouldn't fall on arrival when going down holes or trap doors
+change movement keys and some special keys into extended commands
 
 
 Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
index 392c64fef63ea6873de3cdcbbcc059035df88618..36aa590e9d44dff593fa5a9599604812111a5fe8 100644 (file)
@@ -445,20 +445,6 @@ enum nh_keyfunc {
     NHKF_ESC = 0,
     NHKF_DOAGAIN,
 
-    NHKF_REQMENU,
-
-    /* run ... clicklook need to be in a continuous block */
-    NHKF_RUN,          /* 'G' */
-    NHKF_RUN2,         /* '5' or M-5 */
-    NHKF_RUSH,         /* 'g' */
-    NHKF_RUSH2,        /* M-5 or '5' */
-    NHKF_FIGHT,        /* 'F' */
-    NHKF_FIGHT2,       /* '-' */
-    NHKF_NOPICKUP,     /* 'm' */
-    NHKF_RUN_NOPICKUP, /* 'M' */
-
-    NHKF_REDRAW,
-    NHKF_REDRAW2,
     NHKF_GETDIR_SELF,
     NHKF_GETDIR_SELF2,
     NHKF_GETDIR_HELP,
@@ -503,9 +489,6 @@ struct cmd {
     boolean pcHack_compat; /* for numpad:  affects 5, M-5, and M-0 */
     boolean phone_layout;  /* inverted keypad:  1,2,3 above, 7,8,9 below */
     boolean swap_yz;       /* QWERTZ keyboards; use z to move NW, y to zap */
-    char move[N_DIRS];     /* char used for moving one step in direction */
-    char rush[N_DIRS];
-    char run[N_DIRS];
     const char *dirchars;      /* current movement/direction characters */
     const char *alphadirchars; /* same as dirchars if !numpad */
     const struct ext_func_tab *commands[256]; /* indexed by input character */
index f890f7d843d46ba0b3321d6c1cadbd818ebb24a4..9bcc56ae96c76f0a0bec472b68cc941e664a0007 100644 (file)
@@ -199,12 +199,41 @@ extern boolean status_hilite_menu(void);
 
 /* ### cmd.c ### */
 
+extern int do_move_west(void);
+extern int do_move_northwest(void);
+extern int do_move_north(void);
+extern int do_move_northeast(void);
+extern int do_move_east(void);
+extern int do_move_southeast(void);
+extern int do_move_south(void);
+extern int do_move_southwest(void);
+extern int do_rush_west(void);
+extern int do_rush_northwest(void);
+extern int do_rush_north(void);
+extern int do_rush_northeast(void);
+extern int do_rush_east(void);
+extern int do_rush_southeast(void);
+extern int do_rush_south(void);
+extern int do_rush_southwest(void);
+extern int do_run_west(void);
+extern int do_run_northwest(void);
+extern int do_run_north(void);
+extern int do_run_northeast(void);
+extern int do_run_east(void);
+extern int do_run_southeast(void);
+extern int do_run_south(void);
+extern int do_run_southwest(void);
+extern int do_reqmenu(void);
+extern int do_rush(void);
+extern int do_run(void);
+extern int do_fight(void);
 extern char randomkey(void);
 extern void random_response(char *, int);
 extern int rnd_extcmd_idx(void);
 extern int domonability(void);
 extern const struct ext_func_tab *ext_func_tab_from_func(int(*)(void));
 extern char cmd_from_func(int(*)(void));
+extern char cmd_from_dir(int, int);
 extern const char *cmdname_from_func(int(*)(void), char *, boolean);
 extern boolean redraw_cmd(char);
 extern const char *levltyp_to_name(int);
index ca2c07c3d3fdd716e6b9557269a718b5041d4d3d..da2b3cca9d1c6d26ce86af2ca0db89c4831af85a 100644 (file)
@@ -15,6 +15,8 @@
 #define NOFUZZERCMD  0x20 /* fuzzer cannot execute this command */
 #define INTERNALCMD  0x40 /* only for internal use, not for user */
 #define CMD_M_PREFIX 0x80 /* accepts menu prefix */
+#define PREFIXCMD    0x100 /* prefix command, requires another one after it */
+#define MOVEMENTCMD  0x200 /* used to move hero/cursor */
 
 struct ext_func_tab {
     uchar key;
index 4fbb6b2a6876a0f5a3f526910c49c824e8b68a0e..703af9bc79bdb4cf925764be4bd3d54adce32f67 100644 (file)
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -106,6 +106,7 @@ static int dotravel(void);
 static int dotravel_target(void);
 static int doclicklook(void);
 static int doterrain(void);
+static void set_move_cmd(int, int);
 static int wiz_wish(void);
 static int wiz_identify(void);
 static int wiz_map(void);
@@ -156,11 +157,10 @@ static char *parse(void);
 static void show_direction_keys(winid, char, boolean);
 static boolean help_dir(char, int, const char *);
 
+static boolean bind_key_fn(uchar, int (*)(void));
 static void commands_init(void);
 static boolean keylist_func_has_key(const struct ext_func_tab *, boolean *);
 static int keylist_putcmds(winid, boolean, int, int, boolean *);
-static int ch2spkeys(char, int, int);
-static boolean prefix_cmd(char);
 static const char *spkey_name(int);
 
 static int (*timed_occ_fn)(void);
@@ -389,7 +389,7 @@ doextcmd(void)
             return 0;
         if (iflags.menu_requested && !accept_menu_prefix(&extcmdlist[idx])) {
             pline("'%s' prefix has no effect for the %s command.",
-                  visctrl(g.Cmd.spkeys[NHKF_REQMENU]),
+                  visctrl(cmd_from_func(do_reqmenu)),
                   extcmdlist[idx].ef_txt);
             iflags.menu_requested = FALSE;
         }
@@ -416,7 +416,7 @@ doc_extcmd_flagstr(winid menuwin,
         add_menu(menuwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE,
                  "[A] Command autocompletes", MENU_ITEMFLAGS_NONE);
         Sprintf(qbuf, "[m] Command accepts '%s' prefix",
-                visctrl(g.Cmd.spkeys[NHKF_REQMENU]));
+                visctrl(cmd_from_func(do_reqmenu)));
         add_menu(menuwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, qbuf,
                  MENU_ITEMFLAGS_NONE);
         return (char *) 0;
@@ -1845,6 +1845,228 @@ doterrain(void)
     return ECMD_OK; /* no time elapses */
 }
 
+static void
+set_move_cmd(int dir, int run)
+{
+    /* #reqmenu -prefix disables autopickup during movement */
+    if (iflags.menu_requested)
+        g.context.nopick = 1;
+    g.context.travel = g.context.travel1 = 0;
+    if (!g.domove_attempting) {
+        g.context.run = run;
+        g.domove_attempting |= (!run ? DOMOVE_WALK : DOMOVE_RUSH);
+    }
+    u.dz = zdir[dir];
+    u.dx = xdir[dir];
+    u.dy = ydir[dir];
+}
+
+/* move or attack */
+int
+do_move_west(void)
+{
+    set_move_cmd(DIR_W, 0);
+    return ECMD_TIME;
+}
+
+int
+do_move_northwest(void)
+{
+    set_move_cmd(DIR_NW, 0);
+    return ECMD_TIME;
+}
+
+int
+do_move_north(void)
+{
+    set_move_cmd(DIR_N, 0);
+    return ECMD_TIME;
+}
+
+int
+do_move_northeast(void)
+{
+    set_move_cmd(DIR_NE, 0);
+    return ECMD_TIME;
+}
+
+int
+do_move_east(void)
+{
+    set_move_cmd(DIR_E, 0);
+    return ECMD_TIME;
+}
+
+int
+do_move_southeast(void)
+{
+    set_move_cmd(DIR_SE, 0);
+    return ECMD_TIME;
+}
+
+int
+do_move_south(void)
+{
+    set_move_cmd(DIR_S, 0);
+    return ECMD_TIME;
+}
+
+int
+do_move_southwest(void)
+{
+    set_move_cmd(DIR_SW, 0);
+    return ECMD_TIME;
+}
+
+/* rush */
+int
+do_rush_west(void)
+{
+    set_move_cmd(DIR_W, 1);
+    return ECMD_TIME;
+}
+
+int
+do_rush_northwest(void)
+{
+    set_move_cmd(DIR_NW, 1);
+    return ECMD_TIME;
+}
+
+int
+do_rush_north(void)
+{
+    set_move_cmd(DIR_N, 1);
+    return ECMD_TIME;
+}
+
+int
+do_rush_northeast(void)
+{
+    set_move_cmd(DIR_NE, 1);
+    return ECMD_TIME;
+}
+
+int
+do_rush_east(void)
+{
+    set_move_cmd(DIR_E, 1);
+    return ECMD_TIME;
+}
+
+int
+do_rush_southeast(void)
+{
+    set_move_cmd(DIR_SE, 1);
+    return ECMD_TIME;
+}
+
+int
+do_rush_south(void)
+{
+    set_move_cmd(DIR_S, 1);
+    return ECMD_TIME;
+}
+
+int
+do_rush_southwest(void)
+{
+    set_move_cmd(DIR_SW, 1);
+    return ECMD_TIME;
+}
+
+/* run */
+int
+do_run_west(void)
+{
+    set_move_cmd(DIR_W, 3);
+    return ECMD_TIME;
+}
+
+int
+do_run_northwest(void)
+{
+    set_move_cmd(DIR_NW, 3);
+    return ECMD_TIME;
+}
+
+int
+do_run_north(void)
+{
+    set_move_cmd(DIR_N, 3);
+    return ECMD_TIME;
+}
+
+int
+do_run_northeast(void)
+{
+    set_move_cmd(DIR_NE, 3);
+    return ECMD_TIME;
+}
+
+int
+do_run_east(void)
+{
+    set_move_cmd(DIR_E, 3);
+    return ECMD_TIME;
+}
+
+int
+do_run_southeast(void)
+{
+    set_move_cmd(DIR_SE, 3);
+    return ECMD_TIME;
+}
+
+int
+do_run_south(void)
+{
+    set_move_cmd(DIR_S, 3);
+    return ECMD_TIME;
+}
+
+int
+do_run_southwest(void)
+{
+    set_move_cmd(DIR_SW, 3);
+    return ECMD_TIME;
+}
+
+/* #reqmenu, prefix command to modify some others */
+int
+do_reqmenu(void)
+{
+    iflags.menu_requested = TRUE;
+    return ECMD_OK;
+}
+
+/* #rush */
+int
+do_rush(void)
+{
+    g.context.run = 2;
+    g.domove_attempting |= DOMOVE_RUSH;
+    return ECMD_OK;
+}
+
+/* #run */
+int
+do_run(void)
+{
+    g.context.run = 3;
+    g.domove_attempting |= DOMOVE_RUSH;
+    return ECMD_OK;
+}
+
+/* #fight */
+int
+do_fight(void)
+{
+    g.context.forcefight = 1;
+    g.domove_attempting |= DOMOVE_WALK;
+    return ECMD_OK;
+}
+
 /* extcmdlist: full command list, ordered by command name;
    commands with no keystroke or with only a meta keystroke generally
    need to be flagged as autocomplete and ones with a regular keystroke
@@ -1893,6 +2115,8 @@ struct ext_func_tab extcmdlist[] = {
        impact frequently used #enhance by making #e become ambiguous */
     { M('X'), "exploremode", "enter explore (discovery) mode",
               enter_explore_mode, IFBURIED | GENERALCMD | NOFUZZERCMD, NULL },
+    { 'F',    "fight", "prefix: force fight even if you don't see a monster",
+              do_fight, PREFIXCMD, NULL },
     { 'f',    "fire", "fire ammunition from quiver",
               dofire, 0, NULL },
     { M('f'), "force", "force a lock",
@@ -1975,12 +2199,18 @@ struct ext_func_tab extcmdlist[] = {
               doredraw, IFBURIED | GENERALCMD, NULL },
     { 'R',    "remove", "remove an accessory (ring, amulet, etc)",
               doremring, 0, NULL },
+    { 'm',    "reqmenu", "prefix: request menu or modify command",
+              do_reqmenu, PREFIXCMD, NULL },
     { C('_'), "retravel", "travel to previously selected travel location",
               dotravel_target, 0, NULL },
     { M('R'), "ride", "mount or dismount a saddled steed",
               doride, AUTOCOMPLETE, NULL },
     { M('r'), "rub", "rub a lamp or a stone",
               dorub, AUTOCOMPLETE, NULL },
+    { 'G',    "run", "prefix: run until something interesting is seen",
+              do_run, PREFIXCMD, NULL },
+    { 'g',    "rush", "prefix: rush until something interesting is seen",
+              do_rush, PREFIXCMD, NULL },
     { 'S',    "save", "save the game and exit",
               dosave, IFBURIED | GENERALCMD | NOFUZZERCMD, NULL },
     { 's',    "search", "search for traps and secret doors",
@@ -2116,66 +2346,98 @@ struct ext_func_tab extcmdlist[] = {
               wiz_show_wmodes, IFBURIED | AUTOCOMPLETE | WIZMODECMD, NULL },
     { 'z',    "zap", "zap a wand",
               dozap, 0, NULL },
+    /* movement commands will be bound by reset_commands() */
+    /* move or attack */
+    { '\0', "movewest", "move west (screen left)",
+            do_move_west, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "movenorthwest", "move northwest (screen upper left)",
+            do_move_northwest, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "movenorth", "move north (screen up)",
+            do_move_north, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "movenortheast", "move northeast (screen upper right)",
+            do_move_northeast, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "moveeast", "move east (screen right)",
+            do_move_east, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "movesoutheast", "move southeast (screen lower right)",
+            do_move_southeast, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "movesouth", "move south (screen down)",
+            do_move_south, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "movesouthwest", "move southwest (screen lower left)",
+            do_move_southwest, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    /* rush */
+    { '\0', "rushwest", "rush west (screen left)",
+            do_rush_west, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "rushnorthwest", "rush northwest (screen upper left)",
+            do_rush_northwest, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "rushnorth", "rush north (screen up)",
+            do_rush_north, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "rushnortheast", "rush northeast (screen upper right)",
+            do_rush_northeast, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "rusheast", "rush east (screen right)",
+            do_rush_east, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "rushsoutheast", "rush southeast (screen lower right)",
+            do_rush_southeast, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "rushsouth", "rush south (screen down)",
+            do_rush_south, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "rushsouthwest", "rush southwest (screen lower left)",
+            do_rush_southwest, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    /* run */
+    { '\0', "runwest", "run west (screen left)",
+            do_run_west, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "runnorthwest", "run northwest (screen upper left)",
+            do_run_northwest, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "runnorth", "run north (screen up)",
+            do_run_north, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "runnortheast", "run northeast (screen upper right)",
+            do_run_northeast, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "runeast", "run east (screen right)",
+            do_run_east, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "runsoutheast", "run southeast (screen lower right)",
+            do_run_southeast, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "runsouth", "run south (screen down)",
+            do_run_south, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+    { '\0', "runsouthwest", "run southwest (screen lower left)",
+            do_run_southwest, MOVEMENTCMD | CMD_M_PREFIX, NULL },
+
     /* internal commands: only used by game core, not available for user */
     { '\0',   "clicklook", NULL, doclicklook, INTERNALCMD, NULL },
     { '\0', (char *) 0, (char *) 0, donull, 0, (char *) 0 } /* sentinel */
 };
 
-/* used by dokeylist() and by key2extcmdesc() for dowhatdoes() */
-static const char
-    run_desc[] = "Prefix: run until something very interesting is seen",
-    rush_desc[] = "Prefix: rush until something interesting is seen",
-    forcefight_desc[] = "Prefix: force fight even if you don't see a monster";
+/* mapping direction and move mode to extended command function */
+static int (*move_funcs[N_DIRS_Z][N_MOVEMODES])(void) = {
+    { do_move_west,      do_run_west,      do_rush_west },
+    { do_move_northwest, do_run_northwest, do_rush_northwest },
+    { do_move_north,     do_run_north,     do_rush_north },
+    { do_move_northeast, do_run_northeast, do_rush_northeast },
+    { do_move_east,      do_run_east,      do_rush_east },
+    { do_move_southeast, do_run_southeast, do_rush_southeast },
+    { do_move_south,     do_run_south,     do_rush_south },
+    { do_move_southwest, do_run_southwest, do_rush_southwest },
+    { dodown,            dodown,           dodown },
+    { doup,              doup,             doup },
+};
 
+/* used by dokeylist() and by key2extcmdesc() for dowhatdoes() */
 static const struct {
     int nhkf;
     const char *desc;
     boolean numpad;
 } misc_keys[] = {
     { NHKF_ESC, "cancel current prompt or pending prefix", FALSE },
-    { NHKF_RUSH, rush_desc, FALSE },
-    { NHKF_RUSH2, rush_desc, TRUE },
-    { NHKF_RUN, run_desc, FALSE },
-    { NHKF_RUN2, run_desc, TRUE },
-    { NHKF_FIGHT, forcefight_desc, FALSE },
-    { NHKF_FIGHT2, forcefight_desc, TRUE } ,
-    { NHKF_NOPICKUP,
-      "Prefix: move without picking up objects or fighting", FALSE },
-    { NHKF_RUN_NOPICKUP,
-      "Prefix: run without picking up objects or fighting", FALSE },
-    { NHKF_REQMENU,
-      "Prefix: request a menu (for some non-movement commands)", FALSE },
     { NHKF_COUNT,
       "Prefix: for digits when preceding a command with a count", TRUE },
     { NHKF_DOAGAIN , "repeat: perform the previous command again", FALSE },
     { 0, (const char *) 0, FALSE }
 };
 
-/* for key2extcmddesc() to support dowhatdoes() */
-struct movcmd {
-    uchar k1, k2, k3, k4; /* 'normal', 'qwertz', 'numpad', 'phone' */
-    const char *txt, *alt; /* compass direction, screen direction */
-};
-static const struct movcmd movtab[] = {
-    { 'h', 'h', '4', '4', "west",      "left" },
-    { 'j', 'j', '2', '8', "south",     "down" },
-    { 'k', 'k', '8', '2', "north",     "up" },
-    { 'l', 'l', '6', '6', "east",      "right" },
-    { 'b', 'b', '1', '7', "southwest", "lower left" },
-    { 'n', 'n', '3', '9', "southeast", "lower right" },
-    { 'u', 'u', '9', '3', "northeast", "upper right" },
-    { 'y', 'z', '7', '1', "northwest", "upper left" },
-    {   0,   0,   0,   0,  (char *) 0, (char *) 0 }
-};
-
 int extcmdlist_length = SIZE(extcmdlist) - 1;
 
 const char *
 key2extcmddesc(uchar key)
 {
     static char key2cmdbuf[QBUFSZ];
-    const struct movcmd *mov;
-    int k, c, i, j;
+    int k, i, j;
     uchar M_5 = (uchar) M('5'), M_0 = (uchar) M('0');
 
     /* need to check for movement commands before checking the extended
@@ -2188,17 +2450,7 @@ key2extcmddesc(uchar key)
         Strcpy(key2cmdbuf, "rush");
     else if (movecmd(k = key, MV_RUN))
         Strcpy(key2cmdbuf, "run");
-    if (*key2cmdbuf) {
-        for (mov = &movtab[0]; mov->k1; ++mov) {
-            c = !g.Cmd.num_pad ? (!g.Cmd.swap_yz ? mov->k1 : mov->k2)
-                             : (!g.Cmd.phone_layout ? mov->k3 : mov->k4);
-            if (c == k) {
-                Sprintf(eos(key2cmdbuf), " %s (screen %s)",
-                        mov->txt, mov->alt);
-                return key2cmdbuf;
-            }
-        }
-    } else if (digit(key) || (g.Cmd.num_pad && digit(unmeta(key)))) {
+    if (digit(key) || (g.Cmd.num_pad && digit(unmeta(key)))) {
         key2cmdbuf[0] = '\0';
         if (!g.Cmd.num_pad)
             Strcpy(key2cmdbuf, "start of, or continuation of, a count");
@@ -2261,6 +2513,24 @@ bind_key(uchar key, const char *command)
     return FALSE;
 }
 
+/* bind key by ext cmd function */
+static boolean
+bind_key_fn(uchar key, int (*fn)(void))
+{
+    struct ext_func_tab *extcmd;
+
+    for (extcmd = extcmdlist; extcmd->ef_txt; extcmd++) {
+        if (extcmd->ef_funct != fn)
+            continue;
+        if ((extcmd->flags & INTERNALCMD) != 0)
+            continue;
+        g.Cmd.commands[key] = extcmd;
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
 /* initialize all keyboard commands */
 static void
 commands_init(void)
@@ -2271,24 +2541,23 @@ commands_init(void)
         if (extcmd->key)
             g.Cmd.commands[extcmd->key] = extcmd;
 
-    (void) bind_key(C('l'), "redraw"); /* if number_pad is set */
-    /*       'b', 'B' : go sw */
-    /*       'F' : fight (one time) */
-    /*       'g', 'G' : multiple go */
-    /*       'h', 'H' : go west */
-    (void) bind_key('h',    "help"); /* if number_pad is set */
-    (void) bind_key('j',    "jump"); /* if number_pad is on */
-    /*       'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' move commands */
-    (void) bind_key('k',    "kick"); /* if number_pad is on */
-    (void) bind_key('l',    "loot"); /* if number_pad is on */
-    (void) bind_key(C('n'), "annotate"); /* if number_pad is on */
-    (void) bind_key(M('n'), "name");
-    (void) bind_key(M('N'), "name");
-    (void) bind_key('u',    "untrap"); /* if number_pad is on */
+    /* number_pad */
+    (void) bind_key(C('l'), "redraw");
+    (void) bind_key('h',    "help");
+    (void) bind_key('j',    "jump");
+    (void) bind_key('k',    "kick");
+    (void) bind_key('l',    "loot");
+    (void) bind_key(C('n'), "annotate");
+    (void) bind_key('u',    "untrap");
+    (void) bind_key('5',    "run");
+    (void) bind_key(M('5'), "rush");
+    (void) bind_key('-',    "fight");
 
     /* alt keys: */
     (void) bind_key(M('O'), "overview");
     (void) bind_key(M('2'), "twoweapon");
+    (void) bind_key(M('n'), "name");
+    (void) bind_key(M('N'), "name");
 
     /* wait_on_space */
     (void) bind_key(' ',    "wait");
@@ -2381,11 +2650,6 @@ dokeylist(void)
     (void) memset((genericptr_t) keys_used, 0, sizeof keys_used);
     (void) memset((genericptr_t) pfx_seen, 0, sizeof pfx_seen);
 
-    for (i = 0; i < N_DIRS; i++) {
-        keys_used[(uchar) g.Cmd.move[i]] = TRUE;
-        keys_used[(uchar) g.Cmd.rush[i]] = TRUE;
-        keys_used[(uchar) g.Cmd.run[i]] = TRUE;
-    }
 #ifndef NO_SIGNAL
     /* this is actually ambiguous; tty raw mode will override SIGINT;
        when enabled, treat it like a movement command since assigning
@@ -2404,10 +2668,9 @@ dokeylist(void)
             continue;
         j = misc_keys[i].nhkf;
         key = (uchar) g.Cmd.spkeys[j];
-        if (key && !mov_seen[key] && (!pfx_seen[key] || j == NHKF_REQMENU)) {
+        if (key && !mov_seen[key] && !pfx_seen[key]) {
             keys_used[key] = TRUE;
-            if (j != NHKF_REQMENU)
-                pfx_seen[key] = j;
+            pfx_seen[key] = j;
         } else
             spkey_gap = TRUE;
     }
@@ -2455,7 +2718,7 @@ dokeylist(void)
         j = misc_keys[i].nhkf;
         key = (uchar) g.Cmd.spkeys[j];
         if (key && !mov_seen[key]
-            && (pfx_seen[key] == j || j == NHKF_REQMENU)) {
+            && (pfx_seen[key] == j)) {
             Sprintf(buf, "%-7s %s", key2txt(key, buf2), misc_keys[i].desc);
             putstr(datawin, 0, buf);
         }
@@ -2479,7 +2742,7 @@ dokeylist(void)
                 continue;
             j = misc_keys[i].nhkf;
             key = (uchar) g.Cmd.spkeys[j];
-            if (!key || (pfx_seen[key] != j && j != NHKF_REQMENU)) {
+            if (!key || (pfx_seen[key] != j)) {
                 Sprintf(buf2, "[%s]", spkey_name(j));
                 /* lines up with the other unassigned commands which use
                    "#%-20s ", but not with the other special keys */
@@ -2490,24 +2753,23 @@ dokeylist(void)
         }
     }
 
+#define IGNORECMD (WIZMODECMD | INTERNALCMD | MOVEMENTCMD)
+
     putstr(datawin, 0, "");
     show_menu_controls(datawin, TRUE);
 
-    if (keylist_putcmds(datawin, TRUE, GENERALCMD,
-                        WIZMODECMD | INTERNALCMD, keys_used)) {
+    if (keylist_putcmds(datawin, TRUE, GENERALCMD, IGNORECMD, keys_used)) {
         putstr(datawin, 0, "");
         putstr(datawin, 0, "General commands:");
         (void) keylist_putcmds(datawin, FALSE, GENERALCMD,
-                               WIZMODECMD | INTERNALCMD,
-                                 keys_used);
+                               IGNORECMD, keys_used);
     }
 
-    if (keylist_putcmds(datawin, TRUE, 0,
-                        GENERALCMD | WIZMODECMD | INTERNALCMD, keys_used)) {
+    if (keylist_putcmds(datawin, TRUE, 0, GENERALCMD | IGNORECMD, keys_used)) {
         putstr(datawin, 0, "");
         putstr(datawin, 0, "Game commands:");
         (void) keylist_putcmds(datawin, FALSE, 0,
-                               GENERALCMD | WIZMODECMD | INTERNALCMD,
+                               GENERALCMD | IGNORECMD,
                                keys_used);
     }
 
@@ -2521,6 +2783,7 @@ dokeylist(void)
 
     display_nhwindow(datawin, FALSE);
     destroy_nhwindow(datawin);
+#undef IGNORECMD
 }
 
 const struct ext_func_tab *
@@ -2535,6 +2798,13 @@ ext_func_tab_from_func(int (*fn)(void))
     return NULL;
 }
 
+/* returns the key bound to a movement command for given DIR_ and MV_ mode */
+char
+cmd_from_dir(int dir, int mode)
+{
+    return cmd_from_func(move_funcs[dir][mode]);
+}
+
 char
 cmd_from_func(int (*fn)(void))
 {
@@ -3036,17 +3306,6 @@ static struct {
 } const spkeys_binds[] = {
     { NHKF_ESC,              '\033', (char *) 0 }, /* no binding */
     { NHKF_DOAGAIN,          DOAGAIN, "repeat" },
-    { NHKF_REQMENU,          'm', "reqmenu" },
-    { NHKF_RUN,              'G', "run" },
-    { NHKF_RUN2,             '5', "run.numpad" },
-    { NHKF_RUSH,             'g', "rush" },
-    { NHKF_RUSH2,            M('5'), "rush.numpad" },
-    { NHKF_FIGHT,            'F', "fight" },
-    { NHKF_FIGHT2,           '-', "fight.numpad" },
-    { NHKF_NOPICKUP,         'm', "nopickup" },
-    { NHKF_RUN_NOPICKUP,     'M', "run.nopickup" },
-    { NHKF_REDRAW,           C('r'), "redraw" },
-    { NHKF_REDRAW2,          C('l'), "redraw.numpad" },
     { NHKF_GETDIR_SELF,      '.', "getdir.self" },
     { NHKF_GETDIR_SELF2,     's', "getdir.self2" },
     { NHKF_GETDIR_HELP,      '?', "getdir.help" },
@@ -3182,11 +3441,13 @@ reset_commands(boolean initial)
     static const int ylist[] = {
         'y', 'Y', C('y'), M('y'), M('Y'), M(C('y'))
     };
-    static struct ext_func_tab *back_dir_cmd[N_DIRS];
+    static struct ext_func_tab *back_dir_cmd[N_DIRS_Z][N_MOVEMODES];
+    static uchar back_dir_key[N_DIRS_Z][N_MOVEMODES];
     static boolean backed_dir_cmd = FALSE;
     const struct ext_func_tab *cmdtmp;
     boolean flagtemp;
     int c, i, updated = 0;
+    int dir, mode;
 
     if (initial) {
         updated = 1;
@@ -3197,8 +3458,10 @@ reset_commands(boolean initial)
         commands_init();
     } else {
         if (backed_dir_cmd) {
-            for (i = 0; i < N_DIRS; i++) {
-                g.Cmd.commands[(uchar) g.Cmd.dirchars[i]] = back_dir_cmd[i];
+            for (dir = 0; dir < N_DIRS_Z; dir++) {
+                for (mode = 0; mode < N_MOVEMODES; mode++) {
+                    g.Cmd.commands[back_dir_key[dir][mode]] = back_dir_cmd[dir][mode];
+                }
             }
         }
 
@@ -3235,10 +3498,6 @@ reset_commands(boolean initial)
             cmdtmp = g.Cmd.commands['5'];
             g.Cmd.commands['5'] = g.Cmd.commands[c];
             g.Cmd.commands[c] = cmdtmp;
-#else
-            c = g.Cmd.spkeys[NHKF_RUN2];
-            g.Cmd.spkeys[NHKF_RUN2] = g.Cmd.spkeys[NHKF_RUSH2];
-            g.Cmd.spkeys[NHKF_RUSH2] = c;
 #endif
             /* FIXME: NHKF_DOINV2 ought to be implemented instead of this */
             c = M('0') & 0xff;
@@ -3263,6 +3522,7 @@ reset_commands(boolean initial)
         }
     } /*?initial*/
 
+    /* choose updated movement keys */
     if (updated)
         g.Cmd.serialno++;
     g.Cmd.dirchars = !g.Cmd.num_pad
@@ -3270,31 +3530,39 @@ reset_commands(boolean initial)
                        : (!g.Cmd.phone_layout ? ndir : ndir_phone_layout);
     g.Cmd.alphadirchars = !g.Cmd.num_pad ? g.Cmd.dirchars : sdir;
 
-    for (i = 0; i < N_DIRS; i++) {
-        g.Cmd.move[i] = g.Cmd.dirchars[i];
-        if (!g.Cmd.num_pad) {
-            g.Cmd.run[i]  = highc(g.Cmd.move[i]);
-            g.Cmd.rush[i] = C(g.Cmd.move[i]);
-        } else {
-            g.Cmd.run[i]  = M(g.Cmd.move[i]);
-            g.Cmd.rush[i] = M(g.Cmd.move[i]);
+    /* back up the commands & keys overwritten by new movement keys */
+    for (dir = 0; dir < N_DIRS_Z; dir++) {
+        for (mode = MV_WALK; mode < N_MOVEMODES; mode++) {
+            uchar di = (uchar) g.Cmd.dirchars[dir];
+
+            if (!g.Cmd.num_pad) {
+                if (mode == MV_RUN) di = highc(di);
+                else if (mode == MV_RUSH) di = C(di);
+            } else {
+                if (mode == MV_RUN) di = M(di);
+                else if (mode == MV_RUSH) di = M(di);
+            }
+            back_dir_key[dir][mode] = di;
+            back_dir_cmd[dir][mode] = (struct ext_func_tab *) g.Cmd.commands[di];
+            g.Cmd.commands[di] = (struct ext_func_tab *) 0;
         }
     }
+    backed_dir_cmd = TRUE;
 
-    if (!initial) {
-        for (i = 0; i < N_DIRS; i++) {
-            uchar di = (uchar) g.Cmd.dirchars[i];
-
-            back_dir_cmd[i] = (struct ext_func_tab *) g.Cmd.commands[di];
-            g.Cmd.commands[di] = (struct ext_func_tab *) 0;
+    /* bind the new keys to movement commands */
+    for (i = 0; i < N_DIRS; i++) {
+        (void) bind_key_fn(g.Cmd.dirchars[i], move_funcs[i][MV_WALK]);
+        if (!g.Cmd.num_pad) {
+            (void) bind_key_fn(highc(g.Cmd.dirchars[i]), move_funcs[i][MV_RUN]);
+            (void) bind_key_fn(C(g.Cmd.dirchars[i]), move_funcs[i][MV_RUSH]);
+        } else {
+            (void) bind_key_fn(M(g.Cmd.dirchars[i]), move_funcs[i][MV_RUN]);
+            (void) bind_key_fn(M(g.Cmd.dirchars[i]), move_funcs[i][MV_RUSH]);
         }
-        backed_dir_cmd = TRUE;
-        for (i = 0; i < N_DIRS; i++)
-            (void) bind_key(g.Cmd.dirchars[i], "nothing");
     }
 }
 
-/* non-movement commands which accept 'm' prefix to request menu operation */
+/* commands which accept 'm' prefix to request menu operation */
 static boolean
 accept_menu_prefix(const struct ext_func_tab *ec)
 {
@@ -3340,10 +3608,9 @@ randomkey(void)
     case 12:
         {
             int d = rn2(N_DIRS);
-            if (!rn2(7))
-                c = !rn2(3) ? g.Cmd.rush[d] : g.Cmd.run[d];
-            else
-                c = g.Cmd.move[d];
+            int m = rn2(7) ? MV_WALK : (!rn2(3) ? MV_RUSH : MV_RUN);
+
+            c = cmd_from_dir(d, m);
         }
         break;
     case 13:
@@ -3384,17 +3651,6 @@ rnd_extcmd_idx(void)
     return rn2(extcmdlist_length + 1) - 1;
 }
 
-static int
-ch2spkeys(char c, int start, int end)
-{
-    int i;
-
-    for (i = start; i <= end; i++)
-        if (g.Cmd.spkeys[i] == c)
-            return i;
-    return NHKF_ESC;
-}
-
 void
 rhack(char *cmd)
 {
@@ -3405,6 +3661,9 @@ rhack(char *cmd)
     const struct ext_func_tab *cmdq_ec = NULL;
 
     iflags.menu_requested = FALSE;
+    prefix_seen = FALSE;
+    g.context.nopick = 0;
+got_prefix_input:
 #ifdef SAFERHANGUP
     if (g.program_state.done_hup)
         end_of_input();
@@ -3425,11 +3684,12 @@ rhack(char *cmd)
         if (cmdq_ec)
             goto do_cmdq_extcmd;
     } else if (firsttime) {
-        g.context.nopick = 0;
         cmd = parse();
     }
+
     if (*cmd == g.Cmd.spkeys[NHKF_ESC]) {
         g.context.move = FALSE;
+        iflags.menu_requested = FALSE;
         return;
     }
     /* DOAGAIN might be '\0'; if so, don't execute it even if *cmd is too */
@@ -3439,151 +3699,21 @@ rhack(char *cmd)
         g.stail = 0;
         rhack((char *) 0); /* read and execute command */
         g.in_doagain = FALSE;
+        iflags.menu_requested = FALSE;
         return;
     }
     /* Special case of *cmd == ' ' handled better below */
     if (!*cmd || *cmd == (char) 0377) {
         nhbell();
         g.context.move = FALSE;
+        iflags.menu_requested = FALSE;
         return; /* probably we just had an interrupt */
     }
 
     /* handle most movement commands */
-    prefix_seen = FALSE;
     g.context.travel = g.context.travel1 = 0;
-    spkey = ch2spkeys(*cmd, NHKF_RUN, NHKF_RUN_NOPICKUP);
-
-    switch (spkey) {
-    case NHKF_RUSH2:
-        if (!g.Cmd.num_pad)
-            break;
-        /*FALLTHRU*/
-    case NHKF_RUSH:
-        if (movecmd(cmd[1], MV_ANY)) {
-            g.context.run = 2;
-            g.domove_attempting |= DOMOVE_RUSH;
-        } else
-            prefix_seen = TRUE;
-        break;
-    case NHKF_RUN2:
-        if (!g.Cmd.num_pad)
-            break;
-        /*FALLTHRU*/
-    case NHKF_RUN:
-        if (movecmd(cmd[1], MV_ANY)) {
-            g.context.run = 3;
-            g.domove_attempting |= DOMOVE_RUSH;
-        } else
-            prefix_seen = TRUE;
-        break;
-    case NHKF_FIGHT2:
-        if (!g.Cmd.num_pad)
-            break;
-        /*FALLTHRU*/
-    /* Effects of movement commands and invisible monsters:
-     * m: always move onto space (even if 'I' remembered)
-     * F: always attack space (even if 'I' not remembered)
-     * normal movement: attack if 'I', move otherwise.
-     */
-    case NHKF_FIGHT:
-        if (movecmd(cmd[1], MV_ANY)) {
-            g.context.forcefight = 1;
-            g.domove_attempting |= DOMOVE_WALK;
-        } else
-            prefix_seen = TRUE;
-        break;
-    case NHKF_NOPICKUP:
-        if (movecmd(cmd[1], MV_ANY) || u.dz) {
-            g.context.run = 0;
-            g.context.nopick = 1;
-            if (!u.dz)
-                g.domove_attempting |= DOMOVE_WALK;
-            else
-                cmd[0] = cmd[1]; /* "m<" or "m>" */
-        } else
-            prefix_seen = TRUE;
-        break;
-    case NHKF_RUN_NOPICKUP:
-        if (movecmd(cmd[1], MV_ANY)) {
-            g.context.run = 1;
-            g.context.nopick = 1;
-            g.domove_attempting |= DOMOVE_RUSH;
-        } else
-            prefix_seen = TRUE;
-        break;
-    default:
-        if (movecmd(*cmd, MV_WALK)) { /* ordinary movement */
-            g.context.run = 0; /* only matters here if it was 8 */
-            g.domove_attempting |= DOMOVE_WALK;
-        } else if (movecmd(*cmd, MV_RUN)) {
-            g.context.run = 1;
-            g.domove_attempting |= DOMOVE_RUSH;
-        } else if (movecmd(*cmd, MV_RUSH)) {
-            g.context.run = 3;
-            g.domove_attempting |= DOMOVE_RUSH;
-        }
-        break;
-    }
 
-    /* after movement--if reqmenu duplicates a prefix, movement takes
-       precedence; "request a menu" (default 'm') */
-    if (cmd[0] == g.Cmd.spkeys[NHKF_REQMENU]) {
-        /* (for func_tab cast, see below) */
-        const struct ext_func_tab *ft = g.Cmd.commands[cmd[1] & 0xff];
-        int (*func)(void) = ft ? ((struct ext_func_tab *) ft)->ef_funct : 0;
-
-        if (func && accept_menu_prefix(ft)) {
-            iflags.menu_requested = TRUE;
-            ++cmd;
-            prefix_seen = FALSE;
-        } else {
-            prefix_seen = TRUE;
-        }
-    }
-
-    if (((g.domove_attempting & (DOMOVE_RUSH | DOMOVE_WALK)) != 0L)
-                            && !g.context.travel && !dxdy_moveok()) {
-        /* trying to move diagonally as a grid bug;
-           this used to be treated by movecmd() as not being
-           a movement attempt, but that didn't provide for any
-           feedback and led to strangeness if the key pressed
-           ('u' in particular) was overloaded for num_pad use */
-        You_cant("get there from here...");
-        g.context.run = 0;
-        g.context.nopick = g.context.forcefight = FALSE;
-        g.context.move = g.context.mv = FALSE;
-        g.multi = 0;
-        return;
-    }
-
-    if ((g.domove_attempting & DOMOVE_WALK) != 0L) {
-        if (g.multi)
-            g.context.mv = TRUE;
-        domove();
-        g.context.forcefight = 0;
-        return;
-    } else if ((g.domove_attempting & DOMOVE_RUSH) != 0L) {
-        if (firsttime) {
-            if (!g.multi)
-                g.multi = max(COLNO, ROWNO);
-            u.last_str_turn = 0;
-        }
-        g.context.mv = TRUE;
-        domove();
-        return;
-    } else if (prefix_seen) {
-        if (cmd[1] == g.Cmd.spkeys[NHKF_ESC]) {
-            /* <prefix><escape> */
-            /* don't report "unknown command" for change of heart... */
-            bad_command = FALSE;
-        } else { /* prefix followed by non-movement command */
-            bad_command = TRUE; /* skip cmdlist[] loop */
-        }
-    } else if (*cmd == ' ' && !flags.rest_on_space) {
-        bad_command = TRUE; /* skip cmdlist[] loop */
-
-    /* handle all other commands */
-    } else {
+    {
         register const struct ext_func_tab *tlist;
         int res, (*func)(void);
 
@@ -3598,6 +3728,11 @@ rhack(char *cmd)
             if (!can_do_extcmd(tlist)) {
                 res = ECMD_OK;
                 cmdq_clear();
+            } else if (prefix_seen && !accept_menu_prefix(tlist)
+                       && !(tlist->flags & PREFIXCMD)) {
+                /* we got a prefix previously, can this command accept one? */
+                res = ECMD_OK;
+                cmdq_clear();
             } else {
                 /* we discard 'const' because some compilers seem to have
                    trouble with the pointer passed to set_occupation() */
@@ -3605,13 +3740,50 @@ rhack(char *cmd)
                 if (tlist->f_text && !g.occupation && g.multi)
                     set_occupation(func, tlist->f_text, g.multi);
                 res = (*func)(); /* perform the command */
+
+                if ((tlist->flags & PREFIXCMD)) {
+                    /* it was a prefix command, mark and get another command */
+                    prefix_seen = TRUE;
+                    bad_command = FALSE;
+                    goto got_prefix_input;
+                } else if (((g.domove_attempting & (DOMOVE_RUSH | DOMOVE_WALK)) != 0L)
+                            && !g.context.travel && !dxdy_moveok()) {
+                    /* trying to move diagonally as a grid bug */
+                    You_cant("get there from here...");
+                    g.context.run = 0;
+                    g.context.nopick = g.context.forcefight = FALSE;
+                    g.context.move = g.context.mv = FALSE;
+                    g.multi = 0;
+                    iflags.menu_requested = FALSE;
+                    return;
+                } else if ((g.domove_attempting & DOMOVE_WALK) != 0L) {
+                    if (g.multi)
+                        g.context.mv = TRUE;
+                    domove();
+                    g.context.forcefight = 0;
+                    iflags.menu_requested = FALSE;
+                    return;
+                } else if ((g.domove_attempting & DOMOVE_RUSH) != 0L) {
+                    if (firsttime) {
+                        if (!g.multi)
+                            g.multi = max(COLNO, ROWNO);
+                        u.last_str_turn = 0;
+                    }
+                    g.context.mv = TRUE;
+                    domove();
+                    iflags.menu_requested = FALSE;
+                    return;
+                }
+                prefix_seen = FALSE;
             }
             if ((res & ECMD_CANCEL)) {
                 /* command was canceled by user, maybe they declined to
                    pick an object to act on. */
+                iflags.menu_requested = FALSE;
                 cmdq_clear();
             }
             if (!(res & ECMD_TIME)) {
+                iflags.menu_requested = FALSE;
                 g.context.move = FALSE;
                 g.multi = 0;
             }
@@ -3667,26 +3839,22 @@ movecmd(char sym, int mode)
 {
     int d = DIR_ERR;
 
-    if (g.Cmd.commands[(uchar)sym]
-        && g.Cmd.commands[(uchar)sym]->ef_funct == dodown) {
-        d = DIR_DOWN;
-    } else if (g.Cmd.commands[(uchar)sym]
-               && g.Cmd.commands[(uchar)sym]->ef_funct == doup) {
-        d = DIR_UP;
-    } else {
-        char *mvkeys = (mode == MV_WALK) ? g.Cmd.move :
-            ((mode == MV_RUN) ? g.Cmd.run : g.Cmd.rush);
-
-        for (d = N_DIRS - 1; d > DIR_ERR; d--) {
-            if (mode == MV_ANY) {
-                if (sym == g.Cmd.move[d]
-                    || sym == g.Cmd.rush[d]
-                    || sym == g.Cmd.run[d])
+    if (g.Cmd.commands[(uchar)sym]) {
+        int (*fnc)(void) = g.Cmd.commands[(uchar)sym]->ef_funct;
+
+        if (mode == MV_ANY) {
+            for (d = N_DIRS_Z - 1; d > DIR_ERR; d--)
+                if (fnc == move_funcs[d][MV_WALK]
+                    || fnc == move_funcs[d][MV_RUN]
+                    || fnc == move_funcs[d][MV_RUSH])
+                    break;
+        } else {
+            for (d = N_DIRS_Z - 1; d > DIR_ERR; d--)
+                if (fnc == move_funcs[d][mode])
                     break;
-            } else if (sym == mvkeys[d])
-                break;
         }
     }
+
     if (d != DIR_ERR) {
         u.dx = xdir[d];
         u.dy = ydir[d];
@@ -3710,22 +3878,8 @@ dxdy_moveok(void)
 boolean
 redraw_cmd(char c)
 {
-    return (boolean) (c == g.Cmd.spkeys[NHKF_REDRAW]
-                      || (g.Cmd.num_pad && c == g.Cmd.spkeys[NHKF_REDRAW2]));
-}
-
-static boolean
-prefix_cmd(char c)
-{
-    return (c == g.Cmd.spkeys[NHKF_REQMENU]
-            || c == g.Cmd.spkeys[NHKF_RUSH]
-            || c == g.Cmd.spkeys[NHKF_RUN]
-            || c == g.Cmd.spkeys[NHKF_NOPICKUP]
-            || c == g.Cmd.spkeys[NHKF_RUN_NOPICKUP]
-            || c == g.Cmd.spkeys[NHKF_FIGHT]
-            || (g.Cmd.num_pad && (c == g.Cmd.spkeys[NHKF_RUN2]
-                                  || c == g.Cmd.spkeys[NHKF_RUSH2]
-                                  || c == g.Cmd.spkeys[NHKF_FIGHT2])));
+    return (boolean) (g.Cmd.commands[(uchar)c]
+                      && g.Cmd.commands[(uchar)c]->ef_funct == doredraw);
 }
 
 /*
@@ -3822,26 +3976,36 @@ show_direction_keys(winid win, /* should specify a window which is
         centerchar = ' ';
 
     if (nodiag) {
-        Sprintf(buf, "             %c   ", g.Cmd.move[DIR_N]);
+        Sprintf(buf, "             %s   ",
+                visctrl(cmd_from_func(do_move_north)));
         putstr(win, 0, buf);
         putstr(win, 0, "             |   ");
-        Sprintf(buf, "          %c- %c -%c",
-                g.Cmd.move[DIR_W], centerchar, g.Cmd.move[DIR_E]);
+        Sprintf(buf, "          %s- %c -%s",
+                visctrl(cmd_from_func(do_move_west)),
+                centerchar,
+                visctrl(cmd_from_func(do_move_east)));
         putstr(win, 0, buf);
         putstr(win, 0, "             |   ");
-        Sprintf(buf, "             %c   ", g.Cmd.move[DIR_S]);
+        Sprintf(buf, "             %s   ",
+                visctrl(cmd_from_func(do_move_south)));
         putstr(win, 0, buf);
     } else {
-        Sprintf(buf, "          %c  %c  %c",
-                g.Cmd.move[DIR_NW], g.Cmd.move[DIR_N], g.Cmd.move[DIR_NE]);
+        Sprintf(buf, "          %s  %s  %s",
+                visctrl(cmd_from_func(do_move_northwest)),
+                visctrl(cmd_from_func(do_move_north)),
+                visctrl(cmd_from_func(do_move_northeast)));
         putstr(win, 0, buf);
         putstr(win, 0, "           \\ | / ");
-        Sprintf(buf, "          %c- %c -%c",
-                g.Cmd.move[DIR_W], centerchar, g.Cmd.move[DIR_E]);
+        Sprintf(buf, "          %s- %c -%s",
+                visctrl(cmd_from_func(do_move_west)),
+                centerchar,
+                visctrl(cmd_from_func(do_move_east)));
         putstr(win, 0, buf);
         putstr(win, 0, "           / | \\ ");
-        Sprintf(buf, "          %c  %c  %c",
-                g.Cmd.move[DIR_SW], g.Cmd.move[DIR_S], g.Cmd.move[DIR_SE]);
+        Sprintf(buf, "          %s  %s  %s",
+                visctrl(cmd_from_func(do_move_southwest)),
+                visctrl(cmd_from_func(do_move_south)),
+                visctrl(cmd_from_func(do_move_southeast)));
         putstr(win, 0, buf);
     };
 }
@@ -3871,37 +4035,7 @@ help_dir(char sym,
      */
     dothat = "do that";
     how = " at"; /* for "<action> at yourself"; not used for up/down */
-    switch (spkey) {
-    case NHKF_NOPICKUP:
-        dothat = "move";
-        break;
-    case NHKF_RUSH2:
-        if (!g.Cmd.num_pad)
-            break;
-        /*FALLTHRU*/
-    case NHKF_RUSH:
-        dothat = "rush";
-        break;
-    case NHKF_RUN2:
-        if (!g.Cmd.num_pad)
-            break;
-        /*FALLTHRU*/
-    case NHKF_RUN:
-    case NHKF_RUN_NOPICKUP:
-        dothat = "run";
-        break;
-    case NHKF_FIGHT2:
-        if (!g.Cmd.num_pad)
-            break;
-        /*FALLTHRU*/
-    case NHKF_FIGHT:
-        dothat = "fight";
-        how = ""; /* avoid "fight at yourself" */
-        break;
-    default:
-        prefixhandling = FALSE;
-        break;
-    }
+    prefixhandling = FALSE;
 
     buf[0] = '\0';
     /* for movement prefix followed by '.' or (numpad && 's') to mean 'self';
@@ -3973,7 +4107,7 @@ help_dir(char sym,
     putstr(win, 0, buf);
     show_direction_keys(win, !prefixhandling ? '.' : ' ', NODIAG(u.umonnum));
 
-    if (!prefixhandling || spkey == NHKF_NOPICKUP) {
+    if (!prefixhandling) {
         /* NOPICKUP: unlike the other prefix keys, 'm' allows up/down for
            stair traversal; we won't get here when "m<" or "m>" has been
            given but we include up and down for 'm'+invalid_direction;
@@ -4359,7 +4493,7 @@ click_to_cmd(int x, int y, int mod)
         dir = xytod(x, y);
         if (!m_at(u.ux + x, u.uy + y)
             && !test_move(u.ux, u.uy, x, y, TEST_MOVE)) {
-            cmd[1] = g.Cmd.move[dir];
+            cmd[1] = cmd_from_func(move_funcs[dir][MV_WALK]);
             cmd[2] = '\0';
 
             if (IS_DOOR(levl[u.ux + x][u.uy + y].typ)) {
@@ -4403,9 +4537,9 @@ click_to_cmd(int x, int y, int mod)
     /* move, attack, etc. */
     cmd[1] = 0;
     if (mod == CLICK_1) {
-        cmd[0] = g.Cmd.move[dir];
+        cmd[0] = cmd_from_func(move_funcs[dir][MV_WALK]);
     } else {
-        cmd[0] = g.Cmd.run[dir];
+        cmd[0] = cmd_from_func(move_funcs[dir][MV_RUN]);
     }
 
     return cmd;
@@ -4511,32 +4645,8 @@ parse(void)
     if (g.multi)
         g.multi--;
 
-    /* in 3.4.3 this was in rhack(), where it was too late to handle M-5 */
-    if (g.Cmd.pcHack_compat) {
-        /* This handles very old inconsistent DOS/Windows behaviour
-           in a different way: earlier, the keyboard handler mapped
-           these, which caused counts to be strange when entered
-           from the number pad. Now do not map them until here. */
-        switch (foo) {
-        case '5':
-            foo = g.Cmd.spkeys[NHKF_RUSH];
-            break;
-        case M('5'):
-            foo = g.Cmd.spkeys[NHKF_RUN];
-            break;
-        default:
-            break; /* as is */
-        }
-    }
-
     g.command_line[0] = foo;
     g.command_line[1] = '\0';
-    if (prefix_cmd(foo)) {
-        foo = readchar();
-        savech((char) foo);
-        g.command_line[1] = foo;
-        g.command_line[2] = 0;
-    }
     clear_nhwindow(WIN_MESSAGE);
 
     iflags.in_parse = FALSE;
index dcd4e3f79880b0171132ef0bce548941cb81bf2c..e6a12c90252b3463cd19d35ab38b0604608340aa 100644 (file)
--- a/src/dig.c
+++ b/src/dig.c
@@ -987,13 +987,8 @@ use_pick_axe(struct obj *obj)
     downok = !!can_reach_floor(FALSE);
     dsp = dirsyms;
     for (dir = 0; dir < N_DIRS_Z; dir++) {
-        char dirch;
-        if (dir == DIR_DOWN)
-            dirch = cmd_from_func(dodown);
-        else if (dir == DIR_UP)
-            dirch = cmd_from_func(doup);
-        else
-            dirch = g.Cmd.move[dir];
+        char dirch = cmd_from_dir(dir, MV_WALK);
+
         /* filter out useless directions */
         if (u.uswallow) {
             ; /* all directions are viable when swallowed */
index 3f9bf05ef8ff59a2855eb1ca95ea6ec17acfcea0..d3a472b053e5124bba7677811e15edd49506a6bb 100644 (file)
--- a/src/do.c
+++ b/src/do.c
@@ -1120,6 +1120,8 @@ dodown(void)
     if (trap && Is_stronghold(&u.uz)) {
         goto_hell(FALSE, TRUE);
     } else {
+        if (!trap)
+            u.dz = 1;
         g.at_ladder = (boolean) (levl[u.ux][u.uy].typ == LADDER);
         next_level(!trap);
         g.at_ladder = FALSE;
@@ -1173,6 +1175,7 @@ doup(void)
         return ECMD_OK;
     }
     g.at_ladder = (boolean) (levl[u.ux][u.uy].typ == LADDER);
+    u.dz = -1;
     prev_level(TRUE);
     g.at_ladder = FALSE;
     return ECMD_TIME;
@@ -2046,7 +2049,7 @@ cmd_safety_prevention(const char *cmddesc, const char *act, int *flagcounter)
         buf[0] = '\0';
         if (iflags.cmdassist || !(*flagcounter)++)
             Sprintf(buf, "  Use '%s' prefix to force %s.",
-                    visctrl(g.Cmd.spkeys[NHKF_REQMENU]), cmddesc);
+                    visctrl(cmd_from_func(do_reqmenu)), cmddesc);
         Norep("%s%s", act, buf);
         return TRUE;
     }
index 64f4ebcc144b6ba2d985b9428115af70f3a14b85..84a96eb89eb806901d9b1d369cd8328a00067bb4 100644 (file)
@@ -108,22 +108,25 @@ getpos_help(boolean force, const char *goal)
     char sbuf[BUFSZ];
     boolean doing_what_is;
     winid tmpwin = create_nhwindow(NHW_MENU);
-    int runkey = iflags.num_pad ? NHKF_RUN2 : NHKF_RUN;
-    int rushkey = iflags.num_pad ? NHKF_RUSH2 : NHKF_RUSH;
 
     Sprintf(sbuf,
             "Use '%s', '%s', '%s', '%s' to move the cursor to %s.", /* hjkl */
-            visctrl(g.Cmd.move[DIR_W]), visctrl(g.Cmd.move[DIR_S]),
-            visctrl(g.Cmd.move[DIR_N]), visctrl(g.Cmd.move[DIR_E]), goal);
+            visctrl(cmd_from_func(do_move_west)),
+            visctrl(cmd_from_func(do_move_south)),
+            visctrl(cmd_from_func(do_move_north)),
+            visctrl(cmd_from_func(do_move_east)), goal);
     putstr(tmpwin, 0, sbuf);
     Sprintf(sbuf,
             "Use '%s', '%s', '%s', '%s' to fast-move the cursor, %s.",
-            visctrl(g.Cmd.run[DIR_W]), visctrl(g.Cmd.run[DIR_S]),
-            visctrl(g.Cmd.run[DIR_N]), visctrl(g.Cmd.run[DIR_E]),
+            visctrl(cmd_from_func(do_run_west)),
+            visctrl(cmd_from_func(do_run_south)),
+            visctrl(cmd_from_func(do_run_north)),
+            visctrl(cmd_from_func(do_run_east)),
             fastmovemode[iflags.getloc_moveskip]);
     putstr(tmpwin, 0, sbuf);
     Sprintf(sbuf, "(or prefix normal move with '%s' or '%s' to fast-move)",
-            visctrl(g.Cmd.spkeys[runkey]), visctrl(g.Cmd.spkeys[rushkey]));
+            visctrl(cmd_from_func(do_run)),
+            visctrl(cmd_from_func(do_rush)));
     putstr(tmpwin, 0, sbuf);
     putstr(tmpwin, 0, "Or enter a background symbol (ex. '<').");
     Sprintf(sbuf, "Use '%s' to move the cursor on yourself.",
@@ -698,8 +701,6 @@ getpos(coord *ccp, boolean force, const char *goal)
     schar udx = u.dx, udy = u.dy, udz = u.dz;
     int dx, dy;
     boolean rushrun = FALSE;
-    int runkey = iflags.num_pad ? NHKF_RUN2 : NHKF_RUN;
-    int rushkey = iflags.num_pad ? NHKF_RUSH2 : NHKF_RUSH;
 
     for (i = 0; i < SIZE(pick_chars_def); i++)
         pick_chars[i] = g.Cmd.spkeys[pick_chars_def[i].nhkf];
@@ -757,7 +758,7 @@ getpos(coord *ccp, boolean force, const char *goal)
             result = -1;
             break;
         }
-        if (c == g.Cmd.spkeys[runkey] || c == g.Cmd.spkeys[rushkey]) {
+        if (c == cmd_from_func(do_run) || c == cmd_from_func(do_rush)) {
             c = readchar_poskey(&tx, &ty, &sidx);
             rushrun = TRUE;
         }
@@ -974,9 +975,11 @@ getpos(coord *ccp, boolean force, const char *goal)
                     if (!force)
                         Strcpy(note, "aborted");
                     else /* hjkl */
-                        Sprintf(note, "use '%c', '%c', '%c', '%c' or '%s'",
-                                g.Cmd.move[DIR_W], g.Cmd.move[DIR_S],
-                                g.Cmd.move[DIR_N], g.Cmd.move[DIR_E],
+                        Sprintf(note, "use '%s', '%s', '%s', '%s' or '%s'",
+                                visctrl(cmd_from_func(do_move_west)),
+                                visctrl(cmd_from_func(do_move_south)),
+                                visctrl(cmd_from_func(do_move_north)),
+                                visctrl(cmd_from_func(do_move_east)),
                                 visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK]));
                     pline("Unknown direction: '%s' (%s).", visctrl((char) c),
                           note);