mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
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:
parent
e3f9a925d8
commit
ff5992e3c4
4 changed files with 174 additions and 107 deletions
148
src/g_game.c
148
src/g_game.c
|
|
@ -660,70 +660,14 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming)
|
|||
return (INT16)((*aiming)>>16);
|
||||
}
|
||||
|
||||
static INT32 KeyValue(UINT8 p, INT32 key, UINT8 menuPlayers)
|
||||
{
|
||||
INT32 deviceID;
|
||||
INT32 i, j;
|
||||
|
||||
if (key <= 0 || key >= NUMINPUTS)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
deviceID = cv_usejoystick[p].value;
|
||||
|
||||
if (menuPlayers > 0)
|
||||
{
|
||||
// Try every device that does NOT belong to another player.
|
||||
for (i = MAXDEVICES-1; i >= 0; i--)
|
||||
{
|
||||
if (i == deviceID)
|
||||
{
|
||||
// We've tried this one multiple times :V
|
||||
continue;
|
||||
}
|
||||
|
||||
if (menuPlayers > 1)
|
||||
{
|
||||
for (j = 1; j < menuPlayers; j++)
|
||||
{
|
||||
if (i == cv_usejoystick[j].value)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j < menuPlayers)
|
||||
{
|
||||
// This one's taken.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (gamekeydown[i][key] != 0)
|
||||
{
|
||||
return gamekeydown[i][key];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (deviceID < 0 || deviceID >= MAXDEVICES)
|
||||
{
|
||||
// Device is unset
|
||||
return 0;
|
||||
}
|
||||
|
||||
return gamekeydown[deviceID][key];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers)
|
||||
{
|
||||
INT32 deviceID;
|
||||
INT32 i;
|
||||
INT32 deadzone = 0;
|
||||
boolean trydefaults = true;
|
||||
boolean tryingotherID = false;
|
||||
INT32 *controltable = &(gamecontrol[p][gc][0]);
|
||||
|
||||
if (p >= MAXSPLITSCREENPLAYERS)
|
||||
{
|
||||
|
|
@ -735,17 +679,24 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers)
|
|||
|
||||
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)
|
||||
{
|
||||
|
|
@ -753,42 +704,51 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers)
|
|||
}
|
||||
}
|
||||
|
||||
if (menuPlayers != 0)
|
||||
// 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 we do several things in this scenario.
|
||||
deviceID = 0;
|
||||
goto retrygetcontrol;
|
||||
}
|
||||
|
||||
// First: check the same device, but with default binds.
|
||||
for (i = 0; i < MAXINPUTMAPPING; i++)
|
||||
if (menuPlayers == 0)
|
||||
{
|
||||
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 < menuPlayers; 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, false);
|
||||
|
||||
if (value >= deadzone)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (p == 0 && menuPlayers == 1)
|
||||
{
|
||||
// Second: if we're Player 1 and there are no other players,
|
||||
// then we can use keyboard defaults as a final resort.
|
||||
|
||||
value = KeyValue(p, key, menuPlayers);
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -417,6 +417,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_a:
|
||||
case gc_b:
|
||||
case gc_up:
|
||||
case gc_down:
|
||||
case gc_left:
|
||||
case gc_right:
|
||||
case gc_start:
|
||||
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
|
||||
|
|
|
|||
|
|
@ -37,13 +37,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*2*2, // 4 sets of 2 axes, each with positive & negative
|
||||
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
|
||||
|
|
@ -134,6 +136,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);
|
||||
|
|
|
|||
|
|
@ -2337,7 +2337,7 @@ void M_DrawEditProfile(void)
|
|||
V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL);
|
||||
if (currentMenu->menuitems[itemOn].tooltip != NULL)
|
||||
{
|
||||
V_DrawCenteredThinString(BASEVIDWIDTH*2/3, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, currentMenu->menuitems[itemOn].tooltip);
|
||||
V_DrawCenteredThinString(224, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, currentMenu->menuitems[itemOn].tooltip);
|
||||
}
|
||||
|
||||
// Draw the menu options...
|
||||
|
|
@ -2480,18 +2480,76 @@ void M_DrawProfileControls(void)
|
|||
}
|
||||
else if (currentMenu->menuitems[i].status & IT_CONTROL)
|
||||
{
|
||||
// Draw what the controls are mapped to
|
||||
UINT32 vflags = V_6WIDTHSPACE;
|
||||
INT32 gc = currentMenu->menuitems[i].mvar1;
|
||||
UINT8 available = 0, set = 0;
|
||||
|
||||
// Get userbound controls...
|
||||
for (k = 0; k < MAXINPUTMAPPING; k++)
|
||||
keys[k] = optionsmenu.profile->controls[currentMenu->menuitems[i].mvar1][k];
|
||||
{
|
||||
keys[k] = optionsmenu.profile->controls[gc][k];
|
||||
if (keys[k] == KEY_NULL)
|
||||
continue;
|
||||
set++;
|
||||
if (!G_KeyIsAvailable(keys[k], cv_usejoystick[0].value))
|
||||
continue;
|
||||
available++;
|
||||
};
|
||||
|
||||
buf[0] = '\0';
|
||||
|
||||
if (keys[0] == KEY_NULL) // If the first key's null, so should every other.
|
||||
strcpy(buf, "\x85NOT BOUND");
|
||||
// Can't reach any of them?
|
||||
if (available == 0)
|
||||
{
|
||||
if (((3*optionsmenu.ticker)/(2*TICRATE)) & 1) // 1.5 seconds
|
||||
{
|
||||
vflags |= V_ORANGEMAP;
|
||||
#ifdef SHOWCONTROLDEFAULT
|
||||
if (G_KeyBindIsNecessary(gc))
|
||||
{
|
||||
// Get the defaults for essential keys.
|
||||
// Went through all the trouble of making this look cool,
|
||||
// then realised defaulting should only apply to menus.
|
||||
// Too much opportunity for confusion if kept.
|
||||
for (k = 0; k < MAXINPUTMAPPING; k++)
|
||||
{
|
||||
keys[k] = gamecontroldefault[gc][k];
|
||||
if (keys[k] == KEY_NULL)
|
||||
continue;
|
||||
available++;
|
||||
}
|
||||
set = available;
|
||||
}
|
||||
else if (set)
|
||||
#else
|
||||
if (!set)
|
||||
{
|
||||
if (!G_KeyBindIsNecessary(gc))
|
||||
vflags = V_REDMAP|V_6WIDTHSPACE;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
strcpy(buf, "CURRENTLY UNAVAILABLE");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vflags |= V_REDMAP;
|
||||
}
|
||||
}
|
||||
|
||||
if (buf[0])
|
||||
;
|
||||
else if (!set)
|
||||
strcpy(buf, "NOT BOUND");
|
||||
else
|
||||
{
|
||||
for (k=0; k < MAXINPUTMAPPING && keys[k] != KEY_NULL; k++)
|
||||
for (k = 0; k < MAXINPUTMAPPING; k++)
|
||||
{
|
||||
if (keys[k] == KEY_NULL)
|
||||
continue;
|
||||
|
||||
if (k > 0)
|
||||
strcat(buf," / ");
|
||||
|
||||
|
|
@ -2499,18 +2557,17 @@ void M_DrawProfileControls(void)
|
|||
strcat(buf, "\n");
|
||||
|
||||
strcat(buf, G_KeynumToString (keys[k]));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// don't shift the text if we didn't draw a patch.
|
||||
V_DrawThinString(x+ (drawnpatch ? 32 : 0), y+ (drawnpatch? 2 : 12), V_6WIDTHSPACE, buf);
|
||||
V_DrawThinString(x + (drawnpatch ? 32 : 0), y + (drawnpatch ? 2 : 12), vflags, buf);
|
||||
|
||||
// controller dest coords:
|
||||
if (itemOn == i && currentMenu->menuitems[i].mvar1 && currentMenu->menuitems[i].mvar1 <= gc_start)
|
||||
if (itemOn == i && gc > 0 && gc <= gc_start)
|
||||
{
|
||||
optionsmenu.tcontx = controlleroffsets[currentMenu->menuitems[i].mvar1][0];
|
||||
optionsmenu.tconty = controlleroffsets[currentMenu->menuitems[i].mvar1][1];
|
||||
optionsmenu.tcontx = controlleroffsets[gc][0];
|
||||
optionsmenu.tconty = controlleroffsets[gc][1];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue