From edd53983736d2eb4292ac6eca7f019c818c5b8e7 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 7 Mar 2023 05:01:26 -0500 Subject: [PATCH] G_PlayerAnalogInput cleanup Emergency keyboard keys work again, and the input code is more straight-forward. --- src/g_game.c | 223 +++++++++++++++++++-------------------------- src/g_input.c | 42 ++++++--- src/g_input.h | 4 +- src/sdl/i_system.c | 22 +---- 4 files changed, 131 insertions(+), 160 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 0e02a678f..9e8ac50ed 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -863,33 +863,48 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming) return (INT16)((*aiming)>>16); } -// Default controls for keyboard. These are hardcoded and cannot be changed. -static INT32 keyboardMenuDefaults[][2] = { - {gc_a, KEY_ENTER}, - {gc_c, KEY_BACKSPACE}, - {gc_x, KEY_ESCAPE}, - {gc_left, KEY_LEFTARROW}, - {gc_right, KEY_RIGHTARROW}, - {gc_up, KEY_UPARROW}, - {gc_down, KEY_DOWNARROW}, +static INT32 G_GetValueFromControlTable(INT32 deviceID, INT32 deadzone, INT32 *controltable) +{ + INT32 i; - // special control - {gc_start, KEY_ESCAPE}, - // 8 total controls* -}; + if (deviceID <= UNASSIGNED_DEVICE) + { + // An invalid device can't have any binds! + return 0; + } -#define KEYBOARDDEFAULTSSPLIT 7 + for (i = 0; i < MAXINPUTMAPPING; i++) + { + INT32 key = controltable[i]; + INT32 value = 0; + // Invalid key number. + if (G_KeyIsAvailable(key, deviceID) == false) + { + continue; + } + + value = G_GetDeviceGameKeyDownArray(deviceID)[key]; + + if (value >= deadzone) + { + return value; + } + } + + // Not pressed. + return 0; +} INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers) { - INT32 deviceID; + const INT32 deadzone = (JOYAXISRANGE * cv_deadzone[p].value) / FRACUNIT; + const INT32 keyboard_player = G_GetPlayerForDevice(KEYBOARD_MOUSE_DEVICE); + const boolean is_main_menu_controller = (p == 0 && menuPlayers > 0); + INT32 deviceID = UNASSIGNED_DEVICE; + INT32 value = -1; INT32 avail_gamepad_id = 0; - INT32 i, j; - INT32 deadzone = 0; - boolean trydefaults = true; - boolean tryingotherID = false; - INT32 *controltable = &(gamecontrol[p][gc][0]); + INT32 i; if (p >= MAXSPLITSCREENPLAYERS) { @@ -899,139 +914,93 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers) return 0; } - deadzone = (JOYAXISRANGE * cv_deadzone[p].value) / FRACUNIT; - deviceID = G_GetDeviceForPlayer(p); - if (deviceID == -1) + if ((menuPlayers > 0 && G_KeyBindIsNecessary(gc) == true) // In menu: check for all unoverrideable menu default controls. + || (menuPlayers == 0 && gc == gc_start)) // In gameplay: check for the unoverrideable start button to be able to bring up the menu. { - INT32 keyboard_player = G_GetPlayerForDevice(KEYBOARD_MOUSE_DEVICE); - - // Player 1 is always allowed to use the keyboard in 1P (there is a check for splitscreen later in this func) - if (p == KEYBOARD_MOUSE_DEVICE && keyboard_player == -1) + value = G_GetValueFromControlTable(KEYBOARD_MOUSE_DEVICE, JOYAXISRANGE/4, &(menucontrolreserved[gc][0])); + if (value > 0) // Check for press instead of bound. { - deviceID = KEYBOARD_MOUSE_DEVICE; - } - else - { - goto deviceunassigned; - } - } - -retrygetcontrol: - for (i = 0; i < MAXINPUTMAPPING; i++) - { - INT32 key = controltable[i]; - INT32 menukey = KEY_NULL; - INT32 value = 0; - boolean processinput = true; - - // for menus, keyboards have defaults! - if (deviceID == 0) - { - - // In menus, check indexes 0 through 5 (everything besides gc_start) - // Outside of menus, only consider the hardcoded input for gc_start at index 6 - - INT32 maxj = menuactive ? KEYBOARDDEFAULTSSPLIT : KEYBOARDDEFAULTSSPLIT+1; - j = (!menuactive) ? KEYBOARDDEFAULTSSPLIT : 0; - - for (; j < maxj; j++) // check keyboardMenuDefaults - { - // the gc we're looking for - if (gc == keyboardMenuDefaults[j][0]) - { - menukey = keyboardMenuDefaults[j][1]; - break; - } - - // The key is mapped to *something else*...? - // Then don't process that as it would conflict with our hardcoded inputs. - else if (key == keyboardMenuDefaults[j][1]) - { - processinput = false; - break; - } - } - } - - // Invalid key number. - if (!G_KeyIsAvailable(key, deviceID) && !G_KeyIsAvailable(menukey, deviceID)) - { - continue; - } - - if (processinput) - { - // It's possible to access this control right now, so let's disable the default control backup for later. - trydefaults = false; - - value = G_GetDeviceGameKeyDownArray(deviceID)[key]; - if (menukey && G_GetDeviceGameKeyDownArray(deviceID)[menukey]) - value = G_GetDeviceGameKeyDownArray(deviceID)[menukey]; - - if (value >= deadzone) + // This is only intended for P1. + if (p == 0) { return value; } + else + { + return 0; + } } } -deviceunassigned: - - // If you're on controller, try your keyboard-based binds as an immediate backup. - // Do not do this if there are more than 1 local player. - if (p == 0 && deviceID > 0 && !tryingotherID && menuPlayers < 2 && !splitscreen) + // Player 1 is always allowed to use the keyboard in 1P, even if they got disconnected. + if (p == 0 && keyboard_player == -1 && deviceID == UNASSIGNED_DEVICE) { deviceID = KEYBOARD_MOUSE_DEVICE; - goto retrygetcontrol; } - if (menuPlayers == 0) + // First, try our actual binds. + value = G_GetValueFromControlTable(deviceID, deadzone, &(gamecontrol[p][gc][0])); + if (value > 0) { - return 0; + return value; } - // We don't want menus to become unnavigable if people unbind - // all of their controls, so we do several things in this scenario. - // First: try other controllers. - - if (!tryingotherID) + // If you're on gamepad in 1P, and you didn't have a gamepad bind for this, then try your keyboard binds. + if (p == 0 && keyboard_player == -1 && deviceID > KEYBOARD_MOUSE_DEVICE) { - avail_gamepad_id = 0; - tryingotherID = true; - } -loweringid: - if (avail_gamepad_id >= G_GetNumAvailableGamepads()) - { - return 0; - } - deviceID = G_GetAvailableGamepadDevice(avail_gamepad_id); - avail_gamepad_id += 1; - if (deviceID > 0) - { - for (i = 0; i < menuPlayers; i++) + value = G_GetValueFromControlTable(KEYBOARD_MOUSE_DEVICE, deadzone, &(gamecontrol[p][gc][0])); + if (value > 0) { - if (deviceID != G_GetDeviceForPlayer(i)) - continue; - // Controller taken? Try again... - goto loweringid; + return value; } - goto retrygetcontrol; } - if (trydefaults && G_KeyBindIsNecessary(gc)) + if (is_main_menu_controller == true) { - // If we still haven't found anything and the keybind is necessary, - // try it all again but with default binds. - trydefaults = false; - controltable = &(gamecontroldefault[gc][0]); - tryingotherID = false; - deviceID = G_GetDeviceForPlayer(p); - goto retrygetcontrol; + // We are P1 controlling menus. We should be able to + // control the menu with any unused gamepads, so + // that gamepads are able to navigate to the player + // setup menu in the first place. + for (avail_gamepad_id = 0; avail_gamepad_id < G_GetNumAvailableGamepads(); avail_gamepad_id++) + { + INT32 tryDevice = G_GetAvailableGamepadDevice(avail_gamepad_id); + if (tryDevice <= KEYBOARD_MOUSE_DEVICE) + { + continue; + } + for (i = 0; i < menuPlayers; i++) + { + if (tryDevice == G_GetDeviceForPlayer(i)) + { + // Don't do this for already taken devices. + break; + } + } + + if (i == menuPlayers) + { + // This gamepad isn't being used, so we can + // use it for P1 menu navigation. + value = G_GetValueFromControlTable(tryDevice, deadzone, &(gamecontrol[p][gc][0])); + if (value > 0) + { + return value; + } + } + } + + // Still nothing bound after everything. Try default gamepad controls. + value = G_GetValueFromControlTable(deviceID, deadzone, &(gamecontroldefault[gc][0])); + if (value > 0) + { + return value; + } } + // Literally not bound at all, so it can't be pressed at all. return 0; } diff --git a/src/g_input.c b/src/g_input.c index df87ae4f9..bdb6bbb9a 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -42,6 +42,7 @@ INT32 gamekeydown[MAXDEVICES][NUMINPUTS]; // two key codes (or virtual key) per game control INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][MAXINPUTMAPPING]; INT32 gamecontroldefault[num_gamecontrols][MAXINPUTMAPPING]; // default control storage +INT32 menucontrolreserved[num_gamecontrols][MAXINPUTMAPPING]; // lists of GC codes for selective operation /* @@ -682,12 +683,13 @@ boolean G_KeyBindIsNecessary(INT32 gc) switch (gc) { case gc_a: - case gc_b: + case gc_c: + case gc_x: case gc_up: case gc_down: case gc_left: case gc_right: - case gc_start: + //case gc_start: // Is necessary, but handled special. return true; default: return false; @@ -698,25 +700,31 @@ boolean G_KeyBindIsNecessary(INT32 gc) // Returns false if a key is deemed unreachable for this device. boolean G_KeyIsAvailable(INT32 key, INT32 deviceID) { + boolean gamepad_key = false; + // Invalid key number. if (key <= 0 || key >= NUMINPUTS) { return false; } - // Valid controller-specific virtual key, but no controller attached for player. - if (key >= KEY_JOY1 && key < JOYINPUTEND && deviceID <= 0) + // Only allow gamepad keys for gamepad devices, + // and vice versa. + gamepad_key = (key >= KEY_JOY1 && key < JOYINPUTEND); + if (deviceID == KEYBOARD_MOUSE_DEVICE) { - return false; + if (gamepad_key == true) + { + return false; + } } - - // Valid mouse-specific virtual key, but no mouse attached for player. TODO HOW TO DETECT ACTIVE MOUSE CONNECTION - /* - if (key >= KEY_MOUSE1 && key < MOUSEINPUTEND && ????????) + else { - return false; + if (gamepad_key == false) + { + return false; + } } - */ return true; } @@ -815,7 +823,7 @@ void G_DefineDefaultControls(void) gamecontroldefault[gc_z ][0] = 'd'; gamecontroldefault[gc_l ][0] = 'q'; gamecontroldefault[gc_r ][0] = 'e'; - gamecontroldefault[gc_start ][0] = KEY_ESCAPE; // * + gamecontroldefault[gc_start ][0] = KEY_ESCAPE; gamecontroldefault[gc_rankings][0] = KEY_TAB; // Gamepad controls @@ -837,6 +845,16 @@ void G_DefineDefaultControls(void) gamecontroldefault[gc_down ][2] = KEY_AXIS1+3; // Axis Y+ gamecontroldefault[gc_left ][2] = KEY_AXIS1+0; // Axis X- gamecontroldefault[gc_right][2] = KEY_AXIS1+1; // Axis X+ + + // Menu reserved controls + menucontrolreserved[gc_up ][0] = KEY_UPARROW; + menucontrolreserved[gc_down ][0] = KEY_DOWNARROW; + menucontrolreserved[gc_left ][0] = KEY_LEFTARROW; + menucontrolreserved[gc_right][0] = KEY_RIGHTARROW; + menucontrolreserved[gc_a ][0] = KEY_ENTER; + menucontrolreserved[gc_c ][0] = KEY_BACKSPACE; + menucontrolreserved[gc_x ][0] = KEY_ESCAPE; + menucontrolreserved[gc_start][0] = KEY_ESCAPE; // Handled special } void G_CopyControls(INT32 (*setupcontrols)[MAXINPUTMAPPING], INT32 (*fromcontrols)[MAXINPUTMAPPING], const INT32 *gclist, INT32 gclen) diff --git a/src/g_input.h b/src/g_input.h index 55b22b0a1..610c3afdc 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -107,12 +107,14 @@ extern consvar_t cv_controlperkey; // current state of the keys: JOYAXISRANGE or 0 when boolean. // Or anything inbetween for analog values #define MAXDEVICES (MAXGAMEPADS + 1) // Gamepads + keyboard & mouse -#define KEYBOARD_MOUSE_DEVICE 0 +#define KEYBOARD_MOUSE_DEVICE (0) +#define UNASSIGNED_DEVICE (-1) extern INT32 gamekeydown[MAXDEVICES][NUMINPUTS]; // several key codes (or virtual key) per game control extern INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][MAXINPUTMAPPING]; extern INT32 gamecontroldefault[num_gamecontrols][MAXINPUTMAPPING]; // default control storage +extern INT32 menucontrolreserved[num_gamecontrols][MAXINPUTMAPPING]; /* #define num_gcl_accelerate 1 diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 186d85030..43099a4ff 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -194,25 +194,7 @@ static char returnWadPath[256]; #include "../byteptr.h" #endif -/** \brief The JoyReset function - - \param JoySet Joystick info to reset - - \return void -*/ -static void JoyReset(SDLJoyInfo_t *JoySet) -{ - JoySet->dev = NULL; - JoySet->oldjoy = -1; - JoySet->axises = JoySet->buttons = JoySet->hats = JoySet->balls = 0; - //JoySet->scale -} - -/** \brief First joystick up and running -*/ -static INT32 joystick_started[MAXSPLITSCREENPLAYERS] = {0,0,0,0}; - -/** \brief SDL info about joystick 1 +/** \brief SDL info about joysticks */ SDLJoyInfo_t JoyInfo[MAXSPLITSCREENPLAYERS]; @@ -1041,7 +1023,7 @@ const char *I_GetJoyName(INT32 joyindex) joyname[0] = 0; joyindex--; //SDL's Joystick System starts at 0, not 1 - if (!SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) + if (SDL_WasInit(SDL_INIT_JOYSTICK) != SDL_INIT_JOYSTICK) { return joyname; }