New customize controller buttons menu option

Handles game controller button bindings exclusively.
Classic "customize controls" option now handles only keyboard / mouse bindings.
This separation is achieved with the new order of QKEYS in keyboard.h, and
binding functions in menus (especially "MenuKey" functions) now take into
account the "scope" where they operate: keyboard/mouse only, controller only,
or both.
This commit is contained in:
Jaime Moreira 2022-05-07 23:55:00 -04:00
parent d4100f7113
commit 0aa44afa13
3 changed files with 224 additions and 17 deletions

View file

@ -206,6 +206,7 @@ enum QKEYS {
// Keyboard keys / codes end here. Any new ones should go before this.
// From here on, only gamepad controls must be allowed.
// Otherwise, separate bindings (keyboard / controller) menu options will not work.
K_BTN_A,
K_JOY_FIRST_REGULAR = K_BTN_A,

View file

@ -43,6 +43,12 @@
#define QMF_GRAYED 0x00000002
#define QMF_NUMBERSONLY 0x00000004
enum {
KEYS_ALL = 0,
KEYS_KEYBOARD_MOUSE,
KEYS_CONTROLLER
};
typedef struct _tag_menuframework
{
int x, y;

View file

@ -64,6 +64,7 @@ static void M_Menu_Video_f(void);
static void M_Menu_Options_f(void);
static void M_Menu_Keys_f(void);
static void M_Menu_Joy_f(void);
static void M_Menu_ControllerButtons_f(void);
static void M_Menu_Quit_f(void);
void M_Menu_Credits(void);
@ -790,8 +791,7 @@ char *bindnames[][2] =
{"invdrop", "drop item"},
{"invprev", "prev item"},
{"invnext", "next item"},
{"cmd help", "help computer"},
{"+joyaltselector", "enable alt joy keys"}
{"cmd help", "help computer"}
};
#define NUM_BINDNAMES (sizeof bindnames / sizeof bindnames[0])
@ -803,11 +803,20 @@ static menuframework_s s_joy_menu;
static menuaction_s s_keys_actions[NUM_BINDNAMES];
static void
M_UnbindCommand(char *command)
M_UnbindCommand(char *command, int scope)
{
int j;
int begin = 0, end = K_LAST;
switch (scope)
{
case KEYS_KEYBOARD_MOUSE:
end = K_JOY_FIRST_REGULAR;
break;
case KEYS_CONTROLLER:
begin = K_JOY_FIRST_REGULAR;
}
for (j = 0; j < K_LAST; j++)
for (j = begin; j < end; j++)
{
char *b;
b = keybindings[j];
@ -825,15 +834,24 @@ M_UnbindCommand(char *command)
}
static void
M_FindKeysForCommand(char *command, int *twokeys)
M_FindKeysForCommand(char *command, int *twokeys, int scope)
{
int count;
int j;
int begin = 0, end = K_LAST;
switch (scope)
{
case KEYS_KEYBOARD_MOUSE:
end = K_JOY_FIRST_REGULAR;
break;
case KEYS_CONTROLLER:
begin = K_JOY_FIRST_REGULAR;
}
twokeys[0] = twokeys[1] = -1;
count = 0;
for (j = 0; j < K_LAST; j++)
for (j = begin; j < end; j++)
{
char *b;
b = keybindings[j];
@ -879,7 +897,7 @@ DrawKeyBindingFunc(void *self)
menuaction_s *a = (menuaction_s *)self;
float scale = SCR_GetMenuScale();
M_FindKeysForCommand(bindnames[a->generic.localdata[0]][0], keys);
M_FindKeysForCommand(bindnames[a->generic.localdata[0]][0], keys, KEYS_KEYBOARD_MOUSE);
if (keys[0] == -1)
{
@ -915,11 +933,11 @@ KeyBindingFunc(void *self)
menuaction_s *a = (menuaction_s *)self;
int keys[2];
M_FindKeysForCommand(bindnames[a->generic.localdata[0]][0], keys);
M_FindKeysForCommand(bindnames[a->generic.localdata[0]][0], keys, KEYS_KEYBOARD_MOUSE);
if (keys[1] != -1)
{
M_UnbindCommand(bindnames[a->generic.localdata[0]][0]);
M_UnbindCommand(bindnames[a->generic.localdata[0]][0], KEYS_KEYBOARD_MOUSE);
}
menukeyitem_bind = true;
@ -967,7 +985,8 @@ Keys_MenuKey(int key)
if (menukeyitem_bind)
{
if ((key != K_ESCAPE) && (key != '`'))
// Any key/button except from the game controller and escape keys
if ((key != K_ESCAPE) && (key != '`') && (key < K_JOY_FIRST_REGULAR))
{
char cmd[1024];
@ -988,7 +1007,7 @@ Keys_MenuKey(int key)
KeyBindingFunc(item);
return menu_in_sound;
case K_BACKSPACE: /* delete bindings */
M_UnbindCommand(bindnames[item->generic.localdata[0]][0]);
M_UnbindCommand(bindnames[item->generic.localdata[0]][0], KEYS_KEYBOARD_MOUSE);
return menu_out_sound;
default:
return Default_MenuKey(&s_keys_menu, key);
@ -1028,7 +1047,7 @@ MultiplayerDrawKeyBindingFunc(void *self)
menuaction_s *a = (menuaction_s *)self;
float scale = SCR_GetMenuScale();
M_FindKeysForCommand(multiplayer_key_bindnames[a->generic.localdata[0]][0], keys);
M_FindKeysForCommand(multiplayer_key_bindnames[a->generic.localdata[0]][0], keys, KEYS_ALL);
if (keys[0] == -1)
{
@ -1064,11 +1083,11 @@ MultiplayerKeyBindingFunc(void *self)
menuaction_s *a = (menuaction_s *)self;
int keys[2];
M_FindKeysForCommand(multiplayer_key_bindnames[a->generic.localdata[0]][0], keys);
M_FindKeysForCommand(multiplayer_key_bindnames[a->generic.localdata[0]][0], keys, KEYS_ALL);
if (keys[1] != -1)
{
M_UnbindCommand(multiplayer_key_bindnames[a->generic.localdata[0]][0]);
M_UnbindCommand(multiplayer_key_bindnames[a->generic.localdata[0]][0], KEYS_ALL);
}
menukeyitem_bind = true;
@ -1116,7 +1135,8 @@ MultiplayerKeys_MenuKey(int key)
if (menukeyitem_bind)
{
if ((key != K_ESCAPE) && (key != '`'))
// Any key/button but the escape ones
if ((key != K_ESCAPE) && (key != '`') && (key != K_JOY_BACK))
{
char cmd[1024];
@ -1137,7 +1157,7 @@ MultiplayerKeys_MenuKey(int key)
MultiplayerKeyBindingFunc(item);
return menu_in_sound;
case K_BACKSPACE: /* delete bindings */
M_UnbindCommand(multiplayer_key_bindnames[item->generic.localdata[0]][0]);
M_UnbindCommand(multiplayer_key_bindnames[item->generic.localdata[0]][0], KEYS_ALL);
return menu_out_sound;
default:
return Default_MenuKey(&s_multiplayer_keys_menu, key);
@ -1151,6 +1171,168 @@ M_Menu_Multiplayer_Keys_f(void)
M_PushMenu(MultiplayerKeys_MenuDraw, MultiplayerKeys_MenuKey);
}
/*
* GAME CONTROLLER ( GAMEPAD / JOYSTICK ) BUTTONS MENU
*/
char *controller_bindnames[][2] =
{
{"+attack", "attack"},
{"+moveup", "up / jump"},
{"+movedown", "down / crouch"},
{"weapnext", "next weapon"},
{"weapprev", "previous weapon"},
{"cycleweap weapon_chaingun weapon_machinegun weapon_blaster", "long range: quickswitch 1"},
{"cycleweap weapon_supershotgun weapon_shotgun", "close range: quickswitch 2"},
{"cycleweap weapon_rocketlauncher weapon_grenadelauncher ammo_grenades", "explosives: quickswitch 3"},
{"cycleweap weapon_bfg weapon_railgun weapon_hyperblaster", "special: quickswitch 4"},
{"prefweap weapon_railgun weapon_hyperblaster weapon_chaingun weapon_supershotgun weapon_machinegun weapon_shotgun weapon_blaster", "best safe weapon"},
{"prefweap weapon_bfg weapon_railgun weapon_rocketlauncher weapon_hyperblaster weapon_grenadelauncher weapon_chaingun ammo_grenades weapon_supershotgun", "best unsafe weapon"},
{"centerview", "center view"},
{"inven", "inventory"},
{"invuse", "use item"},
{"invdrop", "drop item"},
{"invprev", "prev item"},
{"invnext", "next item"},
{"cmd help", "help computer"},
{"+joyaltselector", "alt buttons modifier"}
};
#define NUM_CONTROLLER_BINDNAMES (sizeof controller_bindnames / sizeof controller_bindnames[0])
static menuframework_s s_controller_buttons_menu;
static menuaction_s s_controller_buttons_actions[NUM_CONTROLLER_BINDNAMES];
static void
DrawControllerButtonBindingFunc(void *self)
{
int keys[2];
menuaction_s *a = (menuaction_s *)self;
float scale = SCR_GetMenuScale();
M_FindKeysForCommand(controller_bindnames[a->generic.localdata[0]][0], keys, KEYS_CONTROLLER);
if (keys[0] == -1)
{
Menu_DrawString(a->generic.x + a->generic.parent->x + RCOLUMN_OFFSET * scale,
a->generic.y + a->generic.parent->y, "???");
}
else
{
int x;
const char *name;
name = Key_KeynumToString(keys[0]);
Menu_DrawString(a->generic.x + a->generic.parent->x + RCOLUMN_OFFSET * scale,
a->generic.y + a->generic.parent->y, name);
x = strlen(name) * 8;
if (keys[1] != -1)
{
Menu_DrawString(a->generic.x + a->generic.parent->x + 24 * scale + (x * scale),
a->generic.y + a->generic.parent->y, "or");
Menu_DrawString(a->generic.x + a->generic.parent->x + 48 * scale + (x * scale),
a->generic.y + a->generic.parent->y,
Key_KeynumToString(keys[1]));
}
}
}
static void
ControllerButtonBindingFunc(void *self)
{
menuaction_s *a = (menuaction_s *)self;
int keys[2];
M_FindKeysForCommand(controller_bindnames[a->generic.localdata[0]][0], keys, KEYS_CONTROLLER);
if (keys[1] != -1)
{
M_UnbindCommand(controller_bindnames[a->generic.localdata[0]][0], KEYS_CONTROLLER);
}
menukeyitem_bind = true;
Menu_SetStatusBar(&s_controller_buttons_menu, "press a button for this action");
}
static void
ControllerButtons_MenuInit(void)
{
int i;
s_controller_buttons_menu.x = (int)(viddef.width * 0.50f);
s_controller_buttons_menu.nitems = 0;
s_controller_buttons_menu.cursordraw = KeyCursorDrawFunc;
for (i = 0; i < NUM_CONTROLLER_BINDNAMES; i++)
{
s_controller_buttons_actions[i].generic.type = MTYPE_ACTION;
s_controller_buttons_actions[i].generic.flags = QMF_GRAYED;
s_controller_buttons_actions[i].generic.x = 0;
s_controller_buttons_actions[i].generic.y = (i * 9);
s_controller_buttons_actions[i].generic.ownerdraw = DrawControllerButtonBindingFunc;
s_controller_buttons_actions[i].generic.localdata[0] = i;
s_controller_buttons_actions[i].generic.name = controller_bindnames[s_controller_buttons_actions[i].generic.localdata[0]][1];
Menu_AddItem(&s_controller_buttons_menu, (void *)&s_controller_buttons_actions[i]);
}
Menu_SetStatusBar(&s_controller_buttons_menu, "BTN_A assigns, BTN_Y clears, BTN_B exits");
Menu_Center(&s_controller_buttons_menu);
}
static void
ControllerButtons_MenuDraw(void)
{
Menu_AdjustCursor(&s_controller_buttons_menu, 1);
Menu_Draw(&s_controller_buttons_menu);
}
static const char *
ControllerButtons_MenuKey(int key)
{
menuaction_s *item = (menuaction_s *)Menu_ItemAtCursor(&s_controller_buttons_menu);
if (menukeyitem_bind)
{
// Only controller buttons allowed
if (key >= K_JOY_FIRST_REGULAR && key != K_JOY_BACK)
{
char cmd[1024];
Com_sprintf(cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n",
Key_KeynumToString(key), controller_bindnames[item->generic.localdata[0]][0]);
Cbuf_InsertText(cmd);
}
Menu_SetStatusBar(&s_controller_buttons_menu, "BTN_A assigns, BTN_Y clears, BTN_B exits");
menukeyitem_bind = false;
return menu_out_sound;
}
key = Key_GetMenuKey(key);
switch (key)
{
case K_ENTER:
ControllerButtonBindingFunc(item);
return menu_in_sound;
case K_BACKSPACE:
M_UnbindCommand(controller_bindnames[item->generic.localdata[0]][0], KEYS_CONTROLLER);
return menu_out_sound;
default:
return Default_MenuKey(&s_controller_buttons_menu, key);
}
}
static void
M_Menu_ControllerButtons_f(void)
{
ControllerButtons_MenuInit();
M_PushMenu(ControllerButtons_MenuDraw, ControllerButtons_MenuKey);
}
/*
* JOY MENU
*/
@ -1161,6 +1343,13 @@ static menuslider_s s_joy_forwardsensitivity_slider;
static menuslider_s s_joy_sidesensitivity_slider;
static menuslider_s s_joy_upsensitivity_slider;
static menuslider_s s_joy_haptic_slider;
static menuaction_s s_joy_customize_buttons_action;
static void
CustomizeControllerButtonsFunc(void *unused)
{
M_Menu_ControllerButtons_f();
}
static void
HapticMagnitudeFunc(void *unused)
@ -1300,6 +1489,16 @@ Joy_MenuInit(void)
Menu_AddItem(&s_joy_menu, (void *)&s_joy_haptic_slider);
}
y += 10;
s_joy_customize_buttons_action.generic.type = MTYPE_ACTION;
s_joy_customize_buttons_action.generic.x = 0;
s_joy_customize_buttons_action.generic.y = y;
y += 10;
s_joy_customize_buttons_action.generic.name = "customize buttons";
s_joy_customize_buttons_action.generic.callback = CustomizeControllerButtonsFunc;
Menu_AddItem(&s_joy_menu, (void *)&s_joy_customize_buttons_action);
Menu_Center(&s_joy_menu);
}
@ -1669,7 +1868,7 @@ Options_MenuInit(void)
s_options_customize_joy_action.generic.type = MTYPE_ACTION;
s_options_customize_joy_action.generic.x = 0;
s_options_customize_joy_action.generic.y = 130;
s_options_customize_joy_action.generic.name = "customize joystick";
s_options_customize_joy_action.generic.name = "customize gamepad";
s_options_customize_joy_action.generic.callback = CustomizeJoyFunc;
s_options_customize_options_action.generic.type = MTYPE_ACTION;
@ -5041,6 +5240,7 @@ M_Init(void)
Cmd_AddCommand("menu_options", M_Menu_Options_f);
Cmd_AddCommand("menu_keys", M_Menu_Keys_f);
Cmd_AddCommand("menu_joy", M_Menu_Joy_f);
Cmd_AddCommand("menu_buttons", M_Menu_ControllerButtons_f);
Cmd_AddCommand("menu_quit", M_Menu_Quit_f);
/* initialize the server address book cvars (adr0, adr1, ...)