From 0aa44afa139f210427af4a554ef3a3329875823e Mon Sep 17 00:00:00 2001 From: Jaime Moreira Date: Sat, 7 May 2022 23:55:00 -0400 Subject: [PATCH] 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. --- src/client/header/keyboard.h | 1 + src/client/menu/header/qmenu.h | 6 + src/client/menu/menu.c | 234 ++++++++++++++++++++++++++++++--- 3 files changed, 224 insertions(+), 17 deletions(-) diff --git a/src/client/header/keyboard.h b/src/client/header/keyboard.h index d0ffe9f1..60ce807e 100644 --- a/src/client/header/keyboard.h +++ b/src/client/header/keyboard.h @@ -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, diff --git a/src/client/menu/header/qmenu.h b/src/client/menu/header/qmenu.h index 2cbf9eef..a8f3cde6 100644 --- a/src/client/menu/header/qmenu.h +++ b/src/client/menu/header/qmenu.h @@ -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; diff --git a/src/client/menu/menu.c b/src/client/menu/menu.c index 8cdc07e3..bf996d4b 100644 --- a/src/client/menu/menu.c +++ b/src/client/menu/menu.c @@ -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, ...)