From 144e967ee59374b8656e53b03e18f2fe957cfd52 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 1 Apr 2022 20:51:37 +0100 Subject: [PATCH] Rework the entire G_PlayerInputAnalog system. * Previous control checking flow: * Current controller/keyboard (userbound controls). * If on a menu: * Current controller/keyboard (default controls). * All controllers not in use by a player (default controls). * New control checking flow: * Current controller/keyboard (userbound controls). * If player 0 and just checked a controller, check keyboard (userbound controls). * If on a menu: * Check all controllers not in use by a player (userbound controls). * If keys are inaccessible/unbound and keybind is necessary to navigate menus, repeat eveyrhting with default controls. * Instead of duplicated code, control the flow in a finer fashion. * Now able to detect if gamepad inputs are possible to recieve (via checking deviceID), instead of assuming they are. * If a keybind is set but inaccessible by the above metric, make it flash on the Profile Controls screen. * Fix out-of-order key mappings for a given bind being invisible on the Profile Controls menu. --- src/g_game.c | 103 ++++++++++++++++++++++++++------------------------ src/g_input.c | 45 ++++++++++++++++++++++ src/g_input.h | 9 ++++- 3 files changed, 106 insertions(+), 51 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 175b55557..3efdfe0ff 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -786,38 +786,14 @@ bound: return false; } -static INT32 KeyValue(UINT8 p, INT32 key, boolean menu) -{ - INT32 deviceID; - - if (key <= 0 || key >= NUMINPUTS) - { - return 0; - } - - if (menu == false) - { - deviceID = cv_usejoystick[p].value; - if (deviceID < 0 || deviceID >= MAXDEVICES) - { - return 0; - } - - return gamekeydown[deviceID][key]; - } - else - { - // Use keyboard as alternative for P1 menu. - return gamekeydown[0][key]; - } - - return 0; -} - INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu) { + INT32 deviceID; INT32 i; INT32 deadzone = 0; + boolean trydefaults = true; + boolean tryingotherID = false; + INT32 *controltable = &(gamecontrol[p][gc][0]); if (p >= MAXSPLITSCREENPLAYERS) { @@ -829,17 +805,24 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu) deadzone = (JOYAXISRANGE * cv_deadzone[p].value) / FRACUNIT; + deviceID = cv_usejoystick[p].value; + +retrygetcontrol: for (i = 0; i < MAXINPUTMAPPING; i++) { - INT32 key = gamecontrol[p][gc][i]; + INT32 key = controltable[i]; INT32 value = 0; - if (key <= 0 || key >= NUMINPUTS) + // Invalid key number. + if (!G_KeyIsAvailable(key, deviceID)) { continue; } - value = KeyValue(p, key, false); + // It's possible to access this control right now, so let's disable the default control backup for later. + trydefaults = false; + + value = gamekeydown[deviceID][key]; if (value >= deadzone) { @@ -847,29 +830,51 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu) } } - if (p == 0 && menu == true) + // If you're on controller, try your keyboard-based binds as an immediate backup. + if (p == 0 && deviceID > 0 && !tryingotherID) { - // We don't want menus to become unnavigable if people unbind - // all of their controls, so use the default control scheme in - // this scenario. + deviceID = 0; + goto retrygetcontrol; + } - for (i = 0; i < MAXINPUTMAPPING; i++) + if (menu == false) + { + return 0; + } + + // 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) + { + deviceID = MAXDEVICES; + tryingotherID = true; + } +loweringid: + deviceID--; + if (deviceID > 0) + { + for (i = 0; i < cv_splitplayers.value-1; i++) { - INT32 key = gamecontroldefault[gc][i]; - INT32 value = 0; - - if (key <= 0 || key >= NUMINPUTS) - { + if (deviceID != cv_usejoystick[i].value) continue; - } - - value = KeyValue(p, key, true); - - if (value >= deadzone) - { - return value; - } + // Controller taken? Try again... + goto loweringid; } + goto retrygetcontrol; + } + + if (trydefaults && G_KeyBindIsNecessary(gc)) + { + // 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 = cv_usejoystick[p].value; + goto retrygetcontrol; + } return 0; diff --git a/src/g_input.c b/src/g_input.c index 426e4e780..d64791758 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -418,6 +418,51 @@ static const char *gamecontrolname[num_gamecontrols] = #define NUMKEYNAMES (sizeof (keynames)/sizeof (keyname_t)) +// If keybind is necessary to navigate menus, it's on this list. +boolean G_KeyBindIsNecessary(INT32 gc) +{ + switch (gc) + { + case gc_accelerate: + case gc_brake: + case gc_aimforward: + case gc_aimbackward: + case gc_turnleft: + case gc_turnright: + case gc_systemmenu: + return true; + default: + return false; + } + return false; +} + +// Returns false if a key is deemed unreachable for this device. +boolean G_KeyIsAvailable(INT32 key, INT32 deviceID) +{ + // 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) + { + 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 && ????????) + { + return false; + } + */ + + return true; +} + // // Detach any keys associated to the given game control // - pass the pointer to the gamecontrol table for the player being edited diff --git a/src/g_input.h b/src/g_input.h index b88c26d07..f24ba4871 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -41,13 +41,15 @@ typedef enum KEY_JOY1 = NUMKEYS, KEY_HAT1 = KEY_JOY1 + JOYBUTTONS, KEY_AXIS1 = KEY_HAT1 + JOYHATS*4, + JOYINPUTEND = KEY_AXIS1 + JOYAXISSET*2*2, // 4 sets of 2 axes, each with positive & negative - KEY_MOUSE1 = KEY_AXIS1 + JOYAXISSET*4, + KEY_MOUSE1 = JOYINPUTEND, KEY_MOUSEMOVE = KEY_MOUSE1 + MOUSEBUTTONS, KEY_MOUSEWHEELUP = KEY_MOUSEMOVE + 4, KEY_MOUSEWHEELDOWN = KEY_MOUSEWHEELUP + 1, + MOUSEINPUTEND = KEY_MOUSEWHEELDOWN + 1, - NUMINPUTS = KEY_MOUSEWHEELDOWN + 1, + NUMINPUTS = MOUSEINPUTEND, } key_input_e; typedef enum @@ -126,6 +128,9 @@ void G_MapEventsToControls(event_t *ev); const char *G_KeynumToString(INT32 keynum); INT32 G_KeyStringtoNum(const char *keystr); +boolean G_KeyBindIsNecessary(INT32 gc); +boolean G_KeyIsAvailable(INT32 key, INT32 deviceID); + // detach any keys associated to the given game control void G_ClearControlKeys(INT32 (*setupcontrols)[MAXINPUTMAPPING], INT32 control); void G_ClearAllControlKeys(void);