mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-12-04 15:12:58 +00:00
All backgrounds are now handled by a seperate routine.
This will allow more fine handling without a ton of special cases everywhere.
Creates an absolutely nasty diff because all menu_t have been updated in one blast 🥹
For the things that DO need to draw outside of GS_MENU (M_DrawOptionsCogs, the Wrongwarp), the behaviourflag MBF_DRAWBGWHILEPLAYING has also been added.
478 lines
14 KiB
C
478 lines
14 KiB
C
/// \file menus/options-profiles-edit-controls.c
|
|
/// \brief Profile Controls Editor
|
|
|
|
#include "../k_menu.h"
|
|
#include "../s_sound.h"
|
|
#include "../i_joy.h" // for joystick menu controls
|
|
|
|
menuitem_t OPTIONS_ProfileControls[] = {
|
|
|
|
{IT_HEADER, "MAIN CONTROLS", "That's the stuff on the controller!!",
|
|
NULL, {NULL}, 0, 0},
|
|
|
|
{IT_CONTROL, "A", "Accelerate / Confirm",
|
|
"PR_BTA", {.routine = M_ProfileSetControl}, gc_a, 0},
|
|
|
|
{IT_CONTROL, "B", "Look backwards / Back",
|
|
"PR_BTB", {.routine = M_ProfileSetControl}, gc_b, 0},
|
|
|
|
{IT_CONTROL, "C", "Spindash / Extra",
|
|
"PR_BTC", {.routine = M_ProfileSetControl}, gc_c, 0},
|
|
|
|
{IT_CONTROL, "X", "Brake / Back",
|
|
"PR_BTX", {.routine = M_ProfileSetControl}, gc_x, 0},
|
|
|
|
{IT_CONTROL, "Y", "Respawn",
|
|
"PR_BTY", {.routine = M_ProfileSetControl}, gc_y, 0},
|
|
|
|
{IT_CONTROL, "Z", "Multiplayer quick-chat / quick-vote",
|
|
"PR_BTZ", {.routine = M_ProfileSetControl}, gc_z, 0},
|
|
|
|
{IT_CONTROL, "L", "Use item",
|
|
"PR_BTL", {.routine = M_ProfileSetControl}, gc_l, 0},
|
|
|
|
{IT_CONTROL, "R", "Drift",
|
|
"PR_BTR", {.routine = M_ProfileSetControl}, gc_r, 0},
|
|
|
|
{IT_CONTROL, "Turn Left", "Turn left",
|
|
"PR_PADL", {.routine = M_ProfileSetControl}, gc_left, 0},
|
|
|
|
{IT_CONTROL, "Turn Right", "Turn right",
|
|
"PR_PADR", {.routine = M_ProfileSetControl}, gc_right, 0},
|
|
|
|
{IT_CONTROL, "Aim Forward", "Aim forwards",
|
|
"PR_PADU", {.routine = M_ProfileSetControl}, gc_up, 0},
|
|
|
|
{IT_CONTROL, "Aim Backwards", "Aim backwards",
|
|
"PR_PADD", {.routine = M_ProfileSetControl}, gc_down, 0},
|
|
|
|
{IT_CONTROL, "Start", "Open pause menu",
|
|
"PR_BTS", {.routine = M_ProfileSetControl}, gc_start, 0},
|
|
|
|
{IT_HEADER, "OPTIONAL CONTROLS", "Take a screenshot, chat...",
|
|
NULL, {NULL}, 0, 0},
|
|
|
|
{IT_CONTROL, "SCREENSHOT", "Take a high resolution screenshot.",
|
|
NULL, {.routine = M_ProfileSetControl}, gc_screenshot, 0},
|
|
|
|
{IT_CONTROL, "RECORD VIDEO", "Record a video with sound.",
|
|
NULL, {.routine = M_ProfileSetControl}, gc_startmovie, 0},
|
|
|
|
{IT_CONTROL, "RECORD LOSSLESS", "Record a pixel perfect GIF.",
|
|
NULL, {.routine = M_ProfileSetControl}, gc_startlossless, 0},
|
|
|
|
{IT_CONTROL, "OPEN CHAT", "Opens full keyboard chatting for online games.",
|
|
NULL, {.routine = M_ProfileSetControl}, gc_talk, 0},
|
|
|
|
{IT_CONTROL, "OPEN TEAM CHAT", "Opens team-only full chat for online games.",
|
|
NULL, {.routine = M_ProfileSetControl}, gc_teamtalk, 0},
|
|
|
|
{IT_CONTROL, "SHOW RANKINGS", "Display the current rankings mid-game.",
|
|
NULL, {.routine = M_ProfileSetControl}, gc_rankings, 0},
|
|
|
|
{IT_CONTROL, "OPEN CONSOLE", "Opens the developer options console.",
|
|
NULL, {.routine = M_ProfileSetControl}, gc_console, 0},
|
|
|
|
{IT_CONTROL, "LUA/A", "May be used by add-ons.",
|
|
NULL, {.routine = M_ProfileSetControl}, gc_luaa, 0},
|
|
|
|
{IT_CONTROL, "LUA/B", "May be used by add-ons.",
|
|
NULL, {.routine = M_ProfileSetControl}, gc_luab, 0},
|
|
|
|
{IT_CONTROL, "LUA/C", "May be used by add-ons.",
|
|
NULL, {.routine = M_ProfileSetControl}, gc_luac, 0},
|
|
|
|
{IT_HEADER, "TOGGLES", "For per-player commands",
|
|
NULL, {NULL}, 0, 0},
|
|
|
|
{IT_CONTROL | IT_CVAR, "RUMBLE", "For gamepad users - should your device rumble?",
|
|
NULL, {.cvar = &cv_dummyprofilerumble}, 0, 0},
|
|
|
|
{IT_CONTROL | IT_CVAR, "KICKSTART ACCEL", "Hold A to auto-accel. Tap it to cancel.",
|
|
NULL, {.cvar = &cv_dummyprofilekickstart}, 0, 0},
|
|
|
|
{IT_CONTROL | IT_CVAR, "AUTO ROULETTE", "Item roulette auto-stops on a random result.",
|
|
NULL, {.cvar = &cv_dummyprofileautoroulette}, 0, 0},
|
|
|
|
{IT_CONTROL | IT_CVAR, "LITE STEER", "Hold DOWN on d-pad/keyboard for shallow turns.",
|
|
NULL, {.cvar = &cv_dummyprofilelitesteer}, 0, 0},
|
|
|
|
{IT_HEADER, "EXTRA", "",
|
|
NULL, {NULL}, 0, 0},
|
|
|
|
{IT_STRING | IT_CALL, "TRY MAPPINGS", "Test your controls.",
|
|
NULL, {.routine = M_ProfileTryController}, 0, 0},
|
|
|
|
{IT_STRING | IT_CALL, "CONFIRM", "Go back to profile setup.",
|
|
NULL, {.routine = M_ProfileControlsConfirm}, 0, 0},
|
|
};
|
|
|
|
menu_t OPTIONS_ProfileControlsDef = {
|
|
sizeof (OPTIONS_ProfileControls) / sizeof (menuitem_t),
|
|
&OPTIONS_EditProfileDef,
|
|
0,
|
|
OPTIONS_ProfileControls,
|
|
32, 80,
|
|
SKINCOLOR_ULTRAMARINE, 0,
|
|
MBF_DRAWBGWHILEPLAYING,
|
|
"FILE",
|
|
3, 5,
|
|
M_DrawProfileControls,
|
|
M_DrawOptionsCogs,
|
|
M_HandleProfileControls,
|
|
NULL,
|
|
NULL,
|
|
M_ProfileControlsInputs,
|
|
};
|
|
|
|
// sets whatever device has had its key pressed to the active device.
|
|
// 20/05/22: Commented out for now but not deleted as it might still find some use in the future?
|
|
/*
|
|
static void SetDeviceOnPress(void)
|
|
{
|
|
UINT8 i;
|
|
|
|
for (i=0; i < MAXDEVICES; i++)
|
|
{
|
|
if (deviceResponding[i])
|
|
{
|
|
G_SetDeviceForPlayer(0, i); // Force-set this joystick as the current joystick we're using for P1 (which is the only one controlling menus)
|
|
CONS_Printf("SetDeviceOnPress: Device for %d set to %d\n", 0, i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
void M_HandleProfileControls(void)
|
|
{
|
|
UINT8 maxscroll = currentMenu->numitems - 5;
|
|
M_OptionsTick();
|
|
|
|
optionsmenu.contx += (optionsmenu.tcontx - optionsmenu.contx)/2;
|
|
optionsmenu.conty += (optionsmenu.tconty - optionsmenu.conty)/2;
|
|
|
|
if (abs(optionsmenu.contx - optionsmenu.tcontx) < 2 && abs(optionsmenu.conty - optionsmenu.tconty) < 2)
|
|
{
|
|
optionsmenu.contx = optionsmenu.tcontx;
|
|
optionsmenu.conty = optionsmenu.tconty; // Avoid awkward 1 px errors.
|
|
}
|
|
|
|
optionsmenu.controlscroll = itemOn - 3; // very barebones scrolling, but it works just fine for our purpose.
|
|
if (optionsmenu.controlscroll > maxscroll)
|
|
optionsmenu.controlscroll = maxscroll;
|
|
|
|
if (optionsmenu.controlscroll < 0)
|
|
optionsmenu.controlscroll = 0;
|
|
|
|
// bindings, cancel if timer is depleted.
|
|
if (optionsmenu.bindcontrol)
|
|
{
|
|
optionsmenu.bindtimer--;
|
|
if (!optionsmenu.bindtimer)
|
|
{
|
|
optionsmenu.bindcontrol = 0; // we've gone past the max, just stop.
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void M_ProfileTryController(INT32 choice)
|
|
{
|
|
(void)choice;
|
|
|
|
optionsmenu.trycontroller = TICRATE*5;
|
|
|
|
// Apply these controls right now on P1's end.
|
|
memcpy(&gamecontrol[0], optionsmenu.tempcontrols, sizeof(gamecontroldefault));
|
|
}
|
|
|
|
static void M_ProfileControlSaveResponse(INT32 choice)
|
|
{
|
|
if (choice == MA_YES)
|
|
{
|
|
SINT8 belongsto = PR_ProfileUsedBy(optionsmenu.profile);
|
|
// Save the profile
|
|
optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value;
|
|
optionsmenu.profile->autoroulette = cv_dummyprofileautoroulette.value;
|
|
optionsmenu.profile->litesteer = cv_dummyprofilelitesteer.value;
|
|
optionsmenu.profile->rumble = cv_dummyprofilerumble.value;
|
|
memcpy(&optionsmenu.profile->controls, optionsmenu.tempcontrols, sizeof(gamecontroldefault));
|
|
|
|
// If this profile is in-use by anyone, apply the changes immediately upon exiting.
|
|
// Don't apply the profile itself as that would lead to issues mid-game.
|
|
if (belongsto > -1 && belongsto < MAXSPLITSCREENPLAYERS)
|
|
{
|
|
memcpy(&gamecontrol[belongsto], optionsmenu.tempcontrols, sizeof(gamecontroldefault));
|
|
CV_SetValue(&cv_kickstartaccel[belongsto], cv_dummyprofilekickstart.value);
|
|
CV_SetValue(&cv_autoroulette[belongsto], cv_dummyprofileautoroulette.value);
|
|
CV_SetValue(&cv_litesteer[belongsto], cv_dummyprofilelitesteer.value);
|
|
CV_SetValue(&cv_rumble[belongsto], cv_dummyprofilerumble.value);
|
|
}
|
|
|
|
M_GoBack(0);
|
|
}
|
|
}
|
|
|
|
void M_ProfileControlsConfirm(INT32 choice)
|
|
{
|
|
(void)choice;
|
|
|
|
//M_StartMessage("Profiles", M_GetText("Exiting will save the control changes\nfor this Profile.\nIs this okay?\n"), &M_ProfileControlSaveResponse, MM_YESNO, NULL, NULL);
|
|
// TODO: Add a graphic for controls saving, instead of obnoxious prompt.
|
|
|
|
M_ProfileControlSaveResponse(MA_YES);
|
|
|
|
optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value; // Make sure to save kickstart accel.
|
|
optionsmenu.profile->autoroulette = cv_dummyprofileautoroulette.value; // We should really just rip this entire construct out at some point
|
|
optionsmenu.profile->rumble = cv_dummyprofilerumble.value; // And rumble too!
|
|
|
|
// Reapply player 1's real profile.
|
|
if (cv_currprofile.value > -1)
|
|
{
|
|
PR_ApplyProfile(cv_lastprofile[0].value, 0);
|
|
}
|
|
}
|
|
|
|
boolean M_ProfileControlsInputs(INT32 ch)
|
|
{
|
|
const UINT8 pid = 0;
|
|
(void)ch;
|
|
|
|
// By default, accept all inputs.
|
|
if (optionsmenu.trycontroller)
|
|
{
|
|
if (menucmd[pid].dpad_ud || menucmd[pid].dpad_lr || menucmd[pid].buttons)
|
|
{
|
|
optionsmenu.trycontroller = 5*TICRATE;
|
|
}
|
|
else
|
|
{
|
|
optionsmenu.trycontroller--;
|
|
}
|
|
|
|
if (optionsmenu.trycontroller == 0)
|
|
{
|
|
// Reset controls to that of the current profile.
|
|
profile_t *cpr = PR_GetProfile(cv_currprofile.value);
|
|
if (cpr == NULL)
|
|
cpr = PR_GetProfile(0); // Creating a profile at boot, revert to guest profile
|
|
memcpy(&gamecontrol[0], cpr->controls, sizeof(gamecontroldefault));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
if (optionsmenu.bindcontrol)
|
|
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...
|
|
|
|
if (M_MenuExtraPressed(pid))
|
|
{
|
|
// check if we're on a valid menu option...
|
|
if (currentMenu->menuitems[itemOn].mvar1)
|
|
{
|
|
// clear controls for that key
|
|
INT32 i;
|
|
|
|
for (i = 0; i < MAXINPUTMAPPING; i++)
|
|
optionsmenu.tempcontrols[currentMenu->menuitems[itemOn].mvar1][i] = KEY_NULL;
|
|
|
|
S_StartSound(NULL, sfx_s3k66);
|
|
}
|
|
M_SetMenuDelay(pid);
|
|
return true;
|
|
}
|
|
else if (M_MenuBackPressed(pid))
|
|
{
|
|
M_ProfileControlsConfirm(0);
|
|
M_SetMenuDelay(pid);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// Map the event to the profile.
|
|
|
|
#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;
|
|
INT32 *DeviceGameKeyDownArray = G_GetDeviceGameKeyDownArray(ev->device);
|
|
|
|
if (!DeviceGameKeyDownArray)
|
|
return;
|
|
|
|
//SetDeviceOnPress(); // Update player gamepad assignments
|
|
|
|
// Only consider keydown and joystick events to make sure we ignore ev_mouse and other events
|
|
// See also G_MapEventsToControls
|
|
switch (ev->type)
|
|
{
|
|
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
|
|
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)
|
|
{
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
// safety result
|
|
if (!c)
|
|
return;
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
#undef KEYHOLDFOR
|