G_PlayerAnalogInput cleanup

Emergency keyboard keys work again, and the input code is more straight-forward.
This commit is contained in:
Sally Coolatta 2023-03-07 05:01:26 -05:00
parent 7ed5e7a7a5
commit edd5398373
4 changed files with 131 additions and 160 deletions

View file

@ -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;
}

View file

@ -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)

View file

@ -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

View file

@ -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;
}