mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Menus/Controls: overhaul multiple bindings input
- Replace the old method of adding each binding one at a time - Hold up to 4 inputs at once, then release to bind those inputs to the control - Changing the bindings overwrites the old bindings, instead of adding to them
This commit is contained in:
parent
9523fd8652
commit
ae5a20756e
5 changed files with 91 additions and 170 deletions
|
|
@ -997,10 +997,10 @@ extern struct optionsmenu_s {
|
|||
// This is only applied to the profile when you exit out of the controls menu.
|
||||
|
||||
INT16 controlscroll; // scrolling for the control menu....
|
||||
UINT8 bindcontrol; // 0: not binding, 1: binding control #1, 2: binding control #2
|
||||
INT16 bindtimer; // Timer until binding is cancelled (5s)
|
||||
UINT16 bindben; // Hold right timer
|
||||
UINT8 bindben_swallow; // (bool) control is about to be cleared; (int) swallow/pose animation timer
|
||||
INT32 bindinputs[MAXINPUTMAPPING]; // Set while binding
|
||||
|
||||
INT16 trycontroller; // Starts at 3*TICRATE, holding B lowers this, when at 0, cancel controller try mode.
|
||||
|
||||
|
|
|
|||
|
|
@ -4854,6 +4854,23 @@ static void M_DrawBindBen(INT32 x, INT32 y, INT32 scroll_remaining)
|
|||
V_DrawMappedPatch(x-30, y, 0, W_CachePatchName(va("PR_BIN%c%c", state, '1' + frame), PU_CACHE), aquamap);
|
||||
}
|
||||
|
||||
static void M_DrawBindMediumString(INT32 y, INT32 flags, const char *string)
|
||||
{
|
||||
fixed_t w = V_StringScaledWidth(FRACUNIT, FRACUNIT, FRACUNIT, flags, MED_FONT, string);
|
||||
fixed_t x = BASEVIDWIDTH/2 * FRACUNIT - w/2;
|
||||
V_DrawStringScaled(
|
||||
x,
|
||||
y * FRACUNIT,
|
||||
FRACUNIT,
|
||||
FRACUNIT,
|
||||
FRACUNIT,
|
||||
flags,
|
||||
NULL,
|
||||
MED_FONT,
|
||||
string
|
||||
);
|
||||
}
|
||||
|
||||
// the control stuff.
|
||||
// Dear god.
|
||||
void M_DrawProfileControls(void)
|
||||
|
|
@ -5158,7 +5175,7 @@ void M_DrawProfileControls(void)
|
|||
}
|
||||
|
||||
// Overlay for control binding
|
||||
if (optionsmenu.bindcontrol)
|
||||
if (optionsmenu.bindtimer)
|
||||
{
|
||||
INT16 reversetimer = TICRATE*5 - optionsmenu.bindtimer;
|
||||
INT32 fade = reversetimer;
|
||||
|
|
@ -5167,14 +5184,33 @@ void M_DrawProfileControls(void)
|
|||
if (fade > 9)
|
||||
fade = 9;
|
||||
|
||||
ypos = (BASEVIDHEIGHT/2) - 4 +16*(9 - fade);
|
||||
ypos = (BASEVIDHEIGHT/2) - 20 +16*(9 - fade);
|
||||
V_DrawFadeScreen(31, fade);
|
||||
|
||||
M_DrawTextBox((BASEVIDWIDTH/2) - (120), ypos - 12, 30, 4);
|
||||
M_DrawTextBox((BASEVIDWIDTH/2) - (120), ypos - 12, 30, 8);
|
||||
|
||||
V_DrawCenteredString(BASEVIDWIDTH/2, ypos, 0, va("Press key #%d for control", optionsmenu.bindcontrol));
|
||||
V_DrawCenteredString(BASEVIDWIDTH/2, ypos +10, 0, va("\"%s\"", currentMenu->menuitems[itemOn].text));
|
||||
V_DrawCenteredString(BASEVIDWIDTH/2, ypos +20, highlightflags, va("(WAIT %d SECONDS TO SKIP)", optionsmenu.bindtimer/TICRATE));
|
||||
V_DrawCenteredMenuString(BASEVIDWIDTH/2, ypos, V_GRAYMAP, "Hold and release inputs for");
|
||||
V_DrawCenteredMenuString(BASEVIDWIDTH/2, ypos + 10, V_GRAYMAP, va("\"%s\"", currentMenu->menuitems[itemOn].text));
|
||||
|
||||
if (optionsmenu.bindtimer > 0)
|
||||
{
|
||||
M_DrawBindMediumString(
|
||||
ypos + 50,
|
||||
highlightflags,
|
||||
va("(WAIT %d SEC TO SKIP)", (optionsmenu.bindtimer + (TICRATE-1)) / TICRATE)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < MAXINPUTMAPPING && optionsmenu.bindinputs[i]; ++i)
|
||||
{
|
||||
M_DrawBindMediumString(
|
||||
ypos + (2 + i)*10,
|
||||
highlightflags | V_FORCEUPPERCASE,
|
||||
G_KeynumToString(optionsmenu.bindinputs[i])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ boolean M_Responder(event_t *ev)
|
|||
|
||||
// Profiles: Control mapping.
|
||||
// We take the WHOLE EVENT for convenience.
|
||||
if (optionsmenu.bindcontrol)
|
||||
if (optionsmenu.bindtimer)
|
||||
{
|
||||
M_MapProfileControl(ev);
|
||||
return true; // eat events.
|
||||
|
|
|
|||
|
|
@ -225,7 +225,6 @@ void M_ProfileDeviceSelect(INT32 choice)
|
|||
|
||||
// While we're here, setup the incoming controls menu to reset the scroll & bind status:
|
||||
optionsmenu.controlscroll = 0;
|
||||
optionsmenu.bindcontrol = 0;
|
||||
optionsmenu.bindtimer = 0;
|
||||
|
||||
optionsmenu.lastkey = 0;
|
||||
|
|
|
|||
|
|
@ -176,14 +176,10 @@ void M_HandleProfileControls(void)
|
|||
optionsmenu.controlscroll = 0;
|
||||
|
||||
// bindings, cancel if timer is depleted.
|
||||
if (optionsmenu.bindcontrol)
|
||||
if (optionsmenu.bindtimer)
|
||||
{
|
||||
if (optionsmenu.bindtimer > 0)
|
||||
optionsmenu.bindtimer--;
|
||||
if (!optionsmenu.bindtimer)
|
||||
{
|
||||
optionsmenu.bindcontrol = 0; // we've gone past the max, just stop.
|
||||
}
|
||||
|
||||
}
|
||||
else if (currentMenu->menuitems[itemOn].mvar1) // check if we're on a valid menu option...
|
||||
{
|
||||
|
|
@ -308,7 +304,7 @@ boolean M_ProfileControlsInputs(INT32 ch)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (optionsmenu.bindcontrol)
|
||||
if (optionsmenu.bindtimer)
|
||||
return true; // Eat all inputs there. We'll use a stupid hack in M_Responder instead.
|
||||
|
||||
//SetDeviceOnPress(); // Update device constantly so that we don't stay stuck with otpions saying a device is unavailable just because we're mapping multiple devices...
|
||||
|
|
@ -347,25 +343,11 @@ boolean M_ProfileControlsInputs(INT32 ch)
|
|||
|
||||
void M_ProfileSetControl(INT32 ch)
|
||||
{
|
||||
INT32 controln = currentMenu->menuitems[itemOn].mvar1;
|
||||
UINT8 i;
|
||||
(void) ch;
|
||||
|
||||
optionsmenu.bindcontrol = 1; // Default to control #1
|
||||
|
||||
for (i = 0; i < MAXINPUTMAPPING; i++)
|
||||
{
|
||||
if (optionsmenu.tempcontrols[controln][i] == KEY_NULL)
|
||||
{
|
||||
optionsmenu.bindcontrol = i+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we could find a null key to map into, map there.
|
||||
// Otherwise, this will stay at 1 which means we'll overwrite the first bound control.
|
||||
|
||||
optionsmenu.bindtimer = TICRATE*5;
|
||||
memset(optionsmenu.bindinputs, 0, sizeof optionsmenu.bindinputs);
|
||||
G_ResetAllDeviceGameKeyDown();
|
||||
}
|
||||
|
||||
static void M_ProfileDefaultControlsResponse(INT32 ch)
|
||||
|
|
@ -417,157 +399,61 @@ void M_ProfileClearControls(INT32 ch)
|
|||
#define KEYHOLDFOR 1
|
||||
void M_MapProfileControl(event_t *ev)
|
||||
{
|
||||
INT32 c = 0;
|
||||
UINT8 n = optionsmenu.bindcontrol-1; // # of input to bind
|
||||
INT32 controln = currentMenu->menuitems[itemOn].mvar1; // gc_
|
||||
UINT8 where = n; // By default, we'll save the bind where we're supposed to map.
|
||||
INT32 i;
|
||||
if (ev->type == ev_keydown && ev->data2) // ignore repeating keys
|
||||
return;
|
||||
|
||||
if (optionsmenu.bindtimer > TICRATE*5 - 9) // grace period after entering the bind dialog
|
||||
return;
|
||||
|
||||
INT32 *DeviceGameKeyDownArray = G_GetDeviceGameKeyDownArray(ev->device);
|
||||
|
||||
if (!DeviceGameKeyDownArray)
|
||||
return;
|
||||
|
||||
//SetDeviceOnPress(); // Update player gamepad assignments
|
||||
// Find every held button.
|
||||
boolean noinput = true;
|
||||
for (INT32 c = 1; c < NUMINPUTS; ++c)
|
||||
{
|
||||
if (DeviceGameKeyDownArray[c] < 3*JOYAXISRANGE/4)
|
||||
continue;
|
||||
|
||||
// Only consider keydown and joystick events to make sure we ignore ev_mouse and other events
|
||||
// See also G_MapEventsToControls
|
||||
switch (ev->type)
|
||||
noinput = false;
|
||||
|
||||
for (UINT8 i = 0; i < MAXINPUTMAPPING; ++i)
|
||||
{
|
||||
case ev_keydown:
|
||||
if (ev->data1 < NUMINPUTS)
|
||||
{
|
||||
c = ev->data1;
|
||||
}
|
||||
#ifdef PARANOIA
|
||||
else
|
||||
{
|
||||
CONS_Debug(DBG_GAMELOGIC, "Bad downkey input %d\n", ev->data1);
|
||||
}
|
||||
#endif
|
||||
// If this key is already bound, don't bind it again.
|
||||
if (optionsmenu.bindinputs[i] == c)
|
||||
break;
|
||||
case ev_gamepad_axis:
|
||||
if (ev->data1 >= JOYAXES)
|
||||
{
|
||||
#ifdef PARANOIA
|
||||
CONS_Debug(DBG_GAMELOGIC, "Bad gamepad axis event %d\n", ev->data1);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
INT32 deadzone = deadzone = (JOYAXISRANGE * cv_deadzone[0].value) / FRACUNIT; // TODO how properly account for different deadzone cvars for different devices
|
||||
boolean responsivelr = ((ev->data2 != INT32_MAX) && (abs(ev->data2) >= deadzone));
|
||||
boolean responsiveud = ((ev->data3 != INT32_MAX) && (abs(ev->data3) >= deadzone));
|
||||
|
||||
i = ev->data1;
|
||||
|
||||
if (i >= JOYANALOGS)
|
||||
// Find the first available slot.
|
||||
if (!optionsmenu.bindinputs[i])
|
||||
{
|
||||
// The trigger axes are handled specially.
|
||||
i -= JOYANALOGS;
|
||||
|
||||
if (responsivelr)
|
||||
{
|
||||
c = KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2);
|
||||
}
|
||||
else if (responsiveud)
|
||||
{
|
||||
c = KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2) + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Actual analog sticks
|
||||
|
||||
// Only consider unambiguous assignment.
|
||||
if (responsivelr == responsiveud)
|
||||
return;
|
||||
|
||||
if (responsivelr)
|
||||
{
|
||||
if (ev->data2 < 0)
|
||||
{
|
||||
// Left
|
||||
c = KEY_AXIS1 + (i * 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Right
|
||||
c = KEY_AXIS1 + (i * 4) + 1;
|
||||
}
|
||||
}
|
||||
else //if (responsiveud)
|
||||
{
|
||||
if (ev->data3 < 0)
|
||||
{
|
||||
// Up
|
||||
c = KEY_AXIS1 + (i * 4) + 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Down
|
||||
c = KEY_AXIS1 + (i * 4) + 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
optionsmenu.bindinputs[i] = c;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (noinput)
|
||||
{
|
||||
{
|
||||
// You can hold a button before entering this
|
||||
// dialog, then buffer a keyup without pressing
|
||||
// anything else. If this happens, don't wipe the
|
||||
// binds, just ignore it.
|
||||
const UINT8 zero[sizeof optionsmenu.bindinputs] = {0};
|
||||
if (!memcmp(zero, optionsmenu.bindinputs, sizeof zero))
|
||||
return;
|
||||
}
|
||||
|
||||
// safety result
|
||||
if (!c)
|
||||
return;
|
||||
INT32 controln = currentMenu->menuitems[itemOn].mvar1;
|
||||
memcpy(&optionsmenu.tempcontrols[controln], optionsmenu.bindinputs, sizeof optionsmenu.bindinputs);
|
||||
optionsmenu.bindtimer = 0;
|
||||
|
||||
// Set menu delay regardless of what we're doing to avoid stupid stuff.
|
||||
M_SetMenuDelay(0);
|
||||
|
||||
// Reset this input so (keyboard keys at least) are not
|
||||
// buffered and caught by menucmd.
|
||||
DeviceGameKeyDownArray[c] = 0;
|
||||
|
||||
// Check if this particular key (c) is already bound in any slot.
|
||||
// If that's the case, simply do nothing.
|
||||
for (i = 0; i < MAXINPUTMAPPING; i++)
|
||||
{
|
||||
if (optionsmenu.tempcontrols[controln][i] == c)
|
||||
{
|
||||
optionsmenu.bindcontrol = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// With the way we do things, there cannot be instances of 'gaps' within the controls, so we don't need to pretend like we need to handle that.
|
||||
// Unless of course you tamper with the cfg file, but then it's *your* fault, not mine.
|
||||
|
||||
optionsmenu.tempcontrols[controln][where] = c;
|
||||
optionsmenu.bindcontrol = 0; // not binding anymore
|
||||
|
||||
// If possible, reapply the profile...
|
||||
// 19/05/22: Actually, no, don't do that, it just fucks everything up in too many cases.
|
||||
|
||||
/*
|
||||
if (gamestate == GS_MENU) // In menu? Apply this to P1, no questions asked.
|
||||
{
|
||||
// Apply the profile's properties to player 1 but keep the last profile cv to p1's ACTUAL profile to revert once we exit.
|
||||
UINT8 lastp = cv_lastprofile[0].value;
|
||||
PR_ApplyProfile(PR_GetProfileNum(optionsmenu.profile), 0);
|
||||
CV_StealthSetValue(&cv_lastprofile[0], lastp);
|
||||
}
|
||||
else // != GS_MENU
|
||||
{
|
||||
// ONLY apply the profile if it's in use by anything currently.
|
||||
UINT8 pnum = PR_GetProfileNum(optionsmenu.profile);
|
||||
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
||||
{
|
||||
if (cv_lastprofile[i].value == pnum)
|
||||
{
|
||||
PR_ApplyProfile(pnum, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
else
|
||||
optionsmenu.bindtimer = -1; // prevent skip countdown
|
||||
}
|
||||
#undef KEYHOLDFOR
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue