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.
This commit is contained in:
toaster 2022-04-01 20:51:37 +01:00 committed by GenericHeroGuy
parent a0348420f7
commit 144e967ee5
3 changed files with 106 additions and 51 deletions

View file

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

View file

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

View file

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