diff --git a/include/decl.h b/include/decl.h index 1a048b007..a34550ef9 100644 --- a/include/decl.h +++ b/include/decl.h @@ -240,6 +240,7 @@ struct instance_globals_c { /* decl.c */ char chosen_windowtype[WINTYPELEN]; int cmd_key; /* parse() / rhack() */ + struct Cmd_bind *cmd_bind; cmdcount_nht command_count; /* some objects need special handling during destruction or placement */ struct obj *current_wand; /* wand currently zapped/applied */ diff --git a/include/extern.h b/include/extern.h index 28ec3fa4f..52358357e 100644 --- a/include/extern.h +++ b/include/extern.h @@ -370,6 +370,7 @@ extern void change_palette(void); /* ### cmd.c ### */ +extern void cmdbind_freeall(void); extern void set_move_cmd(int, int); extern int do_move_west(void); extern int do_move_northwest(void); @@ -447,7 +448,7 @@ extern int doextlist(void); extern int extcmd_via_menu(void); extern int enter_explore_mode(void); extern boolean bind_mousebtn(int, const char *); -extern boolean bind_key(uchar, const char *); +extern boolean bind_key(uchar, const char *, boolean); extern void dokeylist(void); extern int xytodir(int, int); extern void dirtocoord(coord *, int); diff --git a/include/func_tab.h b/include/func_tab.h index 2924b3bf3..b5d1f9704 100644 --- a/include/func_tab.h +++ b/include/func_tab.h @@ -29,6 +29,14 @@ #define ECM_EXACTMATCH 0x02 /* needs exact match of findstr */ #define ECM_NO1CHARCMD 0x04 /* ignore commands like '?' and '#' */ +/* a key bound to ext_func_tab */ +struct Cmd_bind { + uchar key; + boolean userbind; /* added by user */ + const struct ext_func_tab *cmd; + struct Cmd_bind *next; +}; + struct ext_func_tab { uchar key; const char *ef_txt, *ef_desc; diff --git a/include/hack.h b/include/hack.h index a52258a4e..0a025a902 100644 --- a/include/hack.h +++ b/include/hack.h @@ -252,7 +252,7 @@ struct cmd { boolean swap_yz; /* QWERTZ keyboards; use z to move NW, y to zap */ 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 */ + struct Cmd_bind *cmdbinds; const struct ext_func_tab *mousebtn[NUM_MOUSE_BUTTONS]; char spkeys[NUM_NHKF]; char extcmd_char; /* key that starts an extended command ('#') */ diff --git a/src/cmd.c b/src/cmd.c index 0969f0222..20866f8db 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -94,6 +94,10 @@ extern int dozap(void); /**/ extern int doorganize(void); /**/ #endif /* DUMB */ +staticfn struct Cmd_bind *cmdbind_get(uchar); +staticfn void cmdbind_add(uchar, const struct ext_func_tab *, boolean); +staticfn void cmdbind_remove(uchar); +staticfn void cmdbind_swapkeys(uchar, uchar); staticfn int dosuspend_core(void); staticfn int dosh_core(void); staticfn int doherecmdmenu(void); @@ -2078,17 +2082,119 @@ extcmds_getentry(int i) return &extcmdlist[i]; } +/* get the command bound to a key */ +staticfn struct Cmd_bind * +cmdbind_get(uchar key) +{ + struct Cmd_bind *bind = gc.Cmd.cmdbinds; + + if (!key) + return NULL; + + while (bind) { + if (bind->key == key) + return bind; + bind = bind->next; + } + return bind; +} + +staticfn void +cmdbind_add(uchar key, const struct ext_func_tab *extcmd, boolean user) +{ + struct Cmd_bind *bind = cmdbind_get(key); + + if (!key) + return; + if (!extcmd && bind) { + cmdbind_remove(key); + return; + } + + /* binding exists, set it to this command */ + if (bind) { + bind->cmd = extcmd; + bind->userbind = user; + return; + } else { + bind = (struct Cmd_bind *) alloc(sizeof(struct Cmd_bind)); + bind->key = key; + bind->userbind = user; + bind->cmd = extcmd; + bind->next = gc.Cmd.cmdbinds; + gc.Cmd.cmdbinds = bind; + } +} + +staticfn void +cmdbind_remove(uchar key) +{ + struct Cmd_bind *bind = gc.Cmd.cmdbinds; + struct Cmd_bind *prev = (struct Cmd_bind *) 0; + + while (bind) { + if (bind->key == key) { + if (prev) + prev->next = bind->next; + else + gc.Cmd.cmdbinds = bind->next; + free(bind); + return; + } + prev = bind; + bind = bind->next; + } +} + +void +cmdbind_freeall(void) +{ + struct Cmd_bind *next; + + while (gc.Cmd.cmdbinds) { + next = gc.Cmd.cmdbinds->next; + free(gc.Cmd.cmdbinds); + gc.Cmd.cmdbinds = next; + } +} + +/* swap key bindings for key1 and key2. both bindings must exist. */ +staticfn void +cmdbind_swapkeys(uchar key1, uchar key2) +{ + struct Cmd_bind *bind1 = cmdbind_get(key1); + struct Cmd_bind *bind2 = cmdbind_get(key2); + + if (bind1 && bind2) { + bind1->key = key2; + bind2->key = key1; + } +} + /* return number of extended commands bound to a non-default key */ int count_bind_keys(void) { - int nbinds = 0; - int i; + struct Cmd_bind *bind = gc.Cmd.cmdbinds; + int i, nbinds = 0; + uchar keys[256]; - for (i = 0; i < extcmdlist_length; i++) - if (extcmdlist[i].key - && gc.Cmd.commands[extcmdlist[i].key] != &extcmdlist[i]) + (void) memset(keys, 0, sizeof(uchar) * 256); + + /* commands bound to different key */ + while (bind) { + keys[bind->key] = 1; + if (bind->userbind && bind->cmd && bind->cmd->key != bind->key) { nbinds++; + } + bind = bind->next; + } + + /* commands which should be bound to a key, but aren't */ + for (i = 0; i < extcmdlist_length; i++) + if (extcmdlist[i].key && !keys[extcmdlist[i].key]) + nbinds++; + return nbinds; } @@ -2100,16 +2206,35 @@ get_changed_key_binds(strbuf_t *sbuf) int i; char buf[BUFSZ]; char buf2[QBUFSZ]; + struct Cmd_bind *bind = gc.Cmd.cmdbinds; + uchar keys[256]; + + (void) memset(keys, 0, sizeof(uchar) * 256); if (!sbuf) win = create_nhwindow(NHW_TEXT); + + /* commands bound to different key */ + while (bind) { + keys[bind->key] = 1; + if (bind->userbind && bind->cmd && bind->cmd->key != bind->key) { + Sprintf(buf, "BIND=%s:%s%s", key2txt(bind->key, buf2), + bind->cmd->ef_txt, + sbuf ? "\n" : ""); + if (sbuf) + strbuf_append(sbuf, buf); + else + putstr(win, 0, buf); + } + bind = bind->next; + } + + /* commands which should be bound to a key, but aren't */ for (i = 0; i < extcmdlist_length; i++) { struct ext_func_tab *ec = &extcmdlist[i]; - if (ec->key && gc.Cmd.commands[ec->key] - && gc.Cmd.commands[ec->key] != ec) { - Sprintf(buf, "BIND=%s:%s%s", key2txt(ec->key, buf2), - gc.Cmd.commands[ec->key]->ef_txt, + if (ec->key && !keys[ec->key]) { + Sprintf(buf, "BIND=%s:nothing%s", key2txt(ec->key, buf2), sbuf ? "\n" : ""); if (sbuf) strbuf_append(sbuf, buf); @@ -2136,6 +2261,7 @@ handler_rebind_keys_add(boolean keyfirst) char buf2[QBUFSZ]; uchar key = '\0'; int clr = NO_COLOR; + struct Cmd_bind *bind; if (keyfirst) { pline("Bind which key? "); @@ -2150,9 +2276,10 @@ handler_rebind_keys_add(boolean keyfirst) any = cg.zeroany; if (key) { - if (gc.Cmd.commands[key]) { + bind = cmdbind_get(key); + if (bind) { Sprintf(buf, "Key '%s' is currently bound to \"%s\".", - key2txt(key, buf2), gc.Cmd.commands[key]->ef_txt); + key2txt(key, buf2), bind->cmd->ef_txt); } else { Sprintf(buf, "Key '%s' is not bound to anything.", key2txt(key, buf2)); @@ -2187,7 +2314,7 @@ handler_rebind_keys_add(boolean keyfirst) npick = select_menu(win, PICK_ONE, &picks); destroy_nhwindow(win); if (npick > 0) { - const struct ext_func_tab *prevec; + struct Cmd_bind *prevcmd; const char *cmdstr; i = picks->item.a_int; @@ -2210,13 +2337,13 @@ handler_rebind_keys_add(boolean keyfirst) return; } - prevec = gc.Cmd.commands[key]; + prevcmd = cmdbind_get(key); - if (bind_key(key, cmdstr)) { - if (prevec && prevec != ec) { + if (bind_key(key, cmdstr, TRUE)) { + if (prevcmd && prevcmd->cmd != ec) { pline("Changed key '%s' from \"%s\" to \"%s\".", - key2txt(key, buf2), prevec->ef_txt, cmdstr); - } else if (!prevec) { + key2txt(key, buf2), prevcmd->cmd->ef_txt, cmdstr); + } else if (!prevcmd) { pline("Bound key '%s' to \"%s\".", key2txt(key, buf2), cmdstr); } @@ -2386,6 +2513,7 @@ key2extcmddesc(uchar key) const char *txt; int k, i, j; uchar M_5 = (uchar) M('5'), M_0 = (uchar) M('0'); + struct Cmd_bind *cmdbind; /* need to check for movement commands before checking the extended commands table because it contains entries for number_pad commands @@ -2418,8 +2546,9 @@ key2extcmddesc(uchar key) return misc_keys[i].desc; } /* finally, check whether 'key' is a command */ - if (gc.Cmd.commands[key] && (txt = gc.Cmd.commands[key]->ef_txt) != 0) { - Sprintf(key2cmdbuf, "%s (#%s)", gc.Cmd.commands[key]->ef_desc, txt); + if ((cmdbind = cmdbind_get(key)) != 0 + && (txt = cmdbind->cmd->ef_txt) != 0) { + Sprintf(key2cmdbuf, "%s (#%s)", cmdbind->cmd->ef_desc, txt); /* special case: for reqmenu prefix (normally 'm'), replace "prefix: request menu or modify command (#reqmenu)" @@ -2478,13 +2607,13 @@ bind_mousebtn(int btn, const char *command) } boolean -bind_key(uchar key, const char *command) +bind_key(uchar key, const char *command, boolean user) { struct ext_func_tab *extcmd; /* special case: "nothing" is reserved for unbinding */ if (!strcmpi(command, "nothing")) { - gc.Cmd.commands[key] = (struct ext_func_tab *) 0; + cmdbind_remove(key); return TRUE; } @@ -2493,7 +2622,7 @@ bind_key(uchar key, const char *command) continue; if ((extcmd->flags & INTERNALCMD) != 0) continue; - gc.Cmd.commands[key] = extcmd; + cmdbind_add(key, extcmd, user); #if 0 /* silently accept key binding for unavailable command (!SHELL,&c) */ if ((extcmd->flags & CMD_NOT_AVAILABLE) != 0) { char buf[BUFSZ]; @@ -2519,7 +2648,7 @@ bind_key_fn(uchar key, int (*fn)(void)) continue; if ((extcmd->flags & INTERNALCMD) != 0) continue; - gc.Cmd.commands[key] = extcmd; + cmdbind_add(key, extcmd, FALSE); return TRUE; } @@ -2534,31 +2663,31 @@ commands_init(void) for (extcmd = extcmdlist; extcmd->ef_txt; extcmd++) if (extcmd->key) - gc.Cmd.commands[extcmd->key] = extcmd; + cmdbind_add(extcmd->key, extcmd, FALSE); (void) bind_mousebtn(1, "therecmdmenu"); (void) bind_mousebtn(2, "clicklook"); /* 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('N', "name"); - (void) bind_key('u', "untrap"); - (void) bind_key('5', "run"); - (void) bind_key(M('5'), "rush"); - (void) bind_key('-', "fight"); + (void) bind_key(C('l'), "redraw", FALSE); + (void) bind_key('h', "help", FALSE); + (void) bind_key('j', "jump", FALSE); + (void) bind_key('k', "kick", FALSE); + (void) bind_key('l', "loot", FALSE); + (void) bind_key(C('n'), "annotate", FALSE); + (void) bind_key('N', "name", FALSE); + (void) bind_key('u', "untrap", FALSE); + (void) bind_key('5', "run", FALSE); + (void) bind_key(M('5'), "rush", FALSE); + (void) bind_key('-', "fight", FALSE); /* alt keys: */ - (void) bind_key(M('O'), "overview"); - (void) bind_key(M('2'), "twoweapon"); - (void) bind_key(M('N'), "name"); + (void) bind_key(M('O'), "overview", FALSE); + (void) bind_key(M('2'), "twoweapon", FALSE); + (void) bind_key(M('N'), "name", FALSE); #if 0 /* don't do this until the rest_on_space option is set or cleared */ - (void) bind_key(' ', "wait"); + (void) bind_key(' ', "wait", FALSE); #endif } @@ -2567,12 +2696,13 @@ keylist_func_has_key(const struct ext_func_tab *extcmd, boolean *skip_keys_used) /* boolean keys_used[256] */ { int i; + struct Cmd_bind *bind; for (i = 0; i < 256; ++i) { if (skip_keys_used[i]) continue; - if (gc.Cmd.commands[i] == extcmd) + if (((bind = cmdbind_get(i)) != 0) && (bind->cmd == extcmd)) return TRUE; } return FALSE; @@ -2588,6 +2718,7 @@ keylist_putcmds(winid datawin, boolean docount, char buf[BUFSZ], buf2[QBUFSZ]; boolean keys_already_used[256]; /* copy of keys_used[] before updates */ int count = 0; + struct Cmd_bind *bind; for (i = 0; i < 256; i++) { uchar key = (uchar) i; @@ -2597,16 +2728,17 @@ keylist_putcmds(winid datawin, boolean docount, continue; if (key == ' ' && !flags.rest_on_space) continue; - if ((extcmd = gc.Cmd.commands[i]) != (struct ext_func_tab *) 0) { - if ((incl_flags && !(extcmd->flags & incl_flags)) - || (excl_flags && (extcmd->flags & excl_flags))) + bind = cmdbind_get(key); + if (bind && bind->cmd != (struct ext_func_tab *) 0) { + if ((incl_flags && !(bind->cmd->flags & incl_flags)) + || (excl_flags && (bind->cmd->flags & excl_flags))) continue; if (docount) { count++; continue; } Sprintf(buf, "%-7s %-13s %s", key2txt(key, buf2), - extcmd->ef_txt, extcmd->ef_desc); + bind->cmd->ef_txt, bind->cmd->ef_desc); putstr(datawin, 0, buf); keys_used[i] = TRUE; } @@ -2810,9 +2942,10 @@ cmd_from_func(int (*fn)(void)) { int i; char ret = '\0'; + struct Cmd_bind *bind; - /* skip NUL; allowing it would wreak havoc */ - for (i = 1; i < 256; ++i) { + for (bind = gc.Cmd.cmdbinds; bind; bind = bind->next) { + i = bind->key; /* skip space; we'll use it below as last resort if no other keystroke invokes space's command */ if (i == ' ') @@ -2823,7 +2956,7 @@ cmd_from_func(int (*fn)(void)) && !gc.Cmd.num_pad) continue; - if (gc.Cmd.commands[i] && gc.Cmd.commands[i]->ef_funct == fn) { + if (bind->cmd && bind->cmd->ef_funct == fn) { if (i >= ' ' && i <= '~') return (char) i; else { @@ -2831,7 +2964,7 @@ cmd_from_func(int (*fn)(void)) } } } - if (gc.Cmd.commands[' '] && gc.Cmd.commands[' ']->ef_funct == fn) + if ((bind = cmdbind_get(' ')) != 0 && bind->cmd->ef_funct == fn) return ' '; return ret; } @@ -3124,7 +3257,6 @@ reset_commands(boolean initial) static struct ext_func_tab *back_dir_cmd[N_DIRS][N_MOVEMODES]; static uchar back_dir_key[N_DIRS][N_MOVEMODES]; static boolean backed_dir_cmd = FALSE; - const struct ext_func_tab *cmdtmp; boolean flagtemp; int c, i, updated = 0; int dir, mode; @@ -3140,8 +3272,7 @@ reset_commands(boolean initial) if (backed_dir_cmd) { for (dir = 0; dir < N_DIRS; dir++) { for (mode = 0; mode < N_MOVEMODES; mode++) { - gc.Cmd.commands[back_dir_key[dir][mode]] - = back_dir_cmd[dir][mode]; + cmdbind_add(back_dir_key[dir][mode], back_dir_cmd[dir][mode], FALSE); } } } @@ -3163,9 +3294,7 @@ reset_commands(boolean initial) perform the swap (or reverse previous one) */ for (i = 0; i < SIZE(ylist); i++) { c = ylist[i] & 0xff; - cmdtmp = gc.Cmd.commands[c]; /* tmp = [y] */ - gc.Cmd.commands[c] = gc.Cmd.commands[c + 1]; /* [y] = [z] */ - gc.Cmd.commands[c + 1] = cmdtmp; /* [z] = tmp */ + cmdbind_swapkeys(c, c + 1); } } /* MSDOS compatibility mode (only applicable for num_pad) */ @@ -3182,8 +3311,10 @@ reset_commands(boolean initial) #endif /* FIXME: NHKF_DOINV2 ought to be implemented instead of this */ c = M('0') & 0xff; - gc.Cmd.commands[c] = gc.Cmd.pcHack_compat ? gc.Cmd.commands['I'] - : 0; + if (gc.Cmd.pcHack_compat) + cmdbind_add(c, ext_func_tab_from_func(dotypeinv), FALSE); + else + cmdbind_remove(c); } /* phone keypad layout (only applicable for num_pad) */ flagtemp = (iflags.num_pad_mode & 2) ? gc.Cmd.num_pad : FALSE; @@ -3193,13 +3324,9 @@ reset_commands(boolean initial) /* phone_layout has been toggled */ for (i = 0; i < 3; i++) { c = '1' + i; /* 1,2,3 <-> 7,8,9 */ - cmdtmp = gc.Cmd.commands[c]; /* tmp = [1] */ - gc.Cmd.commands[c] = gc.Cmd.commands[c + 6]; /* [1] = [7] */ - gc.Cmd.commands[c + 6] = cmdtmp; /* [7] = tmp */ + cmdbind_swapkeys(c, c + 6); c = (M('1') & 0xff) + i; /* M-1,M-2,M-3 <-> M-7,M-8,M-9 */ - cmdtmp = gc.Cmd.commands[c]; /* tmp = [M-1] */ - gc.Cmd.commands[c] = gc.Cmd.commands[c + 6]; /* [M-1]=[M-7] */ - gc.Cmd.commands[c + 6] = cmdtmp; /* [M-7] = tmp */ + cmdbind_swapkeys(c, c + 6); } } } /*?initial*/ @@ -3216,6 +3343,7 @@ reset_commands(boolean initial) for (dir = 0; dir < N_DIRS; dir++) { for (mode = MV_WALK; mode < N_MOVEMODES; mode++) { uchar di = (uchar) gc.Cmd.dirchars[dir]; + struct Cmd_bind *bind; if (!gc.Cmd.num_pad) { if (mode == MV_RUN) di = highc(di); @@ -3225,9 +3353,11 @@ reset_commands(boolean initial) else if (mode == MV_RUSH) di = M(di); } back_dir_key[dir][mode] = di; - back_dir_cmd[dir][mode] - = (struct ext_func_tab *) gc.Cmd.commands[di]; - gc.Cmd.commands[di] = (struct ext_func_tab *) 0; + if ((bind = cmdbind_get(di)) != 0) + back_dir_cmd[dir][mode] = (struct ext_func_tab *) bind->cmd; + else + back_dir_cmd[dir][mode] = (struct ext_func_tab *) 0; + cmdbind_remove(di); } } backed_dir_cmd = TRUE; @@ -3265,15 +3395,15 @@ update_rest_on_space(void) donull, (IFBURIED | CMD_M_PREFIX), "waiting" }; static const struct ext_func_tab *unrestonspace = 0; - const struct ext_func_tab *bound_f = gc.Cmd.commands[' ']; + struct Cmd_bind *bind = cmdbind_get(' '); /* when 'rest_on_space' is On, will run the #wait command; when it is Off, will use 'unrestonspace' which will either be Null and elicit "Unknown command ' '." or have some non-Null command bound in player's RC file */ - if (bound_f != 0 && bound_f != &restonspace) - unrestonspace = bound_f; - gc.Cmd.commands[' '] = flags.rest_on_space ? &restonspace : unrestonspace; + if (bind && bind->cmd != &restonspace) + unrestonspace = bind->cmd; + cmdbind_add(' ', flags.rest_on_space ? &restonspace : unrestonspace, FALSE); } /* commands which accept 'm' prefix to request menu operation or other @@ -3450,11 +3580,13 @@ rhack(int key) const struct ext_func_tab *tlist; int res; + gc.cmd_bind = cmdbind_get(key & 0xFF); + do_cmdq_extcmd: if (cmdq_ec) tlist = cmdq_ec; else - tlist = gc.Cmd.commands[key & 0xff]; + tlist = gc.cmd_bind ? gc.cmd_bind->cmd : NULL; /* current - use key to directly index cmdlist array */ if (tlist != 0) { @@ -3640,9 +3772,10 @@ int movecmd(char sym, int mode) { int d = DIR_ERR; + struct Cmd_bind *bind = cmdbind_get(sym); - if (gc.Cmd.commands[(uchar) sym]) { - int (*fnc)(void) = gc.Cmd.commands[(uchar) sym]->ef_funct; + if (bind) { + int (*fnc)(void) = bind->cmd->ef_funct; if (mode == MV_ANY) { for (d = N_DIRS_Z - 1; d > DIR_ERR; d--) @@ -3681,9 +3814,9 @@ boolean redraw_cmd(char c) { uchar uc = (uchar) c; - const struct ext_func_tab *cmd = gc.Cmd.commands[uc]; + struct Cmd_bind *bind = cmdbind_get(uc); - return (boolean) (cmd && cmd->ef_funct == doredraw); + return (boolean) (bind && bind->cmd->ef_funct == doredraw); } /* @@ -4865,6 +4998,7 @@ staticfn int parse(void) { int foo; + struct Cmd_bind *bind; iflags.in_parse = TRUE; gc.command_count = 0; @@ -4894,13 +5028,14 @@ parse(void) gl.last_command_count = 0; } else if (gi.in_doagain) { gc.command_count = gl.last_command_count; - } else if (foo && gc.Cmd.commands[foo & 0xff] + } else if (foo && (bind = cmdbind_get(foo & 0xFF)) != 0 /* these shouldn't go into the do-again buffer */ - && (gc.Cmd.commands[foo & 0xff]->ef_funct == do_repeat - || gc.Cmd.commands[foo & 0xff]->ef_funct == doprev_message + && bind && bind->cmd + && (bind->cmd->ef_funct == do_repeat + || bind->cmd->ef_funct == doprev_message /* this one might get put into the do-again buffer but only if the interface code tells the core to do it */ - || gc.Cmd.commands[foo & 0xff]->ef_funct == doextcmd)) { + || bind->cmd->ef_funct == doextcmd)) { /* gc.command_count will be set again when we re-enter with gi.in_doagain set true */ gc.command_count = gl.last_command_count; diff --git a/src/decl.c b/src/decl.c index e6fd152f0..cc1e8a3b6 100644 --- a/src/decl.c +++ b/src/decl.c @@ -247,6 +247,7 @@ static const struct instance_globals_c g_init_c = { /* decl.c */ UNDEFINED_VALUES, /* chosen_windowtype */ 0, /* cmd_key */ + NULL, /* cmd_bind */ 0L, /* command_count */ UNDEFINED_PTR, /* current_wand */ #ifdef DEF_PAGER diff --git a/src/options.c b/src/options.c index d63db5ba7..51faccca8 100644 --- a/src/options.c +++ b/src/options.c @@ -7682,7 +7682,7 @@ parsebindings(char *bindings) } /* extended command? */ - if (!bind_key(key, bind)) { + if (!bind_key(key, bind, TRUE)) { config_error_add("Unknown key binding command '%s'", bind); return FALSE; } diff --git a/src/save.c b/src/save.c index 57c8bfb41..461ed10a1 100644 --- a/src/save.c +++ b/src/save.c @@ -1109,6 +1109,7 @@ freedynamicdata(void) freeroleoptvals(); /* saveoptvals(&tnhfp) */ cmdq_clear(CQ_CANNED); cmdq_clear(CQ_REPEAT); + cmdbind_freeall(); free_tutorial(); /* (only needed if quitting while in tutorial) */ /* per-turn data, but might get added to when freeing other stuff */