Rudimentary key rebinding in game options

Currently shown only in the full options list, as it's not
quite complete. (For example, it doesn't handle movement commands,
or the getpos keys)
This commit is contained in:
Pasi Kallinen
2022-08-04 14:00:47 +03:00
parent b5acfc5c3f
commit 4ff9537b0d
5 changed files with 209 additions and 0 deletions

View File

@@ -1764,6 +1764,7 @@ some quest nemeses release a cloud of poisonous gas when they die
taming magic acting on an already tame creature might make it become tamer
eliminate scimitar skill and have scimitars use saber skill
simplified configuration options menu
rudimentary key rebinding in game options
Platform- and/or Interface-Specific New Features

View File

@@ -264,6 +264,8 @@ extern void savech_extcmd(const char *, boolean);
extern char extcmd_initiator(void);
extern int doextcmd(void);
extern struct ext_func_tab *extcmds_getentry(int);
extern int count_bind_keys(void);
extern void handler_rebind_keys(void);
extern int extcmds_match(const char *, int, int **);
extern const char *key2extcmddesc(uchar);
extern boolean bind_specialkey(uchar, const char *);

View File

@@ -132,6 +132,8 @@ opt_##a,
+ sizeof "kick" + sizeof "force" + 20),
opt_out, set_in_game, Yes, Yes, No, Yes, NoAlias,
"action to take when encountering locked door or chest")
NHOPTO("bind keys", Advanced, o_bind_keys, BUFSZ, opt_in,
set_in_game, No, Yes, No, NoAlias, "edit key binds")
#if defined(MICRO) && !defined(AMIGA)
NHOPTB(BIOS, Advanced, 0, opt_in, set_in_config, Off, Yes, No, No, NoAlias,
&iflags.BIOS)

182
src/cmd.c
View File

@@ -161,6 +161,7 @@ static char *parse(void);
static void show_direction_keys(winid, char, boolean);
static boolean help_dir(char, uchar, const char *);
static void handler_rebind_keys_add(boolean);
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 *);
@@ -2826,6 +2827,187 @@ extcmds_getentry(int i)
return &extcmdlist[i];
}
/* return number of extended commands bound to a non-default key */
int
count_bind_keys(void)
{
int nbinds = 0;
int i;
for (i = 0; i < extcmdlist_length; i++)
if (extcmdlist[i].key && g.Cmd.commands[extcmdlist[i].key] != &extcmdlist[i])
nbinds++;
return nbinds;
}
static void
display_changed_key_binds(void)
{
winid win;
int i;
char buf[BUFSZ];
char buf2[QBUFSZ];
win = create_nhwindow(NHW_TEXT);
for (i = 0; i < extcmdlist_length; i++) {
struct ext_func_tab *ec = &extcmdlist[i];
if (ec->key && g.Cmd.commands[ec->key]
&& g.Cmd.commands[ec->key] != ec) {
Sprintf(buf, "BIND=%s:%s", key2txt(ec->key, buf2),
g.Cmd.commands[ec->key]->ef_txt);
putstr(win, 0, buf);
}
}
display_nhwindow(win, TRUE);
destroy_nhwindow(win);
}
/* interactive key binding */
static void
handler_rebind_keys_add(boolean keyfirst)
{
struct ext_func_tab *ec;
winid win;
anything any;
int i, npick;
menu_item *picks = (menu_item *) 0;
char buf[BUFSZ];
char buf2[QBUFSZ];
uchar key = '\0';
if (keyfirst) {
pline("Bind which key? ");
key = pgetchar();
if (!key || key == '\027')
return;
}
win = create_nhwindow(NHW_MENU);
start_menu(win, MENU_BEHAVE_STANDARD);
any = cg.zeroany;
if (key) {
if (g.Cmd.commands[key]) {
Sprintf(buf, "Key '%s' is currently bound to \"%s\".",
key2txt(key, buf2), g.Cmd.commands[key]->ef_txt);
} else {
Sprintf(buf, "Key '%s' is not bound to anything.",
key2txt(key, buf2));
}
add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, buf,
MENU_ITEMFLAGS_NONE);
add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, "",
MENU_ITEMFLAGS_NONE);
}
any.a_int = -1;
add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, "nothing: unbind the key",
MENU_ITEMFLAGS_NONE);
any.a_int = 0;
add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, "",
MENU_ITEMFLAGS_NONE);
for (i = 0; i < extcmdlist_length; i++) {
ec = &extcmdlist[i];
if ((ec->flags & (MOVEMENTCMD|INTERNALCMD|CMD_NOT_AVAILABLE)) != 0)
continue;
any.a_int = (i + 1);
Sprintf(buf, "%s: %s", ec->ef_txt, ec->ef_desc);
add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, buf,
MENU_ITEMFLAGS_NONE);
}
if (key)
Sprintf(buf, "Bind '%s' to what command?", key2txt(key, buf2));
else
Sprintf(buf, "Bind what command?");
end_menu(win, buf);
npick = select_menu(win, PICK_ONE, &picks);
destroy_nhwindow(win);
if (npick > 0) {
const struct ext_func_tab *prevec;
const char *cmdstr;
i = picks->item.a_int;
free((genericptr_t) picks);
if (i == -1) {
ec = NULL;
cmdstr = "nothing";
goto bindit;
} else {
ec = &extcmdlist[i-1];
cmdstr = ec->ef_txt;
}
bindit:
if (!key) {
pline("Bind which key? ");
key = pgetchar();
if (!key || key == '\027')
return;
}
prevec = g.Cmd.commands[key];
if (bind_key(key, cmdstr)) {
if (prevec && prevec != ec) {
pline("Changed key '%s' from \"%s\" to \"%s\".",
key2txt(key, buf2), prevec->ef_txt, cmdstr);
} else if (!prevec) {
pline("Bound key '%s' to \"%s\".", key2txt(key, buf2), cmdstr);
}
} else {
pline("Key binding failed?!");
}
}
}
void
handler_rebind_keys(void)
{
winid win;
anything any;
int i, npick;
menu_item *picks = (menu_item *) 0;
redo_rebind:
win = create_nhwindow(NHW_MENU);
start_menu(win, MENU_BEHAVE_STANDARD);
any = cg.zeroany;
any.a_int = 1;
add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, "bind key to a command",
MENU_ITEMFLAGS_NONE);
any.a_int = 2;
add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, "bind command to a key",
MENU_ITEMFLAGS_NONE);
if (count_bind_keys()) {
any.a_int = 3;
add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, "view changed key binds",
MENU_ITEMFLAGS_NONE);
}
end_menu(win, "Do what?");
npick = select_menu(win, PICK_ONE, &picks);
destroy_nhwindow(win);
if (npick > 0) {
i = picks->item.a_int;
free((genericptr_t) picks);
if (i == 1 || i == 2) {
handler_rebind_keys_add((i == 1));
} else if (i == 3) {
display_changed_key_binds();
}
goto redo_rebind;
}
}
/* find extended command entries matching findstr.
if findstr is NULL, returns all available entries.
returns: number of matching extended commands,

View File

@@ -7567,6 +7567,28 @@ optfn_o_autopickup_exceptions(
return optn_ok;
}
static int
optfn_o_bind_keys(
int optidx UNUSED, int req, boolean negated UNUSED,
char *opts, char *op UNUSED)
{
if (req == do_init) {
return optn_ok;
}
if (req == do_set) {
}
if (req == get_val) {
if (!opts)
return optn_err;
Sprintf(opts, n_currently_set, count_bind_keys());
return optn_ok;
}
if (req == do_handler) {
handler_rebind_keys();
}
return optn_ok;
}
static int
optfn_o_menu_colors(int optidx UNUSED, int req, boolean negated UNUSED,
char *opts, char *op UNUSED)