diff --git a/src/Sourcefile b/src/Sourcefile index de90bb609..9de90eee4 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -51,6 +51,7 @@ p_spec.c p_telept.c p_tick.c p_user.c +p_haptic.c p_slopes.c tables.c r_bsp.c diff --git a/src/command.c b/src/command.c index e0b04a184..db5726186 100644 --- a/src/command.c +++ b/src/command.c @@ -78,7 +78,6 @@ CV_PossibleValue_t CV_Natural[] = {{1, "MIN"}, {999999999, "MAX"}, {0, NULL}}; // Filter consvars by EXECVERSION // First implementation is 26 (2.1.21), so earlier configs default at 25 (2.1.20) -// Also set CV_HIDEN during runtime, after config is loaded static boolean execversion_enabled = false; consvar_t cv_execversion = CVAR_INIT ("execversion","25",CV_CALL,CV_Unsigned, CV_EnforceExecVersion); @@ -2234,12 +2233,12 @@ static boolean CV_FilterJoyAxisVars(consvar_t *v, const char *valstr) // reset all axis settings to defaults if (joyaxis_count == 6) { - COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis.name, cv_turnaxis.defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis.name, cv_moveaxis.defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis.name, cv_sideaxis.defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis.name, cv_lookaxis.defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis.name, cv_fireaxis.defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis.name, cv_firenaxis.defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis[0].name, cv_turnaxis[0].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis[0].name, cv_moveaxis[0].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis[0].name, cv_sideaxis[0].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis[0].name, cv_lookaxis[0].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis[0].name, cv_fireaxis[0].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis[0].name, cv_firenaxis[0].defaultvalue)); joyaxis_count++; return false; } @@ -2293,12 +2292,12 @@ static boolean CV_FilterJoyAxisVars(consvar_t *v, const char *valstr) // reset all axis settings to defaults if (joyaxis2_count == 6) { - COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis2.name, cv_turnaxis2.defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis2.name, cv_moveaxis2.defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis2.name, cv_sideaxis2.defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis2.name, cv_lookaxis2.defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis2.name, cv_fireaxis2.defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis2.name, cv_firenaxis2.defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis[1].name, cv_turnaxis[1].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis[1].name, cv_moveaxis[1].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis[1].name, cv_sideaxis[1].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis[1].name, cv_lookaxis[1].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis[1].name, cv_fireaxis[1].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis[1].name, cv_firenaxis[1].defaultvalue)); joyaxis2_count++; return false; } @@ -2308,6 +2307,49 @@ static boolean CV_FilterJoyAxisVars(consvar_t *v, const char *valstr) return true; } +#ifndef OLD_GAMEPAD_AXES +static boolean CV_ConvertOldJoyAxisVars(consvar_t *v, const char *valstr) +{ + static struct { + const char *old; + const char *new; + } axis_names[] = { + {"X-Axis", "Left Stick X"}, + {"Y-Axis", "Left Stick Y"}, + {"X-Axis-", "Left Stick X-"}, + {"Y-Axis-", "Left Stick Y-"}, + {"X-Rudder", "Right Stick X"}, + {"Y-Rudder", "Right Stick Y"}, + {"X-Rudder-", "Right Stick X-"}, + {"Y-Rudder-", "Right Stick Y-"}, + {"Z-Axis", "Left Trigger"}, + {"Z-Rudder", "Right Trigger"}, + {"Z-Axis-", "Left Trigger"}, + {"Z-Rudder-", "Right Trigger"}, + {NULL, NULL} + }; + + if (v->PossibleValue != joyaxis_cons_t) + return true; + + for (unsigned i = 0;; i++) + { + if (axis_names[i].old == NULL) + { + CV_SetCVar(v, "None", false); + return false; + } + else if (!stricmp(valstr, axis_names[i].old)) + { + CV_SetCVar(v, axis_names[i].new, false); + return false; + } + } + + return true; +} +#endif + static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr) { // True means allow the CV change, False means block it @@ -2336,8 +2378,8 @@ static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr) && atoi(valstr) == 35) return false; - // JOYSTICK DEFAULTS - // use_joystick was changed from 0 to 1 to automatically use a joystick if available + // GAMEPAD DEFAULTS + // use_gamepad was changed from 0 to 1 to automatically use a gamepad if available #if defined(HAVE_SDL) || defined(_WINDOWS) if ((!stricmp(v->name, "use_joystick") || !stricmp(v->name, "use_joystick2")) @@ -2350,6 +2392,15 @@ static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr) if (!CV_FilterJoyAxisVars(v, valstr)) return false; } + +#ifndef OLD_GAMEPAD_AXES + if (GETMAJOREXECVERSION(cv_execversion.value) <= 51 && GETMINOREXECVERSION(cv_execversion.value) < 1) + { + if (!CV_ConvertOldJoyAxisVars(v, valstr)) + return false; + } +#endif + return true; } diff --git a/src/console.c b/src/console.c index 40fb43121..7cad4aee5 100644 --- a/src/console.c +++ b/src/console.c @@ -918,7 +918,8 @@ boolean CON_Responder(event_t *ev) static INT32 alias_skips; const char *cmd = NULL; - INT32 key; + INT32 key = ev->key; + boolean key_is_console = (key == gamecontrol[GC_CONSOLE][0] || key == gamecontrol[GC_CONSOLE][1]); if (chat_on) return false; @@ -926,20 +927,18 @@ boolean CON_Responder(event_t *ev) // let go keyup events, don't eat them if (ev->type != ev_keydown && ev->type != ev_console) { - if (ev->key == gamecontrol[GC_CONSOLE][0] || ev->key == gamecontrol[GC_CONSOLE][1]) + if (key_is_console) consdown = false; return false; } - key = ev->key; - // check for console toggle key if (ev->type != ev_console) { if (modeattacking || metalrecording || marathonmode) return false; - if (key == gamecontrol[GC_CONSOLE][0] || key == gamecontrol[GC_CONSOLE][1]) + if (key_is_console) { if (consdown) // ignore repeat return true; diff --git a/src/d_clisrv.c b/src/d_clisrv.c index ac486faea..57c3e5924 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -25,7 +25,8 @@ #include "st_stuff.h" #include "hu_stuff.h" #include "keys.h" -#include "g_input.h" // JOY1 +#include "g_input.h" +#include "i_gamepad.h" #include "m_menu.h" #include "console.h" #include "d_netfil.h" @@ -33,6 +34,7 @@ #include "p_saveg.h" #include "z_zone.h" #include "p_local.h" +#include "p_haptic.h" #include "m_misc.h" #include "am_map.h" #include "m_random.h" @@ -49,7 +51,7 @@ #include "m_perfstats.h" // aaaaaa -#include "i_joy.h" +#include "i_gamepad.h" #ifndef NONET // cl loading screen @@ -653,22 +655,6 @@ static UINT8 Snake_GetOppositeDir(UINT8 dir) return 12 + 5 - dir; } -event_t *snakejoyevents[MAXEVENTS]; -UINT16 joyeventcount = 0; - -// I'm screaming the hack is clean - ashi -static boolean Snake_Joy_Grabber(event_t *ev) -{ - if (ev->type == ev_joystick && ev->key == 0) - { - snakejoyevents[joyeventcount] = ev; - joyeventcount++; - return true; - } - else - return false; -} - static void Snake_FindFreeSlot(UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady) { UINT8 x, y; @@ -695,19 +681,17 @@ static void Snake_Handle(void) UINT8 x, y; UINT8 oldx, oldy; UINT16 i; - UINT16 j; UINT16 joystate = 0; - static INT32 pjoyx = 0, pjoyy = 0; // Handle retry - if (snake->gameover && (PLAYER1INPUTDOWN(GC_JUMP) || gamekeydown[KEY_ENTER])) + if (snake->gameover && (G_PlayerInputDown(0, GC_JUMP) || gamekeydown[KEY_ENTER])) { Snake_Initialise(); snake->pausepressed = true; // Avoid accidental pause on respawn } // Handle pause - if (PLAYER1INPUTDOWN(GC_PAUSE) || gamekeydown[KEY_ENTER]) + if (G_PlayerInputDown(0, GC_PAUSE) || gamekeydown[KEY_ENTER]) { if (!snake->pausepressed) snake->paused = !snake->paused; @@ -726,58 +710,23 @@ static void Snake_Handle(void) oldx = snake->snakex[1]; oldy = snake->snakey[1]; - // process the input events in here dear lord - for (j = 0; j < joyeventcount; j++) - { - event_t *ev = snakejoyevents[j]; - const INT32 jdeadzone = (JOYAXISRANGE * cv_digitaldeadzone.value) / FRACUNIT; - if (ev->y != INT32_MAX) - { - if (Joystick.bGamepadStyle || abs(ev->y) > jdeadzone) - { - if (ev->y < 0 && pjoyy >= 0) - joystate = 1; - else if (ev->y > 0 && pjoyy <= 0) - joystate = 2; - pjoyy = ev->y; - } - else - pjoyy = 0; - } - - if (ev->x != INT32_MAX) - { - if (Joystick.bGamepadStyle || abs(ev->x) > jdeadzone) - { - if (ev->x < 0 && pjoyx >= 0) - joystate = 3; - else if (ev->x > 0 && pjoyx <= 0) - joystate = 4; - pjoyx = ev->x; - } - else - pjoyx = 0; - } - } - joyeventcount = 0; - // Update direction - if (PLAYER1INPUTDOWN(GC_STRAFELEFT) || gamekeydown[KEY_LEFTARROW] || joystate == 3) + if (G_PlayerInputDown(0, GC_STRAFELEFT) || gamekeydown[KEY_LEFTARROW] || joystate == 3) { if (snake->snakelength < 2 || x <= oldx) snake->snakedir[0] = 1; } - else if (PLAYER1INPUTDOWN(GC_STRAFERIGHT) || gamekeydown[KEY_RIGHTARROW] || joystate == 4) + else if (G_PlayerInputDown(0, GC_STRAFERIGHT) || gamekeydown[KEY_RIGHTARROW] || joystate == 4) { if (snake->snakelength < 2 || x >= oldx) snake->snakedir[0] = 2; } - else if (PLAYER1INPUTDOWN(GC_FORWARD) || gamekeydown[KEY_UPARROW] || joystate == 1) + else if (G_PlayerInputDown(0, GC_FORWARD) || gamekeydown[KEY_UPARROW] || joystate == 1) { if (snake->snakelength < 2 || y <= oldy) snake->snakedir[0] = 3; } - else if (PLAYER1INPUTDOWN(GC_BACKWARD) || gamekeydown[KEY_DOWNARROW] || joystate == 2) + else if (G_PlayerInputDown(0, GC_BACKWARD) || gamekeydown[KEY_DOWNARROW] || joystate == 2) { if (snake->snakelength < 2 || y >= oldy) snake->snakedir[0] = 4; @@ -1703,6 +1652,8 @@ static void CL_LoadReceivedSavegame(boolean reloading) titledemo = false; automapactive = false; + P_StopRumble(NULL); + // load a base level if (P_LoadNetGame(reloading)) { @@ -1989,9 +1940,10 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) static void M_ConfirmConnect(event_t *ev) { #ifndef NONET - if (ev->type == ev_keydown) + + if (ev->type == ev_keydown || ev->type == ev_gamepad_down) { - if (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER || ev->key == KEY_JOY1) + if ((ev->type == ev_keydown && (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_A)) { if (totalfilesrequestednum > 0) { @@ -2006,7 +1958,7 @@ static void M_ConfirmConnect(event_t *ev) M_ClearMenus(true); } - else if (ev->key == 'n' || ev->key == KEY_ESCAPE || ev->key == KEY_JOY1 + 3) + else if ((ev->type == ev_keydown && (ev->key == 'n' || ev->key == KEY_ESCAPE)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_B)) { cl_mode = CL_ABORTED; M_ClearMenus(true); @@ -2440,14 +2392,11 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic // my hand has been forced and I am dearly sorry for this awful hack :vomit: for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) { -#ifndef NONET - if (!Snake_Joy_Grabber(&events[eventtail])) -#endif - G_MapEventsToControls(&events[eventtail]); + G_MapEventsToControls(&events[eventtail]); } } - if (gamekeydown[KEY_ESCAPE] || gamekeydown[KEY_JOY1+1] || cl_mode == CL_ABORTED) + if (gamekeydown[KEY_ESCAPE] || gamepads[0].buttons[GAMEPAD_BUTTON_B] || cl_mode == CL_ABORTED) { CONS_Printf(M_GetText("Network game synchronization aborted.\n")); M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING); @@ -5230,7 +5179,7 @@ static void Local_Maketic(INT32 realtics) // game responder calls HU_Responder, AM_Responder, // and G_MapEventsToControls if (!dedicated) rendergametic = gametic; - // translate inputs (keyboard/mouse/joystick) into game controls + // translate inputs (keyboard/mouse/gamepad) into game controls G_BuildTiccmd(&localcmds, realtics, 1); if (splitscreen || botingame) G_BuildTiccmd(&localcmds2, realtics, 2); diff --git a/src/d_event.h b/src/d_event.h index c0b9cef77..9448b9c5a 100644 --- a/src/d_event.h +++ b/src/d_event.h @@ -24,19 +24,21 @@ typedef enum ev_keyup, ev_console, ev_mouse, - ev_joystick, ev_mouse2, - ev_joystick2, + ev_gamepad_up, + ev_gamepad_down, + ev_gamepad_axis } evtype_t; // Event structure. typedef struct { evtype_t type; - INT32 key; // keys/mouse/joystick buttons - INT32 x; // mouse/joystick x move - INT32 y; // mouse/joystick y move - boolean repeated; // key repeat + INT32 key; // key, mouse button, or gamepad button/axis type + INT32 x; // mouse x move, or gamepad axis value + INT32 y; // mouse y move + UINT8 which; // which gamepad or mouse ID + boolean repeated; // is the event repeated? } event_t; // diff --git a/src/d_main.c b/src/d_main.c index 4fd19e5ac..35f5a69d0 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -43,6 +43,7 @@ #include "i_time.h" #include "i_threads.h" #include "i_video.h" +#include "i_gamepad.h" #include "m_argv.h" #include "m_menu.h" #include "m_misc.h" @@ -986,6 +987,7 @@ void D_StartTitle(void) G_SetGametype(GT_COOP); paused = false; advancedemo = false; + P_StopRumble(NULL); F_InitMenuPresValues(); F_StartTitleScreen(); @@ -1396,6 +1398,9 @@ void D_SRB2Main(void) CONS_Printf("I_InitializeTime()...\n"); I_InitializeTime(); + // Initializes the game logic side of gamepads + G_InitGamepads(); + // Make backups of some SOCcable tables. P_BackupTables(); @@ -1451,6 +1456,9 @@ void D_SRB2Main(void) D_RegisterServerCommands(); D_RegisterClientCommands(); // be sure that this is called before D_CheckNetGame + + I_InitGamepads(); + R_RegisterEngineStuff(); S_RegisterSoundStuff(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 4e90db0dc..5f02bc2de 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -21,6 +21,7 @@ #include "g_game.h" #include "hu_stuff.h" #include "g_input.h" +#include "i_gamepad.h" #include "m_menu.h" #include "r_local.h" #include "r_skins.h" @@ -181,14 +182,6 @@ static CV_PossibleValue_t mouse2port_cons_t[] = {{1, "COM1"}, {2, "COM2"}, {3, " {0, NULL}}; #endif -#ifdef LJOYSTICK -static CV_PossibleValue_t joyport_cons_t[] = {{1, "/dev/js0"}, {2, "/dev/js1"}, {3, "/dev/js2"}, - {4, "/dev/js3"}, {0, NULL}}; -#else -// accept whatever value - it is in fact the joystick device number -#define usejoystick_cons_t NULL -#endif - static CV_PossibleValue_t teamscramble_cons_t[] = {{0, "Off"}, {1, "Random"}, {2, "Points"}, {0, NULL}}; static CV_PossibleValue_t startingliveslimit_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, NULL}}; @@ -247,19 +240,61 @@ INT32 cv_debug; consvar_t cv_usemouse = CVAR_INIT ("use_mouse", "On", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse); consvar_t cv_usemouse2 = CVAR_INIT ("use_mouse2", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse2); -consvar_t cv_usejoystick = CVAR_INIT ("use_gamepad", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick); -consvar_t cv_usejoystick2 = CVAR_INIT ("use_gamepad2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick2); -#if (defined (LJOYSTICK) || defined (HAVE_SDL)) -#ifdef LJOYSTICK -consvar_t cv_joyport = CVAR_INIT ("padport", "/dev/js0", CV_SAVE, joyport_cons_t, NULL); -consvar_t cv_joyport2 = CVAR_INIT ("padport2", "/dev/js0", CV_SAVE, joyport_cons_t, NULL); //Alam: for later -#endif -consvar_t cv_joyscale = CVAR_INIT ("padscale", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale); -consvar_t cv_joyscale2 = CVAR_INIT ("padscale2", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale2); -#else -consvar_t cv_joyscale = CVAR_INIT ("padscale", "1", CV_SAVE|CV_HIDEN, NULL, NULL); //Alam: Dummy for save -consvar_t cv_joyscale2 = CVAR_INIT ("padscale2", "1", CV_SAVE|CV_HIDEN, NULL, NULL); //Alam: Dummy for save -#endif +// We use cv_usegamepad.string as the USER-SET var +// and cv_usegamepad.value as the INTERNAL var +// +// In practice, if cv_usegamepad.string == 0, this overrides +// cv_usegamepad.value and always disables + +static void UseGamepad_OnChange(void) +{ + I_ChangeGamepad(0); +} + +static void UseGamepad2_OnChange(void) +{ + I_ChangeGamepad(1); +} + +consvar_t cv_usegamepad[2] = { + CVAR_INIT ("use_gamepad", "1", CV_SAVE|CV_CALL, NULL, UseGamepad_OnChange), + CVAR_INIT ("use_gamepad2", "2", CV_SAVE|CV_CALL, NULL, UseGamepad2_OnChange) +}; + +static void PadScale_OnChange(void) +{ + I_SetGamepadDigital(0, cv_gamepad_scale[0].value == 0); +} + +static void PadScale2_OnChange(void) +{ + I_SetGamepadDigital(1, cv_gamepad_scale[1].value == 0); +} + +consvar_t cv_gamepad_scale[2] = { + CVAR_INIT ("padscale", "1", CV_SAVE|CV_CALL, NULL, PadScale_OnChange), + CVAR_INIT ("padscale2", "1", CV_SAVE|CV_CALL, NULL, PadScale2_OnChange) +}; + +static void PadRumble_OnChange(void) +{ + if (!cv_gamepad_rumble[0].value) + I_StopGamepadRumble(0); +} + +static void PadRumble2_OnChange(void) +{ + if (!cv_gamepad_rumble[1].value) + I_StopGamepadRumble(1); +} + +consvar_t cv_gamepad_rumble[2] = { + CVAR_INIT ("padrumble", "Off", CV_SAVE|CV_CALL, CV_OnOff, PadRumble_OnChange), + CVAR_INIT ("padrumble2", "Off", CV_SAVE|CV_CALL, CV_OnOff, PadRumble2_OnChange) +}; + +consvar_t cv_gamepad_autopause = CVAR_INIT ("pauseongamepaddisconnect", "On", CV_SAVE, CV_OnOff, NULL); + #if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON) consvar_t cv_mouse2port = CVAR_INIT ("mouse2port", "/dev/gpmdata", CV_SAVE, mouse2port_cons_t, NULL); consvar_t cv_mouse2opt = CVAR_INIT ("mouse2opt", "0", CV_SAVE, NULL, NULL); @@ -771,26 +806,26 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_pauseifunfocused); // g_input.c - CV_RegisterVar(&cv_sideaxis); - CV_RegisterVar(&cv_sideaxis2); - CV_RegisterVar(&cv_turnaxis); - CV_RegisterVar(&cv_turnaxis2); - CV_RegisterVar(&cv_moveaxis); - CV_RegisterVar(&cv_moveaxis2); - CV_RegisterVar(&cv_lookaxis); - CV_RegisterVar(&cv_lookaxis2); - CV_RegisterVar(&cv_jumpaxis); - CV_RegisterVar(&cv_jumpaxis2); - CV_RegisterVar(&cv_spinaxis); - CV_RegisterVar(&cv_spinaxis2); - CV_RegisterVar(&cv_fireaxis); - CV_RegisterVar(&cv_fireaxis2); - CV_RegisterVar(&cv_firenaxis); - CV_RegisterVar(&cv_firenaxis2); - CV_RegisterVar(&cv_deadzone); - CV_RegisterVar(&cv_deadzone2); - CV_RegisterVar(&cv_digitaldeadzone); - CV_RegisterVar(&cv_digitaldeadzone2); + CV_RegisterVar(&cv_sideaxis[0]); + CV_RegisterVar(&cv_sideaxis[1]); + CV_RegisterVar(&cv_turnaxis[0]); + CV_RegisterVar(&cv_turnaxis[1]); + CV_RegisterVar(&cv_moveaxis[0]); + CV_RegisterVar(&cv_moveaxis[1]); + CV_RegisterVar(&cv_lookaxis[0]); + CV_RegisterVar(&cv_lookaxis[1]); + CV_RegisterVar(&cv_jumpaxis[0]); + CV_RegisterVar(&cv_jumpaxis[1]); + CV_RegisterVar(&cv_spinaxis[0]); + CV_RegisterVar(&cv_spinaxis[1]); + CV_RegisterVar(&cv_fireaxis[0]); + CV_RegisterVar(&cv_fireaxis[1]); + CV_RegisterVar(&cv_firenaxis[0]); + CV_RegisterVar(&cv_firenaxis[1]); + CV_RegisterVar(&cv_deadzone[0]); + CV_RegisterVar(&cv_deadzone[1]); + CV_RegisterVar(&cv_digitaldeadzone[0]); + CV_RegisterVar(&cv_digitaldeadzone[1]); // filesrch.c CV_RegisterVar(&cv_addons_option); @@ -819,14 +854,14 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_mousemove); CV_RegisterVar(&cv_mousemove2); - CV_RegisterVar(&cv_usejoystick); - CV_RegisterVar(&cv_usejoystick2); -#ifdef LJOYSTICK - CV_RegisterVar(&cv_joyport); - CV_RegisterVar(&cv_joyport2); -#endif - CV_RegisterVar(&cv_joyscale); - CV_RegisterVar(&cv_joyscale2); + for (i = 0; i < 2; i++) + { + CV_RegisterVar(&cv_usegamepad[i]); + CV_RegisterVar(&cv_gamepad_scale[i]); + CV_RegisterVar(&cv_gamepad_rumble[i]); + } + + CV_RegisterVar(&cv_gamepad_autopause); // Analog Control CV_RegisterVar(&cv_analog[0]); @@ -2215,9 +2250,14 @@ static void Got_Pause(UINT8 **cp, INT32 playernum) { if (!menuactive || netgame) S_PauseAudio(); + + P_PauseRumble(NULL); } else + { S_ResumeAudio(); + P_UnpauseRumble(NULL); + } } I_UpdateMouseGrab(); @@ -4563,6 +4603,8 @@ void Command_ExitGame_f(void) emeralds = 0; memset(&luabanks, 0, sizeof(luabanks)); + P_StopRumble(NULL); + if (dirmenu) closefilemenu(true); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 0beeae154..47f68a17e 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -33,14 +33,10 @@ extern consvar_t cv_defaultskin2; extern consvar_t cv_seenames, cv_allowseenames; extern consvar_t cv_usemouse; -extern consvar_t cv_usejoystick; -extern consvar_t cv_usejoystick2; -#ifdef LJOYSTICK -extern consvar_t cv_joyport; -extern consvar_t cv_joyport2; -#endif -extern consvar_t cv_joyscale; -extern consvar_t cv_joyscale2; +extern consvar_t cv_usegamepad[2]; +extern consvar_t cv_gamepad_scale[2]; +extern consvar_t cv_gamepad_rumble[2]; +extern consvar_t cv_gamepad_autopause; // splitscreen with second mouse extern consvar_t cv_mouse2port; diff --git a/src/deh_tables.c b/src/deh_tables.c index 4a3467f78..a2cc9732d 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -22,9 +22,9 @@ #include "v_video.h" // video flags (for lua) #include "i_sound.h" // musictype_t (for lua) #include "g_state.h" // gamestate_t (for lua) -#include "g_game.h" // Joystick axes (for lua) -#include "i_joy.h" +#include "g_game.h" // Gamepad axes (for lua) #include "g_input.h" // Game controls (for lua) +#include "i_gamepad.h" #include "deh_tables.h" @@ -4841,7 +4841,7 @@ const char *const MENUTYPES_LIST[] = { "OP_CHANGECONTROLS", // OP_ChangeControlsDef shared with P2 "OP_P1MOUSE", "OP_P1JOYSTICK", - "OP_JOYSTICKSET", // OP_JoystickSetDef shared with P2 + "OP_JOYSTICKSET", // OP_GamepadSetDef shared with P2 "OP_P1CAMERA", "OP_P2CONTROLS", @@ -5642,7 +5642,7 @@ struct int_const_s const INT_CONST[] = { {"GS_DEDICATEDSERVER",GS_DEDICATEDSERVER}, {"GS_WAITINGPLAYERS",GS_WAITINGPLAYERS}, - // Joystick axes + // Gamepad axes {"JA_NONE",JA_NONE}, {"JA_TURN",JA_TURN}, {"JA_MOVE",JA_MOVE}, @@ -5653,7 +5653,7 @@ struct int_const_s const INT_CONST[] = { {"JA_SPIN",JA_SPIN}, {"JA_FIRE",JA_FIRE}, {"JA_FIRENORMAL",JA_FIRENORMAL}, - {"JOYAXISRANGE",JOYAXISRANGE}, + {"JOYAXISRANGE",OLDJOYAXISRANGE}, // Game controls {"GC_NULL",GC_NULL}, diff --git a/src/doomdef.h b/src/doomdef.h index 62afcc6c7..2840d7a4a 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -211,7 +211,7 @@ extern char logfilename[1024]; // to an increment in MODVERSION. This might never happen in practice. // If MODVERSION increases, set MINOREXECVERSION to 0. #define MAJOREXECVERSION MODVERSION -#define MINOREXECVERSION 0 +#define MINOREXECVERSION 1 // (It would have been nice to use VERSION and SUBVERSION but those are zero'd out for DEVELOP builds) // Macros @@ -558,9 +558,6 @@ UINT32 quickncasehash (const char *p, size_t n) #define max(x, y) (((x) > (y)) ? (x) : (y)) #endif -// Max gamepad/joysticks that can be detected/used. -#define MAX_JOYSTICKS 4 - #ifndef M_PIl #define M_PIl 3.1415926535897932384626433832795029L #endif diff --git a/src/f_finale.c b/src/f_finale.c index bca8e3ba6..307e00aaa 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -37,6 +37,7 @@ #include "m_cond.h" #include "p_local.h" #include "p_setup.h" +#include "p_haptic.h" #include "st_stuff.h" // hud hiding #include "fastcmp.h" #include "console.h" @@ -510,6 +511,7 @@ void F_StartIntro(void) gameaction = ga_nothing; paused = false; CON_ToggleOff(); + P_StopRumble(NULL); F_NewCutscene(introtext[0]); intro_scenenum = 0; @@ -991,9 +993,10 @@ void F_IntroTicker(void) // boolean F_IntroResponder(event_t *event) { - INT32 key = event->key; + INT32 type = event->type; + INT32 key = G_RemapGamepadEvent(event, &type); - // remap virtual keys (mouse & joystick buttons) + // remap virtual keys (mouse & gamepad buttons) switch (key) { case KEY_MOUSE1: @@ -1002,34 +1005,30 @@ boolean F_IntroResponder(event_t *event) case KEY_MOUSE1 + 1: key = KEY_BACKSPACE; break; - case KEY_JOY1: - case KEY_JOY1 + 2: + case GAMEPAD_KEY(START): + case GAMEPAD_KEY(A): + case GAMEPAD_KEY(X): + case GAMEPAD_KEY(B): key = KEY_ENTER; break; - case KEY_JOY1 + 3: - key = 'n'; - break; - case KEY_JOY1 + 1: - key = KEY_BACKSPACE; - break; - case KEY_HAT1: + case GAMEPAD_KEY(DPAD_UP): key = KEY_UPARROW; break; - case KEY_HAT1 + 1: + case GAMEPAD_KEY(DPAD_DOWN): key = KEY_DOWNARROW; break; - case KEY_HAT1 + 2: + case GAMEPAD_KEY(DPAD_LEFT): key = KEY_LEFTARROW; break; - case KEY_HAT1 + 3: + case GAMEPAD_KEY(DPAD_RIGHT): key = KEY_RIGHTARROW; break; } - if (event->type != ev_keydown && key != 301) + if (type != ev_keydown) return false; - if (key != 27 && key != KEY_ENTER && key != KEY_SPACE && key != KEY_BACKSPACE) + if (key != KEY_ESCAPE && key != KEY_ENTER && key != KEY_SPACE && key != KEY_BACKSPACE) return false; if (keypressed) @@ -1264,6 +1263,7 @@ void F_StartCredits(void) gameaction = ga_nothing; paused = false; CON_ToggleOff(); + P_StopRumble(NULL); S_StopMusic(); S_StopSounds(); @@ -1376,9 +1376,10 @@ void F_CreditTicker(void) boolean F_CreditResponder(event_t *event) { - INT32 key = event->key; + INT32 type = event->type; + INT32 key = G_RemapGamepadEvent(event, &type); - // remap virtual keys (mouse & joystick buttons) + // remap virtual keys (mouse & gamepad buttons) switch (key) { case KEY_MOUSE1: @@ -1387,26 +1388,22 @@ boolean F_CreditResponder(event_t *event) case KEY_MOUSE1 + 1: key = KEY_BACKSPACE; break; - case KEY_JOY1: - case KEY_JOY1 + 2: + case GAMEPAD_KEY(START): + case GAMEPAD_KEY(A): + case GAMEPAD_KEY(X): + case GAMEPAD_KEY(B): key = KEY_ENTER; break; - case KEY_JOY1 + 3: - key = 'n'; - break; - case KEY_JOY1 + 1: - key = KEY_BACKSPACE; - break; - case KEY_HAT1: + case GAMEPAD_KEY(DPAD_UP): key = KEY_UPARROW; break; - case KEY_HAT1 + 1: + case GAMEPAD_KEY(DPAD_DOWN): key = KEY_DOWNARROW; break; - case KEY_HAT1 + 2: + case GAMEPAD_KEY(DPAD_LEFT): key = KEY_LEFTARROW; break; - case KEY_HAT1 + 3: + case GAMEPAD_KEY(DPAD_RIGHT): key = KEY_RIGHTARROW; break; } @@ -1414,7 +1411,7 @@ boolean F_CreditResponder(event_t *event) if (!(timesBeaten) && !(netgame || multiplayer) && !cv_debug) return false; - if (event->type != ev_keydown) + if (type != ev_keydown) return false; if (key != KEY_ESCAPE && key != KEY_ENTER && key != KEY_SPACE && key != KEY_BACKSPACE) @@ -1455,6 +1452,7 @@ void F_StartGameEvaluation(void) gameaction = ga_nothing; paused = false; CON_ToggleOff(); + P_StopRumble(NULL); finalecount = -1; sparklloop = 0; @@ -1780,6 +1778,7 @@ void F_StartEnding(void) gameaction = ga_nothing; paused = false; CON_ToggleOff(); + P_StopRumble(NULL); S_StopMusic(); // todo: placeholder S_StopSounds(); @@ -2225,6 +2224,7 @@ void F_StartGameEnd(void) paused = false; CON_ToggleOff(); S_StopSounds(); + P_StopRumble(NULL); // In case menus are still up?!! M_ClearMenus(true); @@ -3567,6 +3567,7 @@ void F_StartContinue(void) keypressed = false; paused = false; CON_ToggleOff(); + P_StopRumble(NULL); // In case menus are still up?!! M_ClearMenus(true); @@ -3819,24 +3820,26 @@ void F_ContinueTicker(void) boolean F_ContinueResponder(event_t *event) { - INT32 key = event->key; - if (keypressed) return true; + INT32 type = event->type; + INT32 key = G_RemapGamepadEvent(event, &type); + if (timetonext >= 21*TICRATE/2) return false; if (event->type != ev_keydown) return false; - // remap virtual keys (mouse & joystick buttons) + // remap virtual keys (mouse & gamepad buttons) switch (key) { case KEY_ENTER: case KEY_SPACE: case KEY_MOUSE1: - case KEY_JOY1: - case KEY_JOY1 + 2: + case GAMEPAD_KEY(START): + case GAMEPAD_KEY(A): + case GAMEPAD_KEY(X): break; default: return false; @@ -3954,6 +3957,7 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset gameaction = ga_nothing; paused = false; CON_ToggleOff(); + P_StopRumble(NULL); F_NewCutscene(cutscenes[cutscenenum]->scene[0].text); diff --git a/src/g_demo.c b/src/g_demo.c index 2da5a76ab..9099adc71 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -32,7 +32,7 @@ #include "z_zone.h" #include "i_video.h" #include "byteptr.h" -#include "i_joy.h" +#include "i_gamepad.h" #include "r_local.h" #include "r_skins.h" #include "y_inter.h" @@ -1527,9 +1527,9 @@ void G_BeginRecording(void) buf |= 0x08; pflags |= PF_AUTOBRAKE; } - if (cv_usejoystick.value) + if (cv_usegamepad[0].value) buf |= 0x10; - CV_SetValue(&cv_showinputjoy, !!(cv_usejoystick.value)); + CV_SetValue(&cv_showinputjoy, !!(cv_usegamepad[0].value)); WRITEUINT8(demo_p,buf); player->pflags = pflags; diff --git a/src/g_game.c b/src/g_game.c index 74bc42711..b4a127a73 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -24,6 +24,7 @@ #include "am_map.h" #include "m_random.h" #include "p_local.h" +#include "p_haptic.h" #include "r_draw.h" #include "r_main.h" #include "s_sound.h" @@ -38,7 +39,7 @@ #include "z_zone.h" #include "i_video.h" #include "byteptr.h" -#include "i_joy.h" +#include "i_gamepad.h" #include "r_local.h" #include "r_skins.h" #include "y_inter.h" @@ -59,9 +60,6 @@ boolean botingame; UINT8 botskin; UINT16 botcolor; -JoyType_t Joystick; -JoyType_t Joystick2; - // 1024 bytes is plenty for a savegame #define SAVEGAMESIZE (1024) @@ -256,12 +254,6 @@ UINT32 timesBeaten; UINT32 timesBeatenWithEmeralds; UINT32 timesBeatenUltimate; -typedef struct joystickvector2_s -{ - INT32 xaxis; - INT32 yaxis; -} joystickvector2_t; - boolean precache = true; // if true, load all graphics at start INT16 prevmap, nextmap; @@ -280,21 +272,42 @@ static void AutoBrake2_OnChange(void); void SendWeaponPref(void); void SendWeaponPref2(void); -static CV_PossibleValue_t crosshair_cons_t[] = {{0, "Off"}, {1, "Cross"}, {2, "Angle"}, {3, "Point"}, {0, NULL}}; -static CV_PossibleValue_t joyaxis_cons_t[] = {{0, "None"}, -{1, "X-Axis"}, {2, "Y-Axis"}, {-1, "X-Axis-"}, {-2, "Y-Axis-"}, -#if JOYAXISSET > 1 -{3, "Z-Axis"}, {4, "X-Rudder"}, {-3, "Z-Axis-"}, {-4, "X-Rudder-"}, +CV_PossibleValue_t joyaxis_cons_t[] = {{0, "None"}, +#ifndef OLD_GAMEPAD_AXES + {1, "Left Stick X"}, {2, "Left Stick Y"}, + {3, "Right Stick X"},{4, "Right Stick Y"}, + {-1, "Left Stick X-"}, {-2, "Left Stick Y-"}, + {-3, "Right Stick X-"}, {-4, "Right Stick Y-"}, + {5, "Left Trigger"}, {6, "Right Trigger"}, +#else + {1, "X-Axis"}, {2, "Y-Axis"}, {-1, "X-Axis-"}, {-2, "Y-Axis-"}, + #if JOYAXISSET > 1 + {3, "Z-Axis"}, {4, "X-Rudder"}, {-3, "Z-Axis-"}, {-4, "X-Rudder-"}, + #endif + #if JOYAXISSET > 2 + {5, "Y-Rudder"}, {6, "Z-Rudder"}, {-5, "Y-Rudder-"}, {-6, "Z-Rudder-"}, + #endif + #if JOYAXISSET > 3 + {7, "U-Axis"}, {8, "V-Axis"}, {-7, "U-Axis-"}, {-8, "V-Axis-"}, + #endif #endif -#if JOYAXISSET > 2 -{5, "Y-Rudder"}, {6, "Z-Rudder"}, {-5, "Y-Rudder-"}, {-6, "Z-Rudder-"}, -#endif -#if JOYAXISSET > 3 -{7, "U-Axis"}, {8, "V-Axis"}, {-7, "U-Axis-"}, {-8, "V-Axis-"}, -#endif - {0, NULL}}; -#if JOYAXISSET > 4 -"More Axis Sets" + {0, NULL} +}; + +#ifndef OLD_GAMEPAD_AXES +#define MOVEAXIS_DEFAULT "Left Stick Y" +#define SIDEAXIS_DEFAULT "Left Stick X" +#define LOOKAXIS_DEFAULT "Right Stick Y-" +#define TURNAXIS_DEFAULT "Right Stick X" +#define FIREAXIS_DEFAULT "Right Trigger" +#define FIRENAXIS_DEFAULT "Left Trigger" +#else +#define MOVEAXIS_DEFAULT "Y-Axis" +#define SIDEAXIS_DEFAULT "X-Axis" +#define LOOKAXIS_DEFAULT "Y-Rudder-" +#define TURNAXIS_DEFAULT "X-Rudder" +#define FIREAXIS_DEFAULT "Z-Rudder" +#define FIRENAXIS_DEFAULT "Z-Axis" #endif // don't mind me putting these here, I was lazy to figure out where else I could put those without blowing up the compiler. @@ -330,6 +343,7 @@ consvar_t cv_consolechat = CVAR_INIT ("chatmode", "Window", CV_SAVE, consolechat // Pause game upon window losing focus consvar_t cv_pauseifunfocused = CVAR_INIT ("pauseifunfocused", "Yes", CV_SAVE, CV_YesNo, NULL); +static CV_PossibleValue_t crosshair_cons_t[] = {{0, "Off"}, {1, "Cross"}, {2, "Angle"}, {3, "Point"}, {0, NULL}}; consvar_t cv_crosshair = CVAR_INIT ("crosshair", "Cross", CV_SAVE, crosshair_cons_t, NULL); consvar_t cv_crosshair2 = CVAR_INIT ("crosshair2", "Cross", CV_SAVE, crosshair_cons_t, NULL); consvar_t cv_invertmouse = CVAR_INIT ("invertmouse", "Off", CV_SAVE, CV_OnOff, NULL); @@ -409,27 +423,46 @@ consvar_t cv_cam_lockonboss[2] = { CVAR_INIT ("cam2_lockaimassist", "Full", CV_SAVE, lockedassist_cons_t, NULL), }; -consvar_t cv_moveaxis = CVAR_INIT ("joyaxis_move", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_sideaxis = CVAR_INIT ("joyaxis_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_lookaxis = CVAR_INIT ("joyaxis_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_turnaxis = CVAR_INIT ("joyaxis_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_jumpaxis = CVAR_INIT ("joyaxis_jump", "None", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_spinaxis = CVAR_INIT ("joyaxis_spin", "None", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_fireaxis = CVAR_INIT ("joyaxis_fire", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_firenaxis = CVAR_INIT ("joyaxis_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_deadzone = CVAR_INIT ("joy_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL); -consvar_t cv_digitaldeadzone = CVAR_INIT ("joy_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL); - -consvar_t cv_moveaxis2 = CVAR_INIT ("joyaxis2_move", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_sideaxis2 = CVAR_INIT ("joyaxis2_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_lookaxis2 = CVAR_INIT ("joyaxis2_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_turnaxis2 = CVAR_INIT ("joyaxis2_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_jumpaxis2 = CVAR_INIT ("joyaxis2_jump", "None", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_spinaxis2 = CVAR_INIT ("joyaxis2_spin", "None", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_fireaxis2 = CVAR_INIT ("joyaxis2_fire", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_firenaxis2 = CVAR_INIT ("joyaxis2_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL); -consvar_t cv_deadzone2 = CVAR_INIT ("joy_deadzone2", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL); -consvar_t cv_digitaldeadzone2 = CVAR_INIT ("joy_digdeadzone2", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL); +consvar_t cv_moveaxis[2] = { + CVAR_INIT ("joyaxis_move", MOVEAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL), + CVAR_INIT ("joyaxis2_move", MOVEAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL) +}; +consvar_t cv_sideaxis[2] = { + CVAR_INIT ("joyaxis_side", SIDEAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL), + CVAR_INIT ("joyaxis2_side", SIDEAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL) +}; +consvar_t cv_lookaxis[2] = { + CVAR_INIT ("joyaxis_look", LOOKAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL), + CVAR_INIT ("joyaxis2_look", LOOKAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL) +}; +consvar_t cv_turnaxis[2] = { + CVAR_INIT ("joyaxis_turn", TURNAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL), + CVAR_INIT ("joyaxis2_turn", TURNAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL) +}; +consvar_t cv_jumpaxis[2] = { + CVAR_INIT ("joyaxis_jump", "None", CV_SAVE, joyaxis_cons_t, NULL), + CVAR_INIT ("joyaxis2_jump", "None", CV_SAVE, joyaxis_cons_t, NULL) +}; +consvar_t cv_spinaxis[2] = { + CVAR_INIT ("joyaxis_spin", "None", CV_SAVE, joyaxis_cons_t, NULL), + CVAR_INIT ("joyaxis2_spin", "None", CV_SAVE, joyaxis_cons_t, NULL) +}; +consvar_t cv_fireaxis[2] = { + CVAR_INIT ("joyaxis_fire", FIREAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL), + CVAR_INIT ("joyaxis2_fire", FIREAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL) +}; +consvar_t cv_firenaxis[2] = { + CVAR_INIT ("joyaxis_firenormal", FIRENAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL), + CVAR_INIT ("joyaxis2_firenormal", FIRENAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL) +}; +consvar_t cv_deadzone[2] = { + CVAR_INIT ("joy_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), + CVAR_INIT ("joy_deadzone2", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL) +}; +consvar_t cv_digitaldeadzone[2] = { + CVAR_INIT ("joy_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), + CVAR_INIT ("joy_digdeadzone2", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL) +}; player_t *seenplayer; // player we're aiming at right now @@ -828,194 +861,186 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming) return (INT16)((*aiming)>>16); } -INT32 JoyAxis(joyaxis_e axissel) +#ifdef OLD_GAMEPAD_AXES +static gamepad_axis_e ConvertXboxControllerAxes(int type) +{ + switch (type) + { + // Left stick + case 1: // X-Axis + return GAMEPAD_AXIS_LEFTX; + case 2: // Y-Axis + return GAMEPAD_AXIS_LEFTY; + + // Right stick + case 4: // X-Rudder + return GAMEPAD_AXIS_RIGHTX; + case 5: // Y-Rudder + return GAMEPAD_AXIS_RIGHTY; + + // Triggers + case 3: // Z-Axis + return GAMEPAD_AXIS_TRIGGERLEFT; + case 6: // Z-Rudder + return GAMEPAD_AXIS_TRIGGERRIGHT; + + default: // All the other ones + return NUM_GAMEPAD_AXES; + } +} +#endif + +static INT16 GetJoystickAxisValue(UINT8 which, joyaxis_e axissel, INT32 axisval) { - INT32 retaxis; - INT32 axisval; boolean flp = false; - //find what axis to get + if (axisval < 0) // odd -axes + { + axisval = -axisval; + flp = true; + } + else if (axisval == 0) + return 0; + + if (axisval > JOYAXISSET*2) + return 0; + + gamepad_axis_e gp_axis; + +#ifdef OLD_GAMEPAD_AXES + gp_axis = ConvertXboxControllerAxes(axisval); +#else + gp_axis = axisval - 1; +#endif + + if (gp_axis >= NUM_GAMEPAD_AXES) + return 0; + + if (axisval % 2) + axisval /= 2; + else + { + axisval--; + axisval /= 2; + } + + INT16 retaxis = G_GetGamepadAxisValue(0, gp_axis); + + if (gamepads[which].digital && axissel >= JA_DIGITAL) + { + const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(which) / 2; + if (-jdeadzone < retaxis && retaxis < jdeadzone) + return 0; + } + + // flip it around + if (flp) + { + if (retaxis == -32768) + retaxis = 32767; + else + retaxis = -retaxis; + } + + return retaxis; +} + +INT16 G_JoyAxis(UINT8 which, joyaxis_e axissel) +{ + INT32 axisval; + INT32 value; + + // find what axis to get switch (axissel) { case JA_TURN: - axisval = cv_turnaxis.value; + axisval = cv_turnaxis[which].value; break; case JA_MOVE: - axisval = cv_moveaxis.value; + axisval = cv_moveaxis[which].value; break; case JA_LOOK: - axisval = cv_lookaxis.value; + axisval = cv_lookaxis[which].value; break; case JA_STRAFE: - axisval = cv_sideaxis.value; + axisval = cv_sideaxis[which].value; break; case JA_JUMP: - axisval = cv_jumpaxis.value; + axisval = cv_jumpaxis[which].value; break; case JA_SPIN: - axisval = cv_spinaxis.value; + axisval = cv_spinaxis[which].value; break; case JA_FIRE: - axisval = cv_fireaxis.value; + axisval = cv_fireaxis[which].value; break; case JA_FIRENORMAL: - axisval = cv_firenaxis.value; + axisval = cv_firenaxis[which].value; break; default: return 0; } - if (axisval < 0) //odd -axises + value = GetJoystickAxisValue(which, axissel, axisval); + if (axissel == JA_LOOK) { - axisval = -axisval; - flp = true; + // Look is inverted because +Y goes _down_ in gamepads. + if (value == -32768) + value = 32767; + else + value = -value; } - if (axisval > JOYAXISSET*2 || axisval == 0) //not there in array or None - return 0; - - if (axisval%2) - { - axisval /= 2; - retaxis = joyxmove[axisval]; - } - else - { - axisval--; - axisval /= 2; - retaxis = joyymove[axisval]; - } - - if (retaxis < (-JOYAXISRANGE)) - retaxis = -JOYAXISRANGE; - if (retaxis > (+JOYAXISRANGE)) - retaxis = +JOYAXISRANGE; - - if (!Joystick.bGamepadStyle && axissel >= JA_DIGITAL) - { - const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_digitaldeadzone.value) >> FRACBITS; - if (-jdeadzone < retaxis && retaxis < jdeadzone) - return 0; - } - - if (flp) retaxis = -retaxis; //flip it around - return retaxis; + return value; } -INT32 Joy2Axis(joyaxis_e axissel) +static INT16 GetAnalogInput(UINT8 which, gamecontrols_e gc) { - INT32 retaxis; - INT32 axisval; - boolean flp = false; - - //find what axis to get - switch (axissel) + for (UINT8 i = 0; i < 2; i++) { - case JA_TURN: - axisval = cv_turnaxis2.value; - break; - case JA_MOVE: - axisval = cv_moveaxis2.value; - break; - case JA_LOOK: - axisval = cv_lookaxis2.value; - break; - case JA_STRAFE: - axisval = cv_sideaxis2.value; - break; - case JA_JUMP: - axisval = cv_jumpaxis2.value; - break; - case JA_SPIN: - axisval = cv_spinaxis2.value; - break; - case JA_FIRE: - axisval = cv_fireaxis2.value; - break; - case JA_FIRENORMAL: - axisval = cv_firenaxis2.value; - break; - default: - return 0; + SINT8 isAnalog = G_PlayerInputIsAnalog(which, gc, i); + if (!isAnalog) + continue; + + INT16 value = G_GetAnalogPlayerInput(which, gc, i); + if (value > 0 && isAnalog == 1) + return value; + else if (value < 0 && isAnalog == -1) + return max(min(-value, INT16_MAX), INT16_MIN); } - - if (axisval < 0) //odd -axises - { - axisval = -axisval; - flp = true; - } - - if (axisval > JOYAXISSET*2 || axisval == 0) //not there in array or None - return 0; - - if (axisval%2) - { - axisval /= 2; - retaxis = joy2xmove[axisval]; - } - else - { - axisval--; - axisval /= 2; - retaxis = joy2ymove[axisval]; - } - - if (retaxis < (-JOYAXISRANGE)) - retaxis = -JOYAXISRANGE; - if (retaxis > (+JOYAXISRANGE)) - retaxis = +JOYAXISRANGE; - - if (!Joystick2.bGamepadStyle && axissel >= JA_DIGITAL) - { - const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_digitaldeadzone2.value) >> FRACBITS; - if (-jdeadzone < retaxis && retaxis < jdeadzone) - return 0; - } - - if (flp) retaxis = -retaxis; //flip it around - return retaxis; + return 0; } - -#define PlayerJoyAxis(p, ax) ((p) == 1 ? JoyAxis(ax) : Joy2Axis(ax)) - -// Take a magnitude of two axes, and adjust it to take out the deadzone -// Will return a value between 0 and JOYAXISRANGE -static INT32 G_BasicDeadZoneCalculation(INT32 magnitude, fixed_t deadZone) +static boolean CheckAxesUsable(UINT8 which, gamecontrols_e gc1, gamecontrols_e gc2) { - const INT32 jdeadzone = (JOYAXISRANGE * deadZone) / FRACUNIT; - INT32 deadzoneAppliedValue = 0; - INT32 adjustedMagnitude = abs(magnitude); + INT32 (*controls)[2] = which == 0 ? gamecontrol : gamecontrolbis; - if (jdeadzone >= JOYAXISRANGE && adjustedMagnitude >= JOYAXISRANGE) // If the deadzone and magnitude are both 100%... - return JOYAXISRANGE; // ...return 100% input directly, to avoid dividing by 0 - else if (adjustedMagnitude > jdeadzone) // Otherwise, calculate how much the magnitude exceeds the deadzone - { - adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE); +#define CHECK_RANGE(x, y, z) \ + (controls[x][y] >= KEY_AXES && controls[x][y] < KEY_AXES + NUM_GAMEPAD_AXES \ + && controls[x][z] >= KEY_INV_AXES && controls[x][z] < KEY_INV_AXES + NUM_GAMEPAD_AXES) - adjustedMagnitude -= jdeadzone; + if (CHECK_RANGE(gc1, 0, 1) || CHECK_RANGE(gc2, 0, 1)) + return false; + if (CHECK_RANGE(gc1, 1, 0) || CHECK_RANGE(gc2, 1, 0)) + return false; - deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone); - } +#undef CHECK_RANGE - return deadzoneAppliedValue; + return true; } +typedef struct +{ + INT32 xaxis, yaxis; +} joystickvector2_t; + // Get the actual sensible radial value for a joystick axis when accounting for a deadzone -static void G_HandleAxisDeadZone(UINT8 splitnum, joystickvector2_t *joystickvector) +static void G_HandleAxisDeadZone(UINT8 playernum, joystickvector2_t *joystickvector) { - INT32 gamepadStyle = Joystick.bGamepadStyle; - fixed_t deadZone = cv_deadzone.value; - - if (splitnum == 1) + if (!gamepads[playernum].digital) { - gamepadStyle = Joystick2.bGamepadStyle; - deadZone = cv_deadzone2.value; - } + const UINT16 deadZone = G_GetGamepadDeadZone(playernum); - // When gamepadstyle is "true" the values are just -1, 0, or 1. This is done in the interface code. - if (!gamepadStyle) - { // Get the total magnitude of the 2 axes INT32 magnitude = (joystickvector->xaxis * joystickvector->xaxis) + (joystickvector->yaxis * joystickvector->yaxis); INT32 normalisedXAxis; @@ -1029,18 +1054,18 @@ static void G_HandleAxisDeadZone(UINT8 splitnum, joystickvector2_t *joystickvect normalisedYAxis = (joystickvector->yaxis * magnitude) / JOYAXISRANGE; // Apply the deadzone to the magnitude to give a correct value between 0 and JOYAXISRANGE - normalisedMagnitude = G_BasicDeadZoneCalculation(magnitude, deadZone); + normalisedMagnitude = G_BasicDeadZoneCalculation(abs(magnitude), deadZone); // Apply the deadzone to the xy axes joystickvector->xaxis = (normalisedXAxis * normalisedMagnitude) / JOYAXISRANGE; joystickvector->yaxis = (normalisedYAxis * normalisedMagnitude) / JOYAXISRANGE; - - // Cap the values so they don't go above the correct maximum - joystickvector->xaxis = min(joystickvector->xaxis, JOYAXISRANGE); - joystickvector->xaxis = max(joystickvector->xaxis, -JOYAXISRANGE); - joystickvector->yaxis = min(joystickvector->yaxis, JOYAXISRANGE); - joystickvector->yaxis = max(joystickvector->yaxis, -JOYAXISRANGE); } + + // Cap the values so they don't go above the correct maximum + joystickvector->xaxis = min(joystickvector->xaxis, JOYAXISRANGE); + joystickvector->xaxis = max(joystickvector->xaxis, -JOYAXISRANGE - 1); + joystickvector->yaxis = min(joystickvector->yaxis, JOYAXISRANGE); + joystickvector->yaxis = max(joystickvector->yaxis, -JOYAXISRANGE - 1); } // @@ -1063,6 +1088,8 @@ boolean ticcmd_centerviewdown[2]; // For simple controls, lock the camera behind mobj_t *ticcmd_ztargetfocus[2]; // Locking onto an object? void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { + UINT8 forplayer = ssplayer - 1; + boolean forcestrafe = false; boolean forcefullinput = false; INT32 tspeed, forward, side, axis, strafeaxis, moveaxis, turnaxis, lookaxis, i; @@ -1071,15 +1098,17 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) const INT32 speed = 1; // these ones used for multiple conditions - boolean turnleft, turnright, strafelkey, straferkey, movefkey, movebkey, mouseaiming, analogjoystickmove, gamepadjoystickmove, thisjoyaiming; + boolean turnleft, turnright, strafelkey, straferkey, movefkey, movebkey, mouseaiming; + boolean analogaxismove, digitalaxismove, thisjoyaiming; boolean strafeisturn; // Simple controls only player_t *player = &players[ssplayer == 2 ? secondarydisplayplayer : consoleplayer]; camera_t *thiscam = ((ssplayer == 1 || player->bot == BOT_2PHUMAN) ? &camera : &camera2); angle_t *myangle = (ssplayer == 1 ? &localangle : &localangle2); INT32 *myaiming = (ssplayer == 1 ? &localaiming : &localaiming2); + gamepad_t *gamepad = &gamepads[forplayer]; angle_t drawangleoffset = (player->powers[pw_carry] == CR_ROLLOUT) ? ANGLE_180 : 0; - INT32 chasecam, chasefreelook, alwaysfreelook, usejoystick, invertmouse, turnmultiplier, mousemove; + INT32 chasecam, chasefreelook, alwaysfreelook, usegamepad, invertmouse, turnmultiplier, mousemove; controlstyle_e controlstyle = G_ControlStyle(ssplayer); INT32 mdx, mdy, mldy; @@ -1093,14 +1122,11 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) static fixed_t tta_factor[2] = {FRACUNIT, FRACUNIT}; // disables turn-to-angle when manually turning camera until movement happens boolean centerviewdown = false; - UINT8 forplayer = ssplayer-1; - if (ssplayer == 1) { chasecam = cv_chasecam.value; chasefreelook = cv_chasefreelook.value; alwaysfreelook = cv_alwaysfreelook.value; - usejoystick = cv_usejoystick.value; invertmouse = cv_invertmouse.value; turnmultiplier = cv_cam_turnmultiplier.value; mousemove = cv_mousemove.value; @@ -1114,7 +1140,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) chasecam = cv_chasecam2.value; chasefreelook = cv_chasefreelook2.value; alwaysfreelook = cv_alwaysfreelook2.value; - usejoystick = cv_usejoystick2.value; invertmouse = cv_invertmouse2.value; turnmultiplier = cv_cam2_turnmultiplier.value; mousemove = cv_mousemove2.value; @@ -1124,6 +1149,8 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); // empty, or external driver } + usegamepad = cv_usegamepad[forplayer].value; + if (menuactive || CON_Ready() || chat_on) mdx = mdy = mldy = 0; @@ -1142,13 +1169,14 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) return; } - turnright = PLAYERINPUTDOWN(ssplayer, GC_TURNRIGHT); - turnleft = PLAYERINPUTDOWN(ssplayer, GC_TURNLEFT); + // Axes for turning or strafing are ignored here + turnright = G_CheckDigitalPlayerInput(forplayer, GC_TURNRIGHT); + turnleft = G_CheckDigitalPlayerInput(forplayer, GC_TURNLEFT); - straferkey = PLAYERINPUTDOWN(ssplayer, GC_STRAFERIGHT); - strafelkey = PLAYERINPUTDOWN(ssplayer, GC_STRAFELEFT); - movefkey = PLAYERINPUTDOWN(ssplayer, GC_FORWARD); - movebkey = PLAYERINPUTDOWN(ssplayer, GC_BACKWARD); + straferkey = G_CheckDigitalPlayerInput(forplayer, GC_STRAFERIGHT); + strafelkey = G_CheckDigitalPlayerInput(forplayer, GC_STRAFELEFT); + movefkey = G_CheckDigitalPlayerInput(forplayer, GC_FORWARD); + movebkey = G_CheckDigitalPlayerInput(forplayer, GC_BACKWARD); if (strafeisturn) { @@ -1157,10 +1185,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) straferkey = strafelkey = false; } - mouseaiming = (PLAYERINPUTDOWN(ssplayer, GC_MOUSEAIMING)) ^ + mouseaiming = (G_PlayerInputDown(forplayer, GC_MOUSEAIMING)) ^ ((chasecam && !player->spectator) ? chasefreelook : alwaysfreelook); - analogjoystickmove = usejoystick && !Joystick.bGamepadStyle; - gamepadjoystickmove = usejoystick && Joystick.bGamepadStyle; + analogaxismove = usegamepad && !gamepad->digital; + digitalaxismove = usegamepad && gamepad->digital; thisjoyaiming = (chasecam && !player->spectator) ? chasefreelook : alwaysfreelook; @@ -1169,19 +1197,38 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) *myaiming = 0; joyaiming[forplayer] = thisjoyaiming; - turnaxis = PlayerJoyAxis(ssplayer, JA_TURN); + turnaxis = G_JoyAxis(forplayer, JA_TURN); if (strafeisturn) - turnaxis += PlayerJoyAxis(ssplayer, JA_STRAFE); - lookaxis = PlayerJoyAxis(ssplayer, JA_LOOK); + turnaxis += G_JoyAxis(forplayer, JA_STRAFE); + lookaxis = G_JoyAxis(forplayer, JA_LOOK); + + if (usegamepad) + { + turnaxis -= GetAnalogInput(forplayer, GC_TURNLEFT); + turnaxis += GetAnalogInput(forplayer, GC_TURNRIGHT); + + if (strafeisturn) + { + turnaxis -= GetAnalogInput(forplayer, GC_STRAFELEFT); + turnaxis += GetAnalogInput(forplayer, GC_STRAFERIGHT); + } + + lookaxis += GetAnalogInput(forplayer, GC_LOOKUP); + lookaxis -= GetAnalogInput(forplayer, GC_LOOKDOWN); + } + + // Handle deadzones lookjoystickvector.xaxis = turnaxis; lookjoystickvector.yaxis = lookaxis; G_HandleAxisDeadZone(forplayer, &lookjoystickvector); - if (gamepadjoystickmove && lookjoystickvector.xaxis != 0) + // Do digital axis turning + if (digitalaxismove && lookjoystickvector.xaxis != 0) { turnright = turnright || (lookjoystickvector.xaxis > 0); turnleft = turnleft || (lookjoystickvector.xaxis < 0); } + forward = side = 0; // use two stage accelerative turning @@ -1220,10 +1267,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (turnleft) side -= sidemove[speed]; - if (analogjoystickmove && lookjoystickvector.xaxis != 0) + if (analogaxismove && lookjoystickvector.xaxis != 0) { - // JOYAXISRANGE is supposed to be 1023 (divide by 1024) - side += ((lookjoystickvector.xaxis * sidemove[1]) >> 10); + // JOYAXISRANGE is supposed to be 32767 (divide by 32768) + side += ((lookjoystickvector.xaxis * sidemove[1]) >> 15); } } else if (controlstyle == CS_LMAOGALOG) // Analog @@ -1241,47 +1288,69 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) else if (turnleft) cmd->angleturn = (INT16)(cmd->angleturn + ((angleturn[tspeed] * turnmultiplier)>>FRACBITS)); - if (analogjoystickmove && lookjoystickvector.xaxis != 0) + if (analogaxismove && lookjoystickvector.xaxis != 0) { - // JOYAXISRANGE should be 1023 (divide by 1024) - cmd->angleturn = (INT16)(cmd->angleturn - ((((lookjoystickvector.xaxis * angleturn[1]) >> 10) * turnmultiplier)>>FRACBITS)); // ANALOG! + // JOYAXISRANGE should be 32767 (divide by 32768) + cmd->angleturn = (INT16)(cmd->angleturn - ((((lookjoystickvector.xaxis * angleturn[1]) >> 15) * turnmultiplier)>>FRACBITS)); // ANALOG! } if (turnright || turnleft || abs(cmd->angleturn) > angleturn[2]) tta_factor[forplayer] = 0; // suspend turn to angle } - strafeaxis = strafeisturn ? 0 : PlayerJoyAxis(ssplayer, JA_STRAFE); - moveaxis = PlayerJoyAxis(ssplayer, JA_MOVE); + // Strafing axes (moving left and right) + if (strafeisturn) + strafeaxis = 0; + else + { + strafeaxis = G_JoyAxis(forplayer, JA_STRAFE); + + if (usegamepad && CheckAxesUsable(forplayer, GC_STRAFELEFT, GC_STRAFERIGHT)) + { + strafeaxis -= GetAnalogInput(forplayer, GC_STRAFELEFT); + strafeaxis += GetAnalogInput(forplayer, GC_STRAFERIGHT); + } + } + + // Moving axes (moving forwards and backwards) + moveaxis = G_JoyAxis(forplayer, JA_MOVE); + if (usegamepad && CheckAxesUsable(forplayer, GC_FORWARD, GC_BACKWARD)) + { + moveaxis -= GetAnalogInput(forplayer, GC_FORWARD); + moveaxis += GetAnalogInput(forplayer, GC_BACKWARD); + } + movejoystickvector.xaxis = strafeaxis; movejoystickvector.yaxis = moveaxis; G_HandleAxisDeadZone(forplayer, &movejoystickvector); - if (gamepadjoystickmove && movejoystickvector.xaxis != 0) + if (digitalaxismove && movejoystickvector.xaxis != 0) { + // Do digital axis movement if (movejoystickvector.xaxis > 0) side += sidemove[speed]; else if (movejoystickvector.xaxis < 0) side -= sidemove[speed]; } - else if (analogjoystickmove && movejoystickvector.xaxis != 0) + else if (analogaxismove && movejoystickvector.xaxis != 0) { - // JOYAXISRANGE is supposed to be 1023 (divide by 1024) - side += ((movejoystickvector.xaxis * sidemove[1]) >> 10); + // JOYAXISRANGE is supposed to be 32767 (divide by 32768) + side += ((movejoystickvector.xaxis * sidemove[1]) >> 15); } // forward with key or button - if (movefkey || (gamepadjoystickmove && movejoystickvector.yaxis < 0) + // also handles digital axis movement + if (movefkey || (digitalaxismove && movejoystickvector.yaxis < 0) || ((player->powers[pw_carry] == CR_NIGHTSMODE) - && (PLAYERINPUTDOWN(ssplayer, GC_LOOKUP) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0)))) + && (G_CheckDigitalPlayerInput(forplayer, GC_LOOKUP) || (digitalaxismove && lookjoystickvector.yaxis > 0)))) forward = forwardmove[speed]; - if (movebkey || (gamepadjoystickmove && movejoystickvector.yaxis > 0) + if (movebkey || (digitalaxismove && movejoystickvector.yaxis > 0) || ((player->powers[pw_carry] == CR_NIGHTSMODE) - && (PLAYERINPUTDOWN(ssplayer, GC_LOOKDOWN) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0)))) + && (G_CheckDigitalPlayerInput(forplayer, GC_LOOKDOWN) || (digitalaxismove && lookjoystickvector.yaxis < 0)))) forward -= forwardmove[speed]; - if (analogjoystickmove && movejoystickvector.yaxis != 0) - forward -= ((movejoystickvector.yaxis * forwardmove[1]) >> 10); // ANALOG! + if (analogaxismove && movejoystickvector.yaxis != 0) + forward -= ((movejoystickvector.yaxis * forwardmove[1]) >> 15); // ANALOG! // some people strafe left & right with mouse buttons // those people are weird @@ -1290,53 +1359,54 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (strafelkey) side -= sidemove[speed]; - if (PLAYERINPUTDOWN(ssplayer, GC_WEAPONNEXT)) + if (G_PlayerInputDown(forplayer, GC_WEAPONNEXT)) cmd->buttons |= BT_WEAPONNEXT; // Next Weapon - if (PLAYERINPUTDOWN(ssplayer, GC_WEAPONPREV)) + if (G_PlayerInputDown(forplayer, GC_WEAPONPREV)) cmd->buttons |= BT_WEAPONPREV; // Previous Weapon #if NUM_WEAPONS > 10 -"Add extra inputs to g_input.h/gamecontrols_e" +#error "Add extra inputs to g_input.h/gamecontrols_e" #endif + //use the four avaliable bits to determine the weapon. cmd->buttons &= ~BT_WEAPONMASK; for (i = 0; i < NUM_WEAPONS; ++i) - if (PLAYERINPUTDOWN(ssplayer, GC_WEPSLOT1 + i)) + if (G_PlayerInputDown(forplayer, GC_WEPSLOT1 + i)) { cmd->buttons |= (UINT16)(i + 1); break; } // fire with any button/key - axis = PlayerJoyAxis(ssplayer, JA_FIRE); - if (PLAYERINPUTDOWN(ssplayer, GC_FIRE) || (usejoystick && axis > 0)) + axis = G_JoyAxis(forplayer, JA_FIRE); + if (G_PlayerInputDown(forplayer, GC_FIRE) || (usegamepad && axis > 0)) cmd->buttons |= BT_ATTACK; // fire normal with any button/key - axis = PlayerJoyAxis(ssplayer, JA_FIRENORMAL); - if (PLAYERINPUTDOWN(ssplayer, GC_FIRENORMAL) || (usejoystick && axis > 0)) + axis = G_JoyAxis(forplayer, JA_FIRENORMAL); + if (G_PlayerInputDown(forplayer, GC_FIRENORMAL) || (usegamepad && axis > 0)) cmd->buttons |= BT_FIRENORMAL; - if (PLAYERINPUTDOWN(ssplayer, GC_TOSSFLAG)) + if (G_PlayerInputDown(forplayer, GC_TOSSFLAG)) cmd->buttons |= BT_TOSSFLAG; // Lua scriptable buttons - if (PLAYERINPUTDOWN(ssplayer, GC_CUSTOM1)) + if (G_PlayerInputDown(forplayer, GC_CUSTOM1)) cmd->buttons |= BT_CUSTOM1; - if (PLAYERINPUTDOWN(ssplayer, GC_CUSTOM2)) + if (G_PlayerInputDown(forplayer, GC_CUSTOM2)) cmd->buttons |= BT_CUSTOM2; - if (PLAYERINPUTDOWN(ssplayer, GC_CUSTOM3)) + if (G_PlayerInputDown(forplayer, GC_CUSTOM3)) cmd->buttons |= BT_CUSTOM3; - // use with any button/key - axis = PlayerJoyAxis(ssplayer, JA_SPIN); - if (PLAYERINPUTDOWN(ssplayer, GC_SPIN) || (usejoystick && axis > 0)) + // spin with any button/key + axis = G_JoyAxis(forplayer, JA_SPIN); + if (G_PlayerInputDown(forplayer, GC_SPIN) || (usegamepad && axis > 0)) cmd->buttons |= BT_SPIN; // Centerview can be a toggle in simple mode! { static boolean last_centerviewdown[2], centerviewhold[2]; // detect taps for toggle behavior - boolean down = PLAYERINPUTDOWN(ssplayer, GC_CENTERVIEW); + boolean down = G_PlayerInputDown(forplayer, GC_CENTERVIEW); if (!(controlstyle == CS_SIMPLE && cv_cam_centertoggle[forplayer].value)) centerviewdown = down; @@ -1435,7 +1505,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (ticcmd_centerviewdown[forplayer] && controlstyle == CS_SIMPLE) controlstyle = CS_LEGACY; - if (PLAYERINPUTDOWN(ssplayer, GC_CAMRESET)) + if (G_PlayerInputDown(forplayer, GC_CAMRESET)) { if (thiscam->chase && !resetdown[forplayer]) P_ResetCamera(&players[ssplayer == 1 ? displayplayer : secondarydisplayplayer], thiscam); @@ -1445,10 +1515,9 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) else resetdown[forplayer] = false; - // jump button - axis = PlayerJoyAxis(ssplayer, JA_JUMP); - if (PLAYERINPUTDOWN(ssplayer, GC_JUMP) || (usejoystick && axis > 0)) + axis = G_JoyAxis(forplayer, JA_JUMP); + if (G_PlayerInputDown(forplayer, GC_JUMP) || (usegamepad && axis > 0)) cmd->buttons |= BT_JUMP; // player aiming shit, ahhhh... @@ -1458,7 +1527,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) (player->mo && (player->mo->eflags & MFE_VERTICALFLIP) && (!thiscam->chase || player->pflags & PF_FLIPCAM)) //because chasecam's not inverted ? -1 : 1; // set to -1 or 1 to multiply - INT32 configlookaxis = ssplayer == 1 ? cv_lookaxis.value : cv_lookaxis2.value; // mouse look stuff (mouse look is not the same as mouse aim) if (mouseaiming) @@ -1469,21 +1537,21 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) *myaiming += (mldy<<19)*player_invert*screen_invert; } - if (analogjoystickmove && joyaiming[forplayer] && lookjoystickvector.yaxis != 0 && configlookaxis != 0) - *myaiming += (lookjoystickvector.yaxis<<16) * screen_invert; + if (joyaiming[forplayer] && lookjoystickvector.yaxis != 0) + *myaiming += (lookjoystickvector.yaxis<<11) * screen_invert; // spring back if not using keyboard neither mouselookin' - if (!keyboard_look[forplayer] && configlookaxis == 0 && !joyaiming[forplayer] && !mouseaiming) + if (!keyboard_look[forplayer] && !joyaiming[forplayer] && !mouseaiming) *myaiming = 0; if (!(player->powers[pw_carry] == CR_NIGHTSMODE)) { - if (PLAYERINPUTDOWN(ssplayer, GC_LOOKUP) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0)) + if (G_CheckDigitalPlayerInput(forplayer, GC_LOOKUP) || (digitalaxismove && lookjoystickvector.yaxis < 0)) { *myaiming += KB_LOOKSPEED * screen_invert; keyboard_look[forplayer] = true; } - else if (PLAYERINPUTDOWN(ssplayer, GC_LOOKDOWN) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0)) + else if (G_CheckDigitalPlayerInput(forplayer, GC_LOOKDOWN) || (digitalaxismove && lookjoystickvector.yaxis > 0)) { *myaiming -= KB_LOOKSPEED * screen_invert; keyboard_look[forplayer] = true; @@ -1716,6 +1784,64 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) return dest; } +static player_t *G_GetInputPlayer(UINT8 which) +{ + if (which == 0) + return &players[displayplayer]; + else if (which == 1) + { + if (splitscreen) + return &players[secondarydisplayplayer]; + else if (playeringame[1] && players[1].bot == BOT_2PHUMAN) + return &players[1]; + } + + return NULL; +} + +// Returns a player's gamepad index, even if it's disabled +// Gamepad indexes correspond to the local player index. +INT16 G_GetGamepadForPlayer(player_t *player) +{ + for (UINT8 i = 0; i < 2; i++) + { + if (player == G_GetInputPlayer(i)) + return i; + } + + return -1; +} + +// Gets the user-set gamepad device for a specific player +INT32 G_GetGamepadDeviceIndex(INT32 player) +{ +#ifdef GAMEPAD_HOTPLUG + if (atoi(cv_usegamepad[player].string) > I_NumGamepads()) + return atoi(cv_usegamepad[player].string); + else +#endif + return cv_usegamepad[player].value; +} + +void G_OnGamepadConnect(UINT8 which) +{ + LUA_HookGamepadEvent(which, HOOK(GamepadAdded)); +} + +void G_OnGamepadDisconnect(UINT8 which) +{ + LUA_HookGamepadEvent(which, HOOK(GamepadRemoved)); + + if (!cv_gamepad_autopause.value) + return; + + if (gamestate != GS_LEVEL || paused || netgame || splitscreen) + return; + + if (which == 0 || (which == 1 && playeringame[1] && players[1].bot == BOT_2PHUMAN)) + COM_ImmedExecute("pause"); +} + // User has designated that they want // analog ON, so tell the game to stop // fudging with it. @@ -1787,6 +1913,25 @@ static void AutoBrake2_OnChange(void) SendWeaponPref2(); } +static void G_ResetInputs(void) +{ + memset(gamekeydown, 0, sizeof (gamekeydown)); + + for (UINT8 i = 0; i < NUM_GAMEPADS; i++) + { + for (UINT8 j = 0; j < NUM_GAMEPAD_BUTTONS; j++) + gamepads[i].buttons[j] = 0; + + for (UINT8 j = 0; j < NUM_GAMEPAD_AXES; j++) + gamepads[i].axes[j] = 0; + } + + G_SetMouseDeltas(0, 0, 1); + G_SetMouseDeltas(0, 0, 2); + + P_StopRumble(NULL); +} + // // G_DoLoadLevel // @@ -1855,14 +2000,7 @@ void G_DoLoadLevel(boolean resetplayer) P_ResetCamera(&players[secondarydisplayplayer], &camera2); // clear cmd building stuff - memset(gamekeydown, 0, sizeof (gamekeydown)); - for (i = 0;i < JOYAXISSET; i++) - { - joyxmove[i] = joyymove[i] = 0; - joy2xmove[i] = joy2ymove[i] = 0; - } - G_SetMouseDeltas(0, 0, 1); - G_SetMouseDeltas(0, 0, 2); + G_ResetInputs(); // clear hud messages remains (usually from game startup) CON_ClearHUD(); @@ -2044,11 +2182,14 @@ static boolean ViewpointSwitchResponder(event_t *ev) // boolean G_Responder(event_t *ev) { + INT32 evtype = ev->type; + INT32 key = G_RemapGamepadEvent(ev, &evtype); + // any other key pops up menu if in demos if (gameaction == ga_nothing && !singledemo && ((demoplayback && !modeattacking && !titledemo) || gamestate == GS_TITLESCREEN)) { - if (ev->type == ev_keydown && ev->key != 301 && !(gamestate == GS_TITLESCREEN && finalecount < TICRATE)) + if (evtype == ev_keydown && !(gamestate == GS_TITLESCREEN && finalecount < TICRATE)) { M_StartControlPanel(); return true; @@ -2073,7 +2214,7 @@ boolean G_Responder(event_t *ev) return true; // chat ate the event if (AM_Responder(ev)) return true; // automap ate it - // map the event (key/mouse/joy) to a gamecontrol + // map the event (key/mouse/gamepad) to a gamecontrol } // Intro else if (gamestate == GS_INTRO) @@ -2128,16 +2269,16 @@ boolean G_Responder(event_t *ev) // update keys current state G_MapEventsToControls(ev); - switch (ev->type) + switch (evtype) { case ev_keydown: - if (ev->key == gamecontrol[GC_PAUSE][0] - || ev->key == gamecontrol[GC_PAUSE][1] - || ev->key == KEY_PAUSE) + if (key == gamecontrol[GC_PAUSE][0] + || key == gamecontrol[GC_PAUSE][1] + || key == KEY_PAUSE) { if (modeattacking && !demoplayback && (gamestate == GS_LEVEL)) { - pausebreakkey = (ev->key == KEY_PAUSE); + pausebreakkey = (key == KEY_PAUSE); if (menuactive || pausedelay < 0 || leveltime < 2) return true; @@ -2162,8 +2303,8 @@ boolean G_Responder(event_t *ev) } } } - if (ev->key == gamecontrol[GC_CAMTOGGLE][0] - || ev->key == gamecontrol[GC_CAMTOGGLE][1]) + if (key == gamecontrol[GC_CAMTOGGLE][0] + || key == gamecontrol[GC_CAMTOGGLE][1]) { if (!camtoggledelay) { @@ -2171,8 +2312,8 @@ boolean G_Responder(event_t *ev) CV_SetValue(&cv_chasecam, cv_chasecam.value ? 0 : 1); } } - if (ev->key == gamecontrolbis[GC_CAMTOGGLE][0] - || ev->key == gamecontrolbis[GC_CAMTOGGLE][1]) + if (key == gamecontrolbis[GC_CAMTOGGLE][0] + || key == gamecontrolbis[GC_CAMTOGGLE][1]) { if (!camtoggledelay2) { @@ -2185,15 +2326,9 @@ boolean G_Responder(event_t *ev) case ev_keyup: return false; // always let key up events filter down - case ev_mouse: - return true; // eat events - - case ev_joystick: - return true; // eat events - - case ev_joystick2: - return true; // eat events - + case ev_mouse: // eat events + case ev_gamepad_axis: + return true; default: break; @@ -2220,6 +2355,10 @@ boolean G_LuaResponder(event_t *ev) cancelled = LUA_HookKey(ev, HOOK(KeyUp)); LUA_InvalidateUserdata(ev); } + else if (ev->type == ev_gamepad_down) + cancelled = LUA_HookGamepadButton(ev, HOOK(GamepadButtonDown)); + else if (ev->type == ev_gamepad_up) + cancelled = LUA_HookGamepadButton(ev, HOOK(GamepadButtonUp)); return cancelled; } @@ -3187,14 +3326,7 @@ void G_DoReborn(INT32 playernum) P_ResetCamera(&players[secondarydisplayplayer], &camera2); // clear cmd building stuff - memset(gamekeydown, 0, sizeof (gamekeydown)); - for (i = 0; i < JOYAXISSET; i++) - { - joyxmove[i] = joyymove[i] = 0; - joy2xmove[i] = joy2ymove[i] = 0; - } - G_SetMouseDeltas(0, 0, 1); - G_SetMouseDeltas(0, 0, 2); + G_ResetInputs(); // clear hud messages remains (usually from game startup) CON_ClearHUD(); @@ -4602,11 +4734,7 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride) } save_p += VERSIONSIZE; -// if (demoplayback) // reset game engine -// G_StopDemo(); - -// paused = false; -// automapactive = false; + P_StopRumble(NULL); // dearchive all the modifications if (!P_LoadGame(mapoverride)) @@ -4869,6 +4997,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean { INT32 i; + P_StopRumble(NULL); Y_CleanupScreenBuffer(); if (paused) diff --git a/src/g_game.h b/src/g_game.h index dca043f2e..e798176af 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -68,10 +68,14 @@ typedef enum { #define P_ControlStyle(player) ((((player)->pflags & PF_ANALOGMODE) ? CS_LMAOGALOG : 0) | (((player)->pflags & PF_DIRECTIONCHAR) ? CS_STANDARD : 0)) extern consvar_t cv_autobrake, cv_autobrake2; -extern consvar_t cv_sideaxis,cv_turnaxis,cv_moveaxis,cv_lookaxis,cv_jumpaxis,cv_spinaxis,cv_fireaxis,cv_firenaxis,cv_deadzone,cv_digitaldeadzone; -extern consvar_t cv_sideaxis2,cv_turnaxis2,cv_moveaxis2,cv_lookaxis2,cv_jumpaxis2,cv_spinaxis2,cv_fireaxis2,cv_firenaxis2,cv_deadzone2,cv_digitaldeadzone2; extern consvar_t cv_ghost_bestscore, cv_ghost_besttime, cv_ghost_bestrings, cv_ghost_last, cv_ghost_guest; +extern consvar_t cv_sideaxis[2], cv_turnaxis[2], cv_moveaxis[2], cv_lookaxis[2], + cv_jumpaxis[2], cv_spinaxis[2], cv_fireaxis[2], cv_firenaxis[2], + cv_deadzone[2], cv_digitaldeadzone[2]; + +extern CV_PossibleValue_t joyaxis_cons_t[]; + // hi here's some new controls extern consvar_t cv_cam_shiftfacing[2], cv_cam_turnfacing[2], cv_cam_turnfacingability[2], cv_cam_turnfacingspindash[2], cv_cam_turnfacinginput[2], @@ -84,10 +88,12 @@ typedef enum LOCK_INTERESTS = 1<<2, } lockassist_e; +// Legacy axis stuff +#define JOYAXISSET 4 // 4 Sets of 2 axes typedef enum { - JA_NONE = 0, + JA_NONE, JA_TURN, JA_MOVE, JA_LOOK, @@ -101,8 +107,7 @@ typedef enum JA_FIRENORMAL, } joyaxis_e; -INT32 JoyAxis(joyaxis_e axissel); -INT32 Joy2Axis(joyaxis_e axissel); +INT16 G_JoyAxis(UINT8 which, joyaxis_e axissel); // mouseaiming (looking up/down with the mouse or keyboard) #define KB_LOOKSPEED (1<<25) @@ -122,6 +127,18 @@ ticcmd_t *G_CopyTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n); // copy ticcmd_t to and fro network packets ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n); +// gets the user-set gamepad device for a specific player +INT32 G_GetGamepadDeviceIndex(INT32 player); + +// returns a player's gamepad index +INT16 G_GetGamepadForPlayer(player_t *player); + +// called when a player's gamepad is connected +void G_OnGamepadConnect(UINT8 which); + +// called when a player's gamepad is disconnected +void G_OnGamepadDisconnect(UINT8 which); + // clip the console player aiming to the view INT16 G_ClipAimingPitch(INT32 *aiming); INT16 G_SoftwareClipAimingPitch(INT32 *aiming); diff --git a/src/g_input.c b/src/g_input.c index 79bd2a4a2..465db0316 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -8,12 +8,14 @@ // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file g_input.c -/// \brief handle mouse/keyboard/joystick inputs, +/// \brief handle mouse/keyboard/gamepad inputs, /// maps inputs to game controls (forward, spin, jump...) #include "doomdef.h" #include "doomstat.h" +#include "g_game.h" #include "g_input.h" +#include "i_gamepad.h" #include "keys.h" #include "hu_stuff.h" // need HUFONT start & end #include "d_net.h" @@ -34,8 +36,7 @@ consvar_t cv_controlperkey = CVAR_INIT ("controlperkey", "One", CV_SAVE, onecont mouse_t mouse; mouse_t mouse2; -// joystick values are repeated -INT32 joyxmove[JOYAXISSET], joyymove[JOYAXISSET], joy2xmove[JOYAXISSET], joy2ymove[JOYAXISSET]; +gamepad_t gamepads[NUM_GAMEPADS]; // current state of the keys: true if pushed UINT8 gamekeydown[NUMINPUTS]; @@ -86,114 +87,77 @@ const INT32 gcl_jump_spin[num_gcl_jump_spin] = { GC_JUMP, GC_SPIN }; +static boolean CheckInputDown(UINT8 which, gamecontrols_e gc, boolean checkaxes) +{ + INT32 (*controls)[2] = which == 0 ? gamecontrol : gamecontrolbis; + + for (unsigned i = 0; i < 2; i++) + { + INT32 key = controls[gc][i]; + + if (key >= KEY_GAMEPAD && key < KEY_AXES) + { + if (gamepads[which].buttons[key - KEY_GAMEPAD]) + return true; + } + else if (checkaxes && (key >= KEY_AXES && key < KEY_INV_AXES + NUM_GAMEPAD_AXES)) + { + const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(which); + const INT16 value = G_GetGamepadAxisValue(which, (key - KEY_AXES) % NUM_GAMEPAD_AXES); + + if (abs(value) > jdeadzone) + return true; + } + else if (gamekeydown[key]) + return true; + } + + return false; +} + +boolean G_PlayerInputDown(UINT8 which, gamecontrols_e gc) +{ + return CheckInputDown(which, gc, true); +} + +boolean G_CheckDigitalPlayerInput(UINT8 which, gamecontrols_e gc) +{ + return CheckInputDown(which, gc, false); +} + +SINT8 G_PlayerInputIsAnalog(UINT8 which, gamecontrols_e gc, UINT8 settings) +{ + INT32 (*controls)[2] = which == 0 ? gamecontrol : gamecontrolbis; + INT32 key = controls[gc][settings]; + + if (key >= KEY_AXES && key < KEY_AXES + NUM_GAMEPAD_AXES) + return 1; + else if (key >= KEY_INV_AXES && key < KEY_INV_AXES + NUM_GAMEPAD_AXES) + return -1; + + return 0; +} + +INT16 G_GetAnalogPlayerInput(UINT8 which, gamecontrols_e gc, UINT8 settings) +{ + INT32 (*controls)[2] = which == 0 ? gamecontrol : gamecontrolbis; + INT32 key = controls[gc][settings]; + + if (key >= KEY_AXES && key < KEY_INV_AXES + NUM_GAMEPAD_AXES) + return G_GetGamepadAxisValue(which, (key - KEY_AXES) % NUM_GAMEPAD_AXES); + + return 0; +} + typedef struct { UINT8 time; UINT8 state; UINT8 clicks; } dclick_t; + static dclick_t mousedclicks[MOUSEBUTTONS]; -static dclick_t joydclicks[JOYBUTTONS + JOYHATS*4]; static dclick_t mouse2dclicks[MOUSEBUTTONS]; -static dclick_t joy2dclicks[JOYBUTTONS + JOYHATS*4]; - -// protos -static UINT8 G_CheckDoubleClick(UINT8 state, dclick_t *dt); - -// -// Remaps the inputs to game controls. -// -// A game control can be triggered by one or more keys/buttons. -// -// Each key/mousebutton/joybutton triggers ONLY ONE game control. -// -void G_MapEventsToControls(event_t *ev) -{ - INT32 i; - UINT8 flag; - - switch (ev->type) - { - case ev_keydown: - if (ev->key < NUMINPUTS) - gamekeydown[ev->key] = 1; -#ifdef PARANOIA - else - { - CONS_Debug(DBG_GAMELOGIC, "Bad downkey input %d\n",ev->key); - } - -#endif - break; - - case ev_keyup: - if (ev->key < NUMINPUTS) - gamekeydown[ev->key] = 0; -#ifdef PARANOIA - else - { - CONS_Debug(DBG_GAMELOGIC, "Bad upkey input %d\n",ev->key); - } -#endif - break; - - case ev_mouse: // buttons are virtual keys - mouse.rdx = ev->x; - mouse.rdy = ev->y; - break; - - case ev_joystick: // buttons are virtual keys - i = ev->key; - if (i >= JOYAXISSET || menuactive || CON_Ready() || chat_on) - break; - if (ev->x != INT32_MAX) joyxmove[i] = ev->x; - if (ev->y != INT32_MAX) joyymove[i] = ev->y; - break; - - case ev_joystick2: // buttons are virtual keys - i = ev->key; - if (i >= JOYAXISSET || menuactive || CON_Ready() || chat_on) - break; - if (ev->x != INT32_MAX) joy2xmove[i] = ev->x; - if (ev->y != INT32_MAX) joy2ymove[i] = ev->y; - break; - - case ev_mouse2: // buttons are virtual keys - if (menuactive || CON_Ready() || chat_on) - break; - mouse2.rdx = ev->x; - mouse2.rdy = ev->y; - break; - - default: - break; - } - - // ALWAYS check for mouse & joystick double-clicks even if no mouse event - for (i = 0; i < MOUSEBUTTONS; i++) - { - flag = G_CheckDoubleClick(gamekeydown[KEY_MOUSE1+i], &mousedclicks[i]); - gamekeydown[KEY_DBLMOUSE1+i] = flag; - } - - for (i = 0; i < JOYBUTTONS + JOYHATS*4; i++) - { - flag = G_CheckDoubleClick(gamekeydown[KEY_JOY1+i], &joydclicks[i]); - gamekeydown[KEY_DBLJOY1+i] = flag; - } - - for (i = 0; i < MOUSEBUTTONS; i++) - { - flag = G_CheckDoubleClick(gamekeydown[KEY_2MOUSE1+i], &mouse2dclicks[i]); - gamekeydown[KEY_DBL2MOUSE1+i] = flag; - } - - for (i = 0; i < JOYBUTTONS + JOYHATS*4; i++) - { - flag = G_CheckDoubleClick(gamekeydown[KEY_2JOY1+i], &joy2dclicks[i]); - gamekeydown[KEY_DBL2JOY1+i] = flag; - } -} // // General double-click detection routine for any kind of input. @@ -225,6 +189,641 @@ static UINT8 G_CheckDoubleClick(UINT8 state, dclick_t *dt) return false; } +// +// Remaps the inputs to game controls. +// +// A game control can be triggered by one or more keys/buttons. +// +// Each key/mouse button/gamepad button triggers ONLY ONE game control. +// +void G_MapEventsToControls(event_t *ev) +{ + INT32 i; + UINT8 flag; + + switch (ev->type) + { + case ev_keydown: + if (ev->key < NUMINPUTS) + gamekeydown[ev->key] = 1; +#ifdef PARANOIA + else + CONS_Debug(DBG_GAMELOGIC, "Bad downkey input %d\n",ev->key); + +#endif + break; + + case ev_keyup: + if (ev->key < NUMINPUTS) + gamekeydown[ev->key] = 0; +#ifdef PARANOIA + else + CONS_Debug(DBG_GAMELOGIC, "Bad upkey input %d\n",ev->key); +#endif + break; + + case ev_gamepad_down: + case ev_gamepad_up: +#ifdef PARANOIA + if (ev->which < NUM_GAMEPADS) +#endif + gamepads[ev->which].buttons[ev->key] = ev->type == ev_gamepad_down ? 1 : 0; + break; + + case ev_gamepad_axis: +#ifdef PARANOIA + if (ev->which < NUM_GAMEPADS) +#endif + gamepads[ev->which].axes[ev->key] = ev->x; + break; + + case ev_mouse: + mouse.rdx = ev->x; + mouse.rdy = ev->y; + break; + + case ev_mouse2: + mouse2.rdx = ev->x; + mouse2.rdy = ev->y; + break; + + default: + break; + } + + // ALWAYS check for mouse double-clicks even if there were no such events + for (i = 0; i < MOUSEBUTTONS; i++) + { + flag = G_CheckDoubleClick(gamekeydown[KEY_MOUSE1+i], &mousedclicks[i]); + gamekeydown[KEY_DBLMOUSE1+i] = flag; + } + + for (i = 0; i < MOUSEBUTTONS; i++) + { + flag = G_CheckDoubleClick(gamekeydown[KEY_2MOUSE1+i], &mouse2dclicks[i]); + gamekeydown[KEY_DBL2MOUSE1+i] = flag; + } +} + +const char *const gamepad_button_names[NUM_GAMEPAD_BUTTONS + 1] = { + "a", + "b", + "x", + "y", + "back", + "guide", + "start", + "left-stick", + "right-stick", + "left-shoulder", + "right-shoulder", + "dpad-up", + "dpad-down", + "dpad-left", + "dpad-right", + "misc1", + "paddle1", + "paddle2", + "paddle3", + "paddle4", + "touchpad", + NULL}; + +const char *const gamepad_axis_names[NUM_GAMEPAD_AXES + 1] = { + "left-x", + "left-y", + "right-x", + "right-y", + "trigger-left", + "trigger-right", + NULL}; + +boolean G_GamepadTypeIsXbox(gamepadtype_e type) +{ + switch (type) + { + case GAMEPAD_TYPE_XBOX360: + case GAMEPAD_TYPE_XBOXONE: + case GAMEPAD_TYPE_XBOX_SERIES_XS: + case GAMEPAD_TYPE_XBOX_ELITE: + return true; + default: + return false; + } +} + +boolean G_GamepadTypeIsPlayStation(gamepadtype_e type) +{ + switch (type) + { + case GAMEPAD_TYPE_PS3: + case GAMEPAD_TYPE_PS4: + case GAMEPAD_TYPE_PS5: + return true; + default: + return false; + } +} + +boolean G_GamepadTypeIsNintendoSwitch(gamepadtype_e type) +{ + switch (type) + { + case GAMEPAD_TYPE_NINTENDO_SWITCH_PRO: + case GAMEPAD_TYPE_NINTENDO_SWITCH_JOY_CON_GRIP: + return true; + default: + return G_GamepadTypeIsJoyCon(type); + } +} + +boolean G_GamepadTypeIsJoyCon(gamepadtype_e type) +{ + switch (type) + { + case GAMEPAD_TYPE_NINTENDO_SWITCH_JOY_CON_LEFT: + case GAMEPAD_TYPE_NINTENDO_SWITCH_JOY_CON_RIGHT: + return true; + default: + return false; + } +} + +boolean G_RumbleSupported(UINT8 which) +{ + if (!I_RumbleSupported() || which >= NUM_GAMEPADS) + return 0; + + return I_GetGamepadRumbleSupported(which); +} + +boolean G_RumbleGamepad(UINT8 which, fixed_t large_magnitude, fixed_t small_magnitude, tic_t duration) +{ + haptic_t effect; + + if (!G_RumbleSupported(which)) + return false; + + effect.large_magnitude = large_magnitude; + effect.small_magnitude = small_magnitude; + effect.duration = duration; + + return I_RumbleGamepad(which, &effect); +} + +void G_StopGamepadRumble(UINT8 which) +{ + if (G_RumbleSupported(which)) + I_StopGamepadRumble(which); +} + +fixed_t G_GetLargeMotorFreq(UINT8 which) +{ + if (!G_RumbleSupported(which) || which >= NUM_GAMEPADS) + return 0; + + gamepad_t *gamepad = &gamepads[which]; + return gamepad->rumble.data.large_magnitude; +} + +fixed_t G_GetSmallMotorFreq(UINT8 which) +{ + if (!G_RumbleSupported(which) || which >= NUM_GAMEPADS) + return 0; + + gamepad_t *gamepad = &gamepads[which]; + return gamepad->rumble.data.small_magnitude; +} + +boolean G_GetGamepadRumblePaused(UINT8 which) +{ + return I_GetGamepadRumblePaused(which); +} + +boolean G_SetLargeMotorFreq(UINT8 which, fixed_t freq) +{ + return I_SetGamepadLargeMotorFreq(which, freq); +} + +boolean G_SetSmallMotorFreq(UINT8 which, fixed_t freq) +{ + return I_SetGamepadSmallMotorFreq(which, freq); +} + +void G_SetGamepadRumblePaused(UINT8 which, boolean pause) +{ + if (G_RumbleSupported(which)) + I_SetGamepadRumblePaused(which, pause); +} + +// Obtains the value of an axis, and makes it digital if needed +INT16 G_GamepadAxisEventValue(UINT8 which, INT16 value) +{ + gamepad_t *gamepad = &gamepads[which]; + + if (gamepad->digital) + { + const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(which); + + if (value < -jdeadzone) + value = -JOYAXISRANGE - 1; + else if (value > jdeadzone) + value = JOYAXISRANGE; + else + value = 0; + } + + return value; +} + +INT16 G_GetGamepadAxisValue(UINT8 which, gamepad_axis_e axis) +{ + gamepad_t *gamepad = &gamepads[which]; + + if (axis >= NUM_GAMEPAD_AXES) + return 0; + + return G_GamepadAxisEventValue(which, gamepad->axes[axis]); +} + +fixed_t G_GetAdjustedGamepadAxis(UINT8 which, gamepad_axis_e axis, boolean applyDeadzone) +{ + gamepad_t *gamepad = &gamepads[which]; + + if (axis >= NUM_GAMEPAD_AXES) + return 0; + + INT32 value = gamepad->axes[axis]; + + if (applyDeadzone && gamepad->digital) + { + INT16 deadzone = G_GetGamepadDigitalDeadZone(which); + + if (value < -deadzone) + value = -JOYAXISRANGE; + else if (value > deadzone) + value = JOYAXISRANGE; + else + value = 0; + } + else if (applyDeadzone) + { + INT32 sign = value < 0 ? -1 : 1; + INT16 deadzone = G_GetGamepadDeadZone(which); + INT32 magnitude = value * value; + INT32 nAxis = magnitude / JOYAXISRANGE; + INT32 nMagnitude = G_BasicDeadZoneCalculation(magnitude, deadzone); + + value = (nAxis * nMagnitude) / JOYAXISRANGE; + value = min(value * sign, JOYAXISRANGE); + value = max(value, -JOYAXISRANGE); + } + + return (value / 32767.0) * FRACUNIT; +} + +static UINT16 CalcGamepadDeadZone(fixed_t deadzone) +{ + INT32 value = (JOYAXISRANGE * deadzone) / FRACUNIT; + + if (value < 0) + value = 0; + else if (value > JOYAXISRANGE) + value = JOYAXISRANGE; + + return value; +} + +UINT16 G_GetGamepadDeadZone(UINT8 which) +{ + return CalcGamepadDeadZone(cv_deadzone[which].value); +} + +UINT16 G_GetGamepadDigitalDeadZone(UINT8 which) +{ + return CalcGamepadDeadZone(cv_digitaldeadzone[which].value); +} + +// Take a magnitude of two axes, and adjust it to take out the deadzone +// Will return a value between 0 and JOYAXISRANGE +INT32 G_BasicDeadZoneCalculation(INT32 magnitude, const UINT16 jdeadzone) +{ + INT32 deadzoneAppliedValue = 0; + INT32 adjustedMagnitude = abs(magnitude); + + if (jdeadzone >= JOYAXISRANGE && adjustedMagnitude >= JOYAXISRANGE) // If the deadzone and magnitude are both 100%... + return JOYAXISRANGE; // ...return 100% input directly, to avoid dividing by 0 + else if (adjustedMagnitude > jdeadzone) // Otherwise, calculate how much the magnitude exceeds the deadzone + { + adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE); + + adjustedMagnitude -= jdeadzone; + + deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone); + } + + return deadzoneAppliedValue; +} + +INT32 G_RemapGamepadEvent(event_t *event, INT32 *type) +{ + if (event->type == ev_gamepad_down) + { + *type = ev_keydown; + return KEY_GAMEPAD + event->key; + } + else if (event->type == ev_gamepad_up) + { + *type = ev_keyup; + return KEY_GAMEPAD + event->key; + } + else if (event->type == ev_gamepad_axis) + { + const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(event->which); + const INT16 value = G_GetGamepadAxisValue(event->which, event->key); + + if (value < -jdeadzone || value > jdeadzone) + *type = ev_keyup; + else + *type = ev_keydown; + + if (value < -jdeadzone) + return KEY_INV_AXES + event->key; + else + return KEY_AXES + event->key; + } + + return event->key; +} + +typedef struct +{ + const char *name; + const char *menu1; + const char *menu2; +} button_strings_t; + +#define DEF_NAME_BUTTON(str) {.name = str, .menu1 = str " Button", .menu2 = "the " str " Button"} +#define DEF_NAME_SIMPLE(str) {.name = str, .menu1 = NULL, .menu2 = "the " str} +#define DEF_NAME_DPAD(a, b) {.name = "D-Pad " a, .menu1 = "D-Pad " b, .menu2 = a} + +#define PARTIAL_DEF_START [GAMEPAD_BUTTON_A] = { NULL } +#define PARTIAL_DEF_END [NUM_GAMEPAD_BUTTONS - 1] = { NULL } + +static const char *GetStringFromButtonList(const button_strings_t *names, gamepad_button_e button, gamepad_string_e type) +{ + switch (type) + { + case GAMEPAD_STRING_DEFAULT: + return names[button].name; + case GAMEPAD_STRING_MENU1: + if (names[button].menu1) + return names[button].menu1; + else + return names[button].name; + case GAMEPAD_STRING_MENU2: + if (names[button].menu2) + return names[button].menu2; + else + return names[button].name; + } + + return NULL; +} + +const char *G_GetGamepadButtonString(gamepadtype_e type, gamepad_button_e button, gamepad_string_e strtype) +{ + static const button_strings_t base_names[] = { + [GAMEPAD_BUTTON_A] = DEF_NAME_BUTTON("A"), + [GAMEPAD_BUTTON_B] = DEF_NAME_BUTTON("B"), + [GAMEPAD_BUTTON_X] = DEF_NAME_BUTTON("X"), + [GAMEPAD_BUTTON_Y] = DEF_NAME_BUTTON("Y"), + [GAMEPAD_BUTTON_BACK] = DEF_NAME_BUTTON("Back"), + [GAMEPAD_BUTTON_GUIDE] = DEF_NAME_BUTTON("Guide"), + [GAMEPAD_BUTTON_START] = DEF_NAME_BUTTON("Start"), + [GAMEPAD_BUTTON_LEFTSTICK] = DEF_NAME_SIMPLE("Left Stick"), + [GAMEPAD_BUTTON_RIGHTSTICK] = DEF_NAME_SIMPLE("Right Stick"), + [GAMEPAD_BUTTON_LEFTSHOULDER] = DEF_NAME_SIMPLE("Left Shoulder"), + [GAMEPAD_BUTTON_RIGHTSHOULDER] = DEF_NAME_SIMPLE("Right Shoulder"), + [GAMEPAD_BUTTON_DPAD_UP] = DEF_NAME_DPAD("Up", "\x1A"), + [GAMEPAD_BUTTON_DPAD_DOWN] = DEF_NAME_DPAD("Down", "\x1B"), + [GAMEPAD_BUTTON_DPAD_LEFT] = DEF_NAME_DPAD("Left", "\x1C"), + [GAMEPAD_BUTTON_DPAD_RIGHT] = DEF_NAME_DPAD("Right", "\x1D"), + [GAMEPAD_BUTTON_PADDLE1] = DEF_NAME_SIMPLE("Paddle 1"), + [GAMEPAD_BUTTON_PADDLE2] = DEF_NAME_SIMPLE("Paddle 2"), + [GAMEPAD_BUTTON_PADDLE3] = DEF_NAME_SIMPLE("Paddle 3"), + [GAMEPAD_BUTTON_PADDLE4] = DEF_NAME_SIMPLE("Paddle 4"), + [GAMEPAD_BUTTON_TOUCHPAD] = DEF_NAME_SIMPLE("Touchpad"), + + // This one's a bit weird + // Suffix the numbers in the event SDL adds more misc buttons + [GAMEPAD_BUTTON_MISC1] = { + .name = "Misc. Button", + .menu1 = "Gamepad Misc.", + .menu2 = "the Misc. Button" + }, + }; + + button_strings_t const *names = NULL; + + if (G_GamepadTypeIsXbox(type)) + { + #define BASE_XBOX_NAMES \ + [GAMEPAD_BUTTON_LEFTSHOULDER] = DEF_NAME_SIMPLE("Left Bumper"), \ + [GAMEPAD_BUTTON_RIGHTSHOULDER] = DEF_NAME_SIMPLE("Right Bumper") + + static const button_strings_t xbox_names[] = { PARTIAL_DEF_START, + BASE_XBOX_NAMES, + PARTIAL_DEF_END }; + + static const button_strings_t series_xs_names[] = { PARTIAL_DEF_START, + BASE_XBOX_NAMES, + [GAMEPAD_BUTTON_MISC1] = DEF_NAME_BUTTON("Share"), + PARTIAL_DEF_END }; + + static const button_strings_t elite_names[] = { PARTIAL_DEF_START, + BASE_XBOX_NAMES, + [GAMEPAD_BUTTON_PADDLE1] = DEF_NAME_SIMPLE("P1 Paddle"), + [GAMEPAD_BUTTON_PADDLE2] = DEF_NAME_SIMPLE("P2 Paddle"), + [GAMEPAD_BUTTON_PADDLE3] = DEF_NAME_SIMPLE("P3 Paddle"), + [GAMEPAD_BUTTON_PADDLE4] = DEF_NAME_SIMPLE("P4 Paddle"), + PARTIAL_DEF_END }; + + if (type == GAMEPAD_TYPE_XBOX_SERIES_XS) // X|S controllers have a Share button + names = series_xs_names; + else if (type == GAMEPAD_TYPE_XBOX_ELITE) // Elite controller has paddles + names = elite_names; + else + names = xbox_names; + + #undef BASE_XBOX_NAMES + } + else if (G_GamepadTypeIsPlayStation(type)) + { + #define BASE_PS_NAMES \ + [GAMEPAD_BUTTON_A] = DEF_NAME_BUTTON("Cross"), \ + [GAMEPAD_BUTTON_B] = DEF_NAME_BUTTON("Circle"), \ + [GAMEPAD_BUTTON_X] = DEF_NAME_BUTTON("Square"), \ + [GAMEPAD_BUTTON_Y] = DEF_NAME_BUTTON("Triangle"), \ + [GAMEPAD_BUTTON_BACK] = DEF_NAME_BUTTON("Select"), \ + [GAMEPAD_BUTTON_GUIDE] = DEF_NAME_BUTTON("PS"), \ + [GAMEPAD_BUTTON_LEFTSTICK] = DEF_NAME_BUTTON("L3"), \ + [GAMEPAD_BUTTON_RIGHTSTICK] = DEF_NAME_BUTTON("R3"), \ + [GAMEPAD_BUTTON_LEFTSHOULDER] = DEF_NAME_BUTTON("L1"), \ + [GAMEPAD_BUTTON_RIGHTSHOULDER] = DEF_NAME_BUTTON("R1") + + static const button_strings_t ps_names[] = { + BASE_PS_NAMES, + PARTIAL_DEF_END }; + + static const button_strings_t ps5_names[] = { + BASE_PS_NAMES, + [GAMEPAD_BUTTON_MISC1] = DEF_NAME_BUTTON("Microphone"), + PARTIAL_DEF_END }; + + names = type == GAMEPAD_TYPE_PS5 ? ps5_names : ps_names; + #undef BASE_PS_NAMES + } + else if (G_GamepadTypeIsNintendoSwitch(type)) + { + static const button_strings_t switch_names[] = { PARTIAL_DEF_START, + [GAMEPAD_BUTTON_BACK] = DEF_NAME_BUTTON("-"), + [GAMEPAD_BUTTON_GUIDE] = DEF_NAME_BUTTON("HOME"), + [GAMEPAD_BUTTON_START] = DEF_NAME_BUTTON("+"), + [GAMEPAD_BUTTON_LEFTSHOULDER] = DEF_NAME_BUTTON("L"), + [GAMEPAD_BUTTON_RIGHTSHOULDER] = DEF_NAME_BUTTON("R"), + [GAMEPAD_BUTTON_MISC1] = DEF_NAME_BUTTON("Capture"), + PARTIAL_DEF_END }; + + names = switch_names; + } + else if (type == GAMEPAD_TYPE_AMAZON_LUNA) + { + static const button_strings_t luna_names[] = { PARTIAL_DEF_START, + [GAMEPAD_BUTTON_MISC1] = DEF_NAME_BUTTON("Microphone"), + PARTIAL_DEF_END }; + + names = luna_names; + } + + const char *str = NULL; + + if (names) + str = GetStringFromButtonList(names, button, strtype); + if (str == NULL) + str = GetStringFromButtonList(base_names, button, strtype); + if (str) + return str; + + return "Unknown"; +} + +#undef DEF_NAME_BUTTON +#undef DEF_NAME_SIMPLE +#undef DEF_NAME_DPAD + +#undef PARTIAL_DEF_START +#undef PARTIAL_DEF_END + +typedef struct +{ + const char *name; + const char *menu1; + const char *menu2; + const char *name_inv; + const char *menu1_inv; + const char *menu2_inv; +} axis_strings_t; + +#define DEF_NAME_AXIS(str, a, inv_a, b, inv_b) {\ + str " " a, str " " b, "the " str " " b, \ + str " " inv_a, str " " inv_b, "the " str " " inv_b} +#define DEF_NAME_TRIGGER(str) {str, NULL, "the " str, NULL, NULL, NULL} +#define DEF_NAME_BUTTON(str) {str, str " Button", "the " str " Button", NULL, NULL, NULL} + +#define PARTIAL_DEF_START [GAMEPAD_AXIS_LEFTX] = { NULL } + +static const char *GetStringFromAxisList(const axis_strings_t *names, gamepad_axis_e axis, gamepad_string_e type, boolean inv) +{ + switch (type) + { + case GAMEPAD_STRING_DEFAULT: + if (inv && names[axis].name_inv) + return names[axis].name_inv; + else + return names[axis].name; + break; + case GAMEPAD_STRING_MENU1: + if (inv && names[axis].menu1_inv) + return names[axis].menu1_inv; + if (names[axis].menu1) + return names[axis].menu1; + else + return names[axis].name; + break; + case GAMEPAD_STRING_MENU2: + if (inv && names[axis].menu2_inv) + return names[axis].menu2_inv; + if (names[axis].menu2) + return names[axis].menu2; + else + return names[axis].name; + break; + } + + return NULL; +} + +const char *G_GetGamepadAxisString(gamepadtype_e type, gamepad_axis_e axis, gamepad_string_e strtype, boolean inv) +{ + static const axis_strings_t base_names[] = { + [GAMEPAD_AXIS_LEFTX] = DEF_NAME_AXIS("Left Stick", "X", "X-", "\x1D", "\x1C"), + [GAMEPAD_AXIS_LEFTY] = DEF_NAME_AXIS("Left Stick", "Y", "Y-", "\x1B", "\x1A"), + [GAMEPAD_AXIS_RIGHTX] = DEF_NAME_AXIS("Right Stick", "X", "X-", "\x1D", "\x1C"), + [GAMEPAD_AXIS_RIGHTY] = DEF_NAME_AXIS("Right Stick", "Y", "Y-", "\x1B", "\x1A"), + [GAMEPAD_AXIS_TRIGGERLEFT] = DEF_NAME_TRIGGER("Left Trigger"), + [GAMEPAD_AXIS_TRIGGERRIGHT] = DEF_NAME_TRIGGER("Right Trigger") + }; + + axis_strings_t const *names = NULL; + + if (G_GamepadTypeIsPlayStation(type)) + { + static const axis_strings_t ps_names[] = { PARTIAL_DEF_START, + [GAMEPAD_AXIS_TRIGGERLEFT] = DEF_NAME_BUTTON("L2"), + [GAMEPAD_AXIS_TRIGGERRIGHT] = DEF_NAME_BUTTON("R2"), + }; + + names = ps_names; + } + else if (G_GamepadTypeIsNintendoSwitch(type)) + { + static const axis_strings_t switch_names[] = { PARTIAL_DEF_START, + [GAMEPAD_AXIS_TRIGGERLEFT] = DEF_NAME_BUTTON("ZL"), + [GAMEPAD_AXIS_TRIGGERRIGHT] = DEF_NAME_BUTTON("ZR"), + }; + + names = switch_names; + } + + const char *str = NULL; + + if (names) + str = GetStringFromAxisList(names, axis, strtype, inv); + if (str == NULL) + str = GetStringFromAxisList(base_names, axis, strtype, inv); + if (str) + return str; + + return "Unknown"; +} + +#undef DEF_NAME_AXIS +#undef DEF_NAME_TRIGGER +#undef DEF_NAME_BUTTON + +#undef PARTIAL_DEF_START + typedef struct { INT32 keynum; @@ -243,20 +842,17 @@ static keyname_t keynames[] = {KEY_NUMLOCK, "numlock"}, {KEY_SCROLLLOCK, "scrolllock"}, - // bill gates keys + // satya nadella keys {KEY_LEFTWIN, "leftwin"}, {KEY_RIGHTWIN, "rightwin"}, {KEY_MENU, "menu"}, {KEY_LSHIFT, "lshift"}, {KEY_RSHIFT, "rshift"}, - {KEY_LSHIFT, "shift"}, {KEY_LCTRL, "lctrl"}, {KEY_RCTRL, "rctrl"}, - {KEY_LCTRL, "ctrl"}, {KEY_LALT, "lalt"}, {KEY_RALT, "ralt"}, - {KEY_LALT, "alt"}, // keypad keys {KEY_KPADSLASH, "keypad /"}, @@ -304,7 +900,7 @@ static keyname_t keynames[] = {'`', "TILDE"}, {KEY_PAUSE, "pause/break"}, - // virtual keys for mouse buttons and joystick buttons + // virtual keys for mouse buttons and gamepad buttons {KEY_MOUSE1+0,"mouse1"}, {KEY_MOUSE1+1,"mouse2"}, {KEY_MOUSE1+2,"mouse3"}, @@ -313,8 +909,8 @@ static keyname_t keynames[] = {KEY_MOUSE1+5,"mouse6"}, {KEY_MOUSE1+6,"mouse7"}, {KEY_MOUSE1+7,"mouse8"}, - {KEY_2MOUSE1+0,"sec_mouse2"}, // BP: sorry my mouse handler swap button 1 and 2 - {KEY_2MOUSE1+1,"sec_mouse1"}, + {KEY_2MOUSE1+0,"sec_mouse1"}, + {KEY_2MOUSE1+1,"sec_mouse2"}, {KEY_2MOUSE1+2,"sec_mouse3"}, {KEY_2MOUSE1+3,"sec_mouse4"}, {KEY_2MOUSE1+4,"sec_mouse5"}, @@ -326,58 +922,48 @@ static keyname_t keynames[] = {KEY_2MOUSEWHEELUP, "wheel 2 up"}, {KEY_2MOUSEWHEELDOWN, "wheel 2 down"}, - {KEY_JOY1+0, "joy1"}, - {KEY_JOY1+1, "joy2"}, - {KEY_JOY1+2, "joy3"}, - {KEY_JOY1+3, "joy4"}, - {KEY_JOY1+4, "joy5"}, - {KEY_JOY1+5, "joy6"}, - {KEY_JOY1+6, "joy7"}, - {KEY_JOY1+7, "joy8"}, - {KEY_JOY1+8, "joy9"}, -#if !defined (NOMOREJOYBTN_1S) - // we use up to 32 buttons in DirectInput - {KEY_JOY1+9, "joy10"}, - {KEY_JOY1+10, "joy11"}, - {KEY_JOY1+11, "joy12"}, - {KEY_JOY1+12, "joy13"}, - {KEY_JOY1+13, "joy14"}, - {KEY_JOY1+14, "joy15"}, - {KEY_JOY1+15, "joy16"}, - {KEY_JOY1+16, "joy17"}, - {KEY_JOY1+17, "joy18"}, - {KEY_JOY1+18, "joy19"}, - {KEY_JOY1+19, "joy20"}, - {KEY_JOY1+20, "joy21"}, - {KEY_JOY1+21, "joy22"}, - {KEY_JOY1+22, "joy23"}, - {KEY_JOY1+23, "joy24"}, - {KEY_JOY1+24, "joy25"}, - {KEY_JOY1+25, "joy26"}, - {KEY_JOY1+26, "joy27"}, - {KEY_JOY1+27, "joy28"}, - {KEY_JOY1+28, "joy29"}, - {KEY_JOY1+29, "joy30"}, - {KEY_JOY1+30, "joy31"}, - {KEY_JOY1+31, "joy32"}, -#endif - // the DOS version uses Allegro's joystick support - {KEY_HAT1+0, "hatup"}, - {KEY_HAT1+1, "hatdown"}, - {KEY_HAT1+2, "hatleft"}, - {KEY_HAT1+3, "hatright"}, - {KEY_HAT1+4, "hatup2"}, - {KEY_HAT1+5, "hatdown2"}, - {KEY_HAT1+6, "hatleft2"}, - {KEY_HAT1+7, "hatright2"}, - {KEY_HAT1+8, "hatup3"}, - {KEY_HAT1+9, "hatdown3"}, - {KEY_HAT1+10, "hatleft3"}, - {KEY_HAT1+11, "hatright3"}, - {KEY_HAT1+12, "hatup4"}, - {KEY_HAT1+13, "hatdown4"}, - {KEY_HAT1+14, "hatleft4"}, - {KEY_HAT1+15, "hatright4"}, +#define DEF_GAMEPAD_NAME(btn, name) {KEY_GAMEPAD+GAMEPAD_BUTTON_##btn, name} +#define DEF_GAMEPAD_AXIS(ax, name) \ + {KEY_AXES+GAMEPAD_AXIS_##ax, name}, \ + {KEY_INV_AXES+GAMEPAD_AXIS_##ax, name "-"} + + DEF_GAMEPAD_NAME(A, "a button"), + DEF_GAMEPAD_NAME(B, "b button"), + DEF_GAMEPAD_NAME(X, "x button"), + DEF_GAMEPAD_NAME(Y, "y button"), + + DEF_GAMEPAD_NAME(BACK, "back button"), + DEF_GAMEPAD_NAME(GUIDE, "guide button"), + DEF_GAMEPAD_NAME(START, "start button"), + DEF_GAMEPAD_NAME(LEFTSTICK, "left stick"), + DEF_GAMEPAD_NAME(RIGHTSTICK, "right stick"), + + DEF_GAMEPAD_NAME(LEFTSHOULDER, "left shoulder"), + DEF_GAMEPAD_NAME(RIGHTSHOULDER, "right shoulder"), + + DEF_GAMEPAD_NAME(DPAD_UP, "d-pad up"), + DEF_GAMEPAD_NAME(DPAD_DOWN, "d-pad down"), + DEF_GAMEPAD_NAME(DPAD_LEFT, "d-pad left"), + DEF_GAMEPAD_NAME(DPAD_RIGHT, "d-pad right"), + + DEF_GAMEPAD_NAME(MISC1, "gamepad misc 1"), + DEF_GAMEPAD_NAME(PADDLE1, "paddle 1"), + DEF_GAMEPAD_NAME(PADDLE2, "paddle 2"), + DEF_GAMEPAD_NAME(PADDLE3, "paddle 3"), + DEF_GAMEPAD_NAME(PADDLE4, "paddle 4"), + DEF_GAMEPAD_NAME(TOUCHPAD, "touchpad"), + + DEF_GAMEPAD_AXIS(LEFTX, "left stick x"), + DEF_GAMEPAD_AXIS(LEFTY, "left stick y"), + + DEF_GAMEPAD_AXIS(RIGHTX, "right stick x"), + DEF_GAMEPAD_AXIS(RIGHTY, "right stick y"), + + DEF_GAMEPAD_AXIS(TRIGGERLEFT, "left trigger"), + DEF_GAMEPAD_AXIS(TRIGGERRIGHT, "right trigger"), + +#undef DEF_GAMEPAD_NAME +#undef DEF_GAMEPAD_AXIS {KEY_DBLMOUSE1+0, "dblmouse1"}, {KEY_DBLMOUSE1+1, "dblmouse2"}, @@ -387,172 +973,154 @@ static keyname_t keynames[] = {KEY_DBLMOUSE1+5, "dblmouse6"}, {KEY_DBLMOUSE1+6, "dblmouse7"}, {KEY_DBLMOUSE1+7, "dblmouse8"}, - {KEY_DBL2MOUSE1+0, "dblsec_mouse2"}, // BP: sorry my mouse handler swap button 1 and 2 - {KEY_DBL2MOUSE1+1, "dblsec_mouse1"}, + {KEY_DBL2MOUSE1+0, "dblsec_mouse1"}, + {KEY_DBL2MOUSE1+1, "dblsec_mouse2"}, {KEY_DBL2MOUSE1+2, "dblsec_mouse3"}, {KEY_DBL2MOUSE1+3, "dblsec_mouse4"}, {KEY_DBL2MOUSE1+4, "dblsec_mouse5"}, {KEY_DBL2MOUSE1+5, "dblsec_mouse6"}, {KEY_DBL2MOUSE1+6, "dblsec_mouse7"}, - {KEY_DBL2MOUSE1+7, "dblsec_mouse8"}, - - {KEY_DBLJOY1+0, "dbljoy1"}, - {KEY_DBLJOY1+1, "dbljoy2"}, - {KEY_DBLJOY1+2, "dbljoy3"}, - {KEY_DBLJOY1+3, "dbljoy4"}, - {KEY_DBLJOY1+4, "dbljoy5"}, - {KEY_DBLJOY1+5, "dbljoy6"}, - {KEY_DBLJOY1+6, "dbljoy7"}, - {KEY_DBLJOY1+7, "dbljoy8"}, -#if !defined (NOMOREJOYBTN_1DBL) - {KEY_DBLJOY1+8, "dbljoy9"}, - {KEY_DBLJOY1+9, "dbljoy10"}, - {KEY_DBLJOY1+10, "dbljoy11"}, - {KEY_DBLJOY1+11, "dbljoy12"}, - {KEY_DBLJOY1+12, "dbljoy13"}, - {KEY_DBLJOY1+13, "dbljoy14"}, - {KEY_DBLJOY1+14, "dbljoy15"}, - {KEY_DBLJOY1+15, "dbljoy16"}, - {KEY_DBLJOY1+16, "dbljoy17"}, - {KEY_DBLJOY1+17, "dbljoy18"}, - {KEY_DBLJOY1+18, "dbljoy19"}, - {KEY_DBLJOY1+19, "dbljoy20"}, - {KEY_DBLJOY1+20, "dbljoy21"}, - {KEY_DBLJOY1+21, "dbljoy22"}, - {KEY_DBLJOY1+22, "dbljoy23"}, - {KEY_DBLJOY1+23, "dbljoy24"}, - {KEY_DBLJOY1+24, "dbljoy25"}, - {KEY_DBLJOY1+25, "dbljoy26"}, - {KEY_DBLJOY1+26, "dbljoy27"}, - {KEY_DBLJOY1+27, "dbljoy28"}, - {KEY_DBLJOY1+28, "dbljoy29"}, - {KEY_DBLJOY1+29, "dbljoy30"}, - {KEY_DBLJOY1+30, "dbljoy31"}, - {KEY_DBLJOY1+31, "dbljoy32"}, -#endif - {KEY_DBLHAT1+0, "dblhatup"}, - {KEY_DBLHAT1+1, "dblhatdown"}, - {KEY_DBLHAT1+2, "dblhatleft"}, - {KEY_DBLHAT1+3, "dblhatright"}, - {KEY_DBLHAT1+4, "dblhatup2"}, - {KEY_DBLHAT1+5, "dblhatdown2"}, - {KEY_DBLHAT1+6, "dblhatleft2"}, - {KEY_DBLHAT1+7, "dblhatright2"}, - {KEY_DBLHAT1+8, "dblhatup3"}, - {KEY_DBLHAT1+9, "dblhatdown3"}, - {KEY_DBLHAT1+10, "dblhatleft3"}, - {KEY_DBLHAT1+11, "dblhatright3"}, - {KEY_DBLHAT1+12, "dblhatup4"}, - {KEY_DBLHAT1+13, "dblhatdown4"}, - {KEY_DBLHAT1+14, "dblhatleft4"}, - {KEY_DBLHAT1+15, "dblhatright4"}, - - {KEY_2JOY1+0, "sec_joy1"}, - {KEY_2JOY1+1, "sec_joy2"}, - {KEY_2JOY1+2, "sec_joy3"}, - {KEY_2JOY1+3, "sec_joy4"}, - {KEY_2JOY1+4, "sec_joy5"}, - {KEY_2JOY1+5, "sec_joy6"}, - {KEY_2JOY1+6, "sec_joy7"}, - {KEY_2JOY1+7, "sec_joy8"}, -#if !defined (NOMOREJOYBTN_2S) - // we use up to 32 buttons in DirectInput - {KEY_2JOY1+8, "sec_joy9"}, - {KEY_2JOY1+9, "sec_joy10"}, - {KEY_2JOY1+10, "sec_joy11"}, - {KEY_2JOY1+11, "sec_joy12"}, - {KEY_2JOY1+12, "sec_joy13"}, - {KEY_2JOY1+13, "sec_joy14"}, - {KEY_2JOY1+14, "sec_joy15"}, - {KEY_2JOY1+15, "sec_joy16"}, - {KEY_2JOY1+16, "sec_joy17"}, - {KEY_2JOY1+17, "sec_joy18"}, - {KEY_2JOY1+18, "sec_joy19"}, - {KEY_2JOY1+19, "sec_joy20"}, - {KEY_2JOY1+20, "sec_joy21"}, - {KEY_2JOY1+21, "sec_joy22"}, - {KEY_2JOY1+22, "sec_joy23"}, - {KEY_2JOY1+23, "sec_joy24"}, - {KEY_2JOY1+24, "sec_joy25"}, - {KEY_2JOY1+25, "sec_joy26"}, - {KEY_2JOY1+26, "sec_joy27"}, - {KEY_2JOY1+27, "sec_joy28"}, - {KEY_2JOY1+28, "sec_joy29"}, - {KEY_2JOY1+29, "sec_joy30"}, - {KEY_2JOY1+30, "sec_joy31"}, - {KEY_2JOY1+31, "sec_joy32"}, -#endif - // the DOS version uses Allegro's joystick support - {KEY_2HAT1+0, "sec_hatup"}, - {KEY_2HAT1+1, "sec_hatdown"}, - {KEY_2HAT1+2, "sec_hatleft"}, - {KEY_2HAT1+3, "sec_hatright"}, - {KEY_2HAT1+4, "sec_hatup2"}, - {KEY_2HAT1+5, "sec_hatdown2"}, - {KEY_2HAT1+6, "sec_hatleft2"}, - {KEY_2HAT1+7, "sec_hatright2"}, - {KEY_2HAT1+8, "sec_hatup3"}, - {KEY_2HAT1+9, "sec_hatdown3"}, - {KEY_2HAT1+10, "sec_hatleft3"}, - {KEY_2HAT1+11, "sec_hatright3"}, - {KEY_2HAT1+12, "sec_hatup4"}, - {KEY_2HAT1+13, "sec_hatdown4"}, - {KEY_2HAT1+14, "sec_hatleft4"}, - {KEY_2HAT1+15, "sec_hatright4"}, - - {KEY_DBL2JOY1+0, "dblsec_joy1"}, - {KEY_DBL2JOY1+1, "dblsec_joy2"}, - {KEY_DBL2JOY1+2, "dblsec_joy3"}, - {KEY_DBL2JOY1+3, "dblsec_joy4"}, - {KEY_DBL2JOY1+4, "dblsec_joy5"}, - {KEY_DBL2JOY1+5, "dblsec_joy6"}, - {KEY_DBL2JOY1+6, "dblsec_joy7"}, - {KEY_DBL2JOY1+7, "dblsec_joy8"}, -#if !defined (NOMOREJOYBTN_2DBL) - {KEY_DBL2JOY1+8, "dblsec_joy9"}, - {KEY_DBL2JOY1+9, "dblsec_joy10"}, - {KEY_DBL2JOY1+10, "dblsec_joy11"}, - {KEY_DBL2JOY1+11, "dblsec_joy12"}, - {KEY_DBL2JOY1+12, "dblsec_joy13"}, - {KEY_DBL2JOY1+13, "dblsec_joy14"}, - {KEY_DBL2JOY1+14, "dblsec_joy15"}, - {KEY_DBL2JOY1+15, "dblsec_joy16"}, - {KEY_DBL2JOY1+16, "dblsec_joy17"}, - {KEY_DBL2JOY1+17, "dblsec_joy18"}, - {KEY_DBL2JOY1+18, "dblsec_joy19"}, - {KEY_DBL2JOY1+19, "dblsec_joy20"}, - {KEY_DBL2JOY1+20, "dblsec_joy21"}, - {KEY_DBL2JOY1+21, "dblsec_joy22"}, - {KEY_DBL2JOY1+22, "dblsec_joy23"}, - {KEY_DBL2JOY1+23, "dblsec_joy24"}, - {KEY_DBL2JOY1+24, "dblsec_joy25"}, - {KEY_DBL2JOY1+25, "dblsec_joy26"}, - {KEY_DBL2JOY1+26, "dblsec_joy27"}, - {KEY_DBL2JOY1+27, "dblsec_joy28"}, - {KEY_DBL2JOY1+28, "dblsec_joy29"}, - {KEY_DBL2JOY1+29, "dblsec_joy30"}, - {KEY_DBL2JOY1+30, "dblsec_joy31"}, - {KEY_DBL2JOY1+31, "dblsec_joy32"}, -#endif - {KEY_DBL2HAT1+0, "dblsec_hatup"}, - {KEY_DBL2HAT1+1, "dblsec_hatdown"}, - {KEY_DBL2HAT1+2, "dblsec_hatleft"}, - {KEY_DBL2HAT1+3, "dblsec_hatright"}, - {KEY_DBL2HAT1+4, "dblsec_hatup2"}, - {KEY_DBL2HAT1+5, "dblsec_hatdown2"}, - {KEY_DBL2HAT1+6, "dblsec_hatleft2"}, - {KEY_DBL2HAT1+7, "dblsec_hatright2"}, - {KEY_DBL2HAT1+8, "dblsec_hatup3"}, - {KEY_DBL2HAT1+9, "dblsec_hatdown3"}, - {KEY_DBL2HAT1+10, "dblsec_hatleft3"}, - {KEY_DBL2HAT1+11, "dblsec_hatright3"}, - {KEY_DBL2HAT1+12, "dblsec_hatup4"}, - {KEY_DBL2HAT1+13, "dblsec_hatdown4"}, - {KEY_DBL2HAT1+14, "dblsec_hatleft4"}, - {KEY_DBL2HAT1+15, "dblsec_hatright4"}, - + {KEY_DBL2MOUSE1+7, "dblsec_mouse8"} }; +#define NUMKEYNAMES (sizeof(keynames) / sizeof(keyname_t)) + +static keyname_t displaykeynames[] = +{ + {KEY_SPACE, "Space Bar"}, + {KEY_CAPSLOCK, "Caps Lock"}, + {KEY_ENTER, "Enter"}, + {KEY_TAB, "Tab"}, + {KEY_ESCAPE, "Escape"}, + {KEY_BACKSPACE, "Backspace"}, + + {KEY_NUMLOCK, "Num Lock"}, + {KEY_SCROLLLOCK, "Scroll Lock"}, + +#ifdef _WIN32 + {KEY_LEFTWIN, "Left Windows"}, + {KEY_RIGHTWIN, "Right Windows"}, +#else + {KEY_LEFTWIN, "Left Super"}, + {KEY_RIGHTWIN, "Right Super"}, +#endif + + {KEY_MENU, "Menu"}, + + {KEY_LSHIFT, "Left Shift"}, + {KEY_RSHIFT, "Right Shift"}, + {KEY_LCTRL, "Left Ctrl"}, + {KEY_RCTRL, "Right Ctrl"}, + {KEY_LALT, "Left Alt"}, + {KEY_RALT, "Right Alt"}, + + {KEY_KEYPAD0, "Keypad 0"}, + {KEY_KEYPAD1, "Keypad 1"}, + {KEY_KEYPAD2, "Keypad 2"}, + {KEY_KEYPAD3, "Keypad 3"}, + {KEY_KEYPAD4, "Keypad 4"}, + {KEY_KEYPAD5, "Keypad 5"}, + {KEY_KEYPAD6, "Keypad 6"}, + {KEY_KEYPAD7, "Keypad 7"}, + {KEY_KEYPAD8, "Keypad 8"}, + {KEY_KEYPAD9, "Keypad 9"}, + {KEY_PLUSPAD, "Keypad +"}, + {KEY_MINUSPAD, "Keypad -"}, + {KEY_KPADSLASH, "Keypad /"}, + {KEY_KPADDEL, "Keypad ."}, + + {KEY_UPARROW, "Up Arrow"}, + {KEY_DOWNARROW, "Down Arrow"}, + {KEY_LEFTARROW, "Left Arrow"}, + {KEY_RIGHTARROW, "Right Arrow"}, + + {KEY_HOME, "Home"}, + {KEY_END, "End"}, + {KEY_PGUP, "Page Up"}, + {KEY_PGDN, "Page Down"}, + {KEY_INS, "Insert"}, + {KEY_DEL, "Delete"}, + + {KEY_F1, "F1"}, + {KEY_F2, "F2"}, + {KEY_F3, "F3"}, + {KEY_F4, "F4"}, + {KEY_F5, "F5"}, + {KEY_F6, "F6"}, + {KEY_F7, "F7"}, + {KEY_F8, "F8"}, + {KEY_F9, "F9"}, + {KEY_F10, "F10"}, + {KEY_F11, "F11"}, + {KEY_F12, "F12"}, + + {'`', "Tilde"}, + {KEY_PAUSE, "Pause/Break"}, + + {KEY_MOUSE1+0, "Left Mouse Button"}, + {KEY_MOUSE1+1, "Right Mouse Button"}, + {KEY_MOUSE1+2, "Middle Mouse Button"}, + {KEY_MOUSE1+3, "X1 Mouse Button"}, + {KEY_MOUSE1+4, "X2 Mouse Button"}, + + {KEY_2MOUSE1+0, "Sec. Mouse Left Button"}, + {KEY_2MOUSE1+1, "Sec. Mouse Right Button"}, + {KEY_2MOUSE1+2, "Sec. Mouse Middle Button"}, + {KEY_2MOUSE1+3, "Sec. Mouse X1 Button"}, + {KEY_2MOUSE1+4, "Sec. Mouse X2 Button"}, + + {KEY_MOUSEWHEELUP, "Mouse Wheel Up"}, + {KEY_MOUSEWHEELDOWN, "Mouse Wheel Down"}, + {KEY_2MOUSEWHEELUP, "Sec. Mouse Wheel Up"}, + {KEY_2MOUSEWHEELDOWN, "Sec. Mouse Wheel Down"}, + +#define DEF_GAMEPAD_NAME(btn, name) {KEY_GAMEPAD+GAMEPAD_BUTTON_##btn, name} +#define DEF_GAMEPAD_AXIS(ax, name) {KEY_AXES+GAMEPAD_AXIS_##ax, name} + + DEF_GAMEPAD_NAME(A, "A Button"), + DEF_GAMEPAD_NAME(B, "B Button"), + DEF_GAMEPAD_NAME(X, "X Button"), + DEF_GAMEPAD_NAME(Y, "Y Button"), + + DEF_GAMEPAD_NAME(BACK, "Back Button"), + DEF_GAMEPAD_NAME(GUIDE, "Guide Button"), + DEF_GAMEPAD_NAME(START, "Start Button"), + DEF_GAMEPAD_NAME(LEFTSTICK, "Left Stick Button"), + DEF_GAMEPAD_NAME(RIGHTSTICK, "Right Stick Button"), + + DEF_GAMEPAD_NAME(LEFTSHOULDER, "Left Shoulder"), + DEF_GAMEPAD_NAME(RIGHTSHOULDER, "Right Shoulder"), + + DEF_GAMEPAD_NAME(DPAD_UP, "D-Pad Up"), + DEF_GAMEPAD_NAME(DPAD_DOWN, "D-Pad Down"), + DEF_GAMEPAD_NAME(DPAD_LEFT, "D-Pad Left"), + DEF_GAMEPAD_NAME(DPAD_RIGHT, "D-Pad Right"), + + DEF_GAMEPAD_NAME(MISC1, "Gamepad Misc. 1"), + DEF_GAMEPAD_NAME(PADDLE1, "Paddle 1"), + DEF_GAMEPAD_NAME(PADDLE2, "Paddle 2"), + DEF_GAMEPAD_NAME(PADDLE3, "Paddle 3"), + DEF_GAMEPAD_NAME(PADDLE4, "Paddle 4"), + DEF_GAMEPAD_NAME(TOUCHPAD, "Touchpad"), + + {KEY_INV_AXES + GAMEPAD_AXIS_LEFTX, "Left Stick \x1C"}, + {KEY_AXES + GAMEPAD_AXIS_LEFTX, "Left Stick \x1D"}, + {KEY_INV_AXES + GAMEPAD_AXIS_LEFTY, "Left Stick \x1A"}, + {KEY_AXES + GAMEPAD_AXIS_LEFTY, "Left Stick \x1B"}, + {KEY_INV_AXES + GAMEPAD_AXIS_RIGHTX, "Right Stick \x1C"}, + {KEY_AXES + GAMEPAD_AXIS_RIGHTX, "Right Stick \x1D"}, + {KEY_INV_AXES + GAMEPAD_AXIS_RIGHTY, "Right Stick \x1A"}, + {KEY_AXES + GAMEPAD_AXIS_RIGHTY, "Right Stick \x1B"}, + + DEF_GAMEPAD_AXIS(TRIGGERLEFT, "Left Trigger"), + DEF_GAMEPAD_AXIS(TRIGGERRIGHT, "Right Trigger"), + +#undef DEF_GAMEPAD_NAME +#undef DEF_GAMEPAD_AXIS +}; + +#define NUMDISPLAYKEYNAMES (sizeof(displaykeynames) / sizeof(keyname_t)) + static const char *gamecontrolname[NUM_GAMECONTROLS] = { "nothing", // a key/button mapped to GC_NULL has no effect @@ -600,8 +1168,6 @@ static const char *gamecontrolname[NUM_GAMECONTROLS] = "custom3", }; -#define NUMKEYNAMES (sizeof (keynames)/sizeof (keyname_t)) - // // Detach any keys associated to the given game control // - pass the pointer to the gamecontrol table for the player being edited @@ -622,7 +1188,7 @@ void G_ClearAllControlKeys(void) } // -// Returns the name of a key (or virtual key for mouse and joy) +// Returns the name of a key (or virtual key for mouse and gamepad) // the input value being an keynum // const char *G_KeyNumToName(INT32 keynum) @@ -645,7 +1211,44 @@ const char *G_KeyNumToName(INT32 keynum) return keynames[j].name; // create a name for unknown keys - sprintf(keynamestr, "KEY%d", keynum); + snprintf(keynamestr, sizeof keynamestr, "KEY%d", keynum); + return keynamestr; +} + +const char *G_GetDisplayNameForKey(INT32 keynum) +{ + static char keynamestr[32]; + + UINT32 j; + + // find a description for special keys + for (j = 0; j < NUMDISPLAYKEYNAMES; j++) + if (displaykeynames[j].keynum == keynum) + return displaykeynames[j].name; + + // return a string with the ascii char if displayable + if (keynum > ' ' && keynum <= 'z' && keynum != KEY_CONSOLE) + { + snprintf(keynamestr, sizeof keynamestr, "%c Key", toupper((char)keynum)); + return keynamestr; + } + + // unnamed mouse buttons + if (keynum >= KEY_MOUSE1 && keynum <= KEY_MOUSE1+7) + { + j = (keynum - KEY_MOUSE1) + 1; + snprintf(keynamestr, sizeof keynamestr, "Mouse Button #%d", j); + return keynamestr; + } + else if (keynum >= KEY_2MOUSE1 && keynum <= KEY_2MOUSE1+7) + { + j = (keynum - KEY_2MOUSE1) + 1; + snprintf(keynamestr, sizeof keynamestr, "Sec. Mouse Button #%d", j); + return keynamestr; + } + + // create a name for unknown keys + snprintf(keynamestr, sizeof keynamestr, "Unknown Key %d", keynum); return keynamestr; } @@ -672,6 +1275,36 @@ INT32 G_KeyNameToNum(const char *keystr) return 0; } +const char *G_GamepadTypeToString(gamepadtype_e type) +{ + static const char *names[] = { + "xbox-360", + "xbox-one", + "xbox-series-xs", + "xbox-elite", + "ps3", + "ps4", + "ps5", + "switch-pro", + "switch-joy-con-grip", + "switch-joy-con-left", + "switch-joy-con-right", + "stadia", + "amazon-luna", + "steam-controller", + "virtual", + "unknown" + }; + + return names[type]; +} + +void G_InitGamepads(void) +{ + for (UINT8 i = 0; i < NUM_GAMEPADS; i++) + gamepads[i].num = i; +} + void G_DefineDefaultControls(void) { INT32 i; @@ -739,36 +1372,36 @@ void G_DefineDefaultControls(void) gamecontroldefault[i][GC_VIEWPOINTNEXT][0] = KEY_F12; // Gamepad controls -- same for both schemes - gamecontroldefault[i][GC_JUMP ][1] = KEY_JOY1+0; // A - gamecontroldefault[i][GC_SPIN ][1] = KEY_JOY1+2; // X - gamecontroldefault[i][GC_CUSTOM1 ][1] = KEY_JOY1+1; // B - gamecontroldefault[i][GC_CUSTOM2 ][1] = KEY_JOY1+3; // Y - gamecontroldefault[i][GC_CUSTOM3 ][1] = KEY_JOY1+8; // Left Stick - gamecontroldefault[i][GC_CENTERVIEW ][1] = KEY_JOY1+9; // Right Stick - gamecontroldefault[i][GC_WEAPONPREV ][1] = KEY_JOY1+4; // LB - gamecontroldefault[i][GC_WEAPONNEXT ][1] = KEY_JOY1+5; // RB - gamecontroldefault[i][GC_SCREENSHOT ][1] = KEY_JOY1+6; // Back - gamecontroldefault[i][GC_SYSTEMMENU ][0] = KEY_JOY1+7; // Start - gamecontroldefault[i][GC_CAMTOGGLE ][1] = KEY_HAT1+0; // D-Pad Up - gamecontroldefault[i][GC_VIEWPOINTNEXT][1] = KEY_HAT1+1; // D-Pad Down - gamecontroldefault[i][GC_TOSSFLAG ][1] = KEY_HAT1+2; // D-Pad Left - gamecontroldefault[i][GC_SCORES ][1] = KEY_HAT1+3; // D-Pad Right + gamecontroldefault[i][GC_JUMP ][1] = GAMEPAD_KEY(A); // A + gamecontroldefault[i][GC_SPIN ][1] = GAMEPAD_KEY(X); // X + gamecontroldefault[i][GC_CUSTOM1 ][1] = GAMEPAD_KEY(B); // B + gamecontroldefault[i][GC_CUSTOM2 ][1] = GAMEPAD_KEY(Y); // Y + gamecontroldefault[i][GC_CUSTOM3 ][1] = GAMEPAD_KEY(LEFTSTICK); // Left Stick + gamecontroldefault[i][GC_CENTERVIEW ][1] = GAMEPAD_KEY(RIGHTSTICK); // Right Stick + gamecontroldefault[i][GC_WEAPONPREV ][1] = GAMEPAD_KEY(LEFTSHOULDER); // LB + gamecontroldefault[i][GC_WEAPONNEXT ][1] = GAMEPAD_KEY(RIGHTSHOULDER); // RB + gamecontroldefault[i][GC_SCREENSHOT ][1] = GAMEPAD_KEY(BACK); // Back + gamecontroldefault[i][GC_SYSTEMMENU ][0] = GAMEPAD_KEY(START); // Start + gamecontroldefault[i][GC_CAMTOGGLE ][1] = GAMEPAD_KEY(DPAD_UP); // D-Pad Up + gamecontroldefault[i][GC_VIEWPOINTNEXT][1] = GAMEPAD_KEY(DPAD_DOWN); // D-Pad Down + gamecontroldefault[i][GC_TOSSFLAG ][1] = GAMEPAD_KEY(DPAD_LEFT); // D-Pad Left + gamecontroldefault[i][GC_SCORES ][1] = GAMEPAD_KEY(DPAD_RIGHT); // D-Pad Right - // Second player controls only have joypad defaults - gamecontrolbisdefault[i][GC_JUMP ][1] = KEY_2JOY1+0; // A - gamecontrolbisdefault[i][GC_SPIN ][1] = KEY_2JOY1+2; // X - gamecontrolbisdefault[i][GC_CUSTOM1 ][1] = KEY_2JOY1+1; // B - gamecontrolbisdefault[i][GC_CUSTOM2 ][1] = KEY_2JOY1+3; // Y - gamecontrolbisdefault[i][GC_CUSTOM3 ][1] = KEY_2JOY1+8; // Left Stick - gamecontrolbisdefault[i][GC_CENTERVIEW ][1] = KEY_2JOY1+9; // Right Stick - gamecontrolbisdefault[i][GC_WEAPONPREV ][1] = KEY_2JOY1+4; // LB - gamecontrolbisdefault[i][GC_WEAPONNEXT ][1] = KEY_2JOY1+5; // RB - gamecontrolbisdefault[i][GC_SCREENSHOT ][1] = KEY_2JOY1+6; // Back - //gamecontrolbisdefault[i][GC_SYSTEMMENU ][0] = KEY_2JOY1+7; // Start - gamecontrolbisdefault[i][GC_CAMTOGGLE ][1] = KEY_2HAT1+0; // D-Pad Up - gamecontrolbisdefault[i][GC_VIEWPOINTNEXT][1] = KEY_2HAT1+1; // D-Pad Down - gamecontrolbisdefault[i][GC_TOSSFLAG ][1] = KEY_2HAT1+2; // D-Pad Left - //gamecontrolbisdefault[i][GC_SCORES ][1] = KEY_2HAT1+3; // D-Pad Right + // Second player only has gamepad defaults + gamecontrolbisdefault[i][GC_JUMP ][1] = GAMEPAD_KEY(A); // A + gamecontrolbisdefault[i][GC_SPIN ][1] = GAMEPAD_KEY(X); // X + gamecontrolbisdefault[i][GC_CUSTOM1 ][1] = GAMEPAD_KEY(B); // B + gamecontrolbisdefault[i][GC_CUSTOM2 ][1] = GAMEPAD_KEY(Y); // Y + gamecontrolbisdefault[i][GC_CUSTOM3 ][1] = GAMEPAD_KEY(LEFTSTICK); // Left Stick + gamecontrolbisdefault[i][GC_CENTERVIEW ][1] = GAMEPAD_KEY(RIGHTSTICK); // Right Stick + gamecontrolbisdefault[i][GC_WEAPONPREV ][1] = GAMEPAD_KEY(LEFTSHOULDER); // LB + gamecontrolbisdefault[i][GC_WEAPONNEXT ][1] = GAMEPAD_KEY(RIGHTSHOULDER); // RB + gamecontrolbisdefault[i][GC_SCREENSHOT ][1] = GAMEPAD_KEY(BACK); // Back + //gamecontrolbisdefault[i][GC_SYSTEMMENU ][0] = GAMEPAD_KEY(START); // Start + gamecontrolbisdefault[i][GC_CAMTOGGLE ][1] = GAMEPAD_KEY(DPAD_UP); // D-Pad Up + gamecontrolbisdefault[i][GC_VIEWPOINTNEXT][1] = GAMEPAD_KEY(DPAD_DOWN); // D-Pad Down + gamecontrolbisdefault[i][GC_TOSSFLAG ][1] = GAMEPAD_KEY(DPAD_LEFT); // D-Pad Left + //gamecontrolbisdefault[i][GC_SCORES ][1] = GAMEPAD_KEY(DPAD_RIGHT); // D-Pad Right } } diff --git a/src/g_input.h b/src/g_input.h index 400e3fd12..fe623034a 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -8,7 +8,7 @@ // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file g_input.h -/// \brief handle mouse/keyboard/joystick inputs, +/// \brief handle mouse/keyboard/gamepad inputs, /// maps inputs to game controls (forward, spin, jump...) #ifndef __G_INPUT__ @@ -17,45 +17,178 @@ #include "d_event.h" #include "keys.h" #include "command.h" +#include "m_fixed.h" // number of total 'button' inputs, include keyboard keys, plus virtual // keys (mousebuttons and joybuttons becomes keys) #define NUMKEYS 256 +// Max gamepads that can be used by every player +#define NUM_GAMEPADS 2 + +// Max gamepads that can be detected +#define MAX_CONNECTED_GAMEPADS 4 + +// Max mouse buttons #define MOUSEBUTTONS 8 -#define JOYBUTTONS 32 // 32 buttons -#define JOYHATS 4 // 4 hats -#define JOYAXISSET 4 // 4 Sets of 2 axises + +typedef enum +{ + GAMEPAD_TYPE_UNKNOWN, + + GAMEPAD_TYPE_XBOX360, + GAMEPAD_TYPE_XBOXONE, + GAMEPAD_TYPE_XBOX_SERIES_XS, + GAMEPAD_TYPE_XBOX_ELITE, + + GAMEPAD_TYPE_PS3, + GAMEPAD_TYPE_PS4, + GAMEPAD_TYPE_PS5, + + GAMEPAD_TYPE_NINTENDO_SWITCH_PRO, + GAMEPAD_TYPE_NINTENDO_SWITCH_JOY_CON_GRIP, + GAMEPAD_TYPE_NINTENDO_SWITCH_JOY_CON_LEFT, + GAMEPAD_TYPE_NINTENDO_SWITCH_JOY_CON_RIGHT, + + GAMEPAD_TYPE_GOOGLE_STADIA, + GAMEPAD_TYPE_AMAZON_LUNA, + GAMEPAD_TYPE_STEAM_CONTROLLER, + + GAMEPAD_TYPE_VIRTUAL +} gamepadtype_e; + +boolean G_GamepadTypeIsXbox(gamepadtype_e type); +boolean G_GamepadTypeIsPlayStation(gamepadtype_e type); +boolean G_GamepadTypeIsNintendoSwitch(gamepadtype_e type); +boolean G_GamepadTypeIsJoyCon(gamepadtype_e type); + +const char *G_GamepadTypeToString(gamepadtype_e type); + +typedef enum +{ + GAMEPAD_BUTTON_A, + GAMEPAD_BUTTON_B, + GAMEPAD_BUTTON_X, + GAMEPAD_BUTTON_Y, + GAMEPAD_BUTTON_BACK, + GAMEPAD_BUTTON_GUIDE, + GAMEPAD_BUTTON_START, + GAMEPAD_BUTTON_LEFTSTICK, + GAMEPAD_BUTTON_RIGHTSTICK, + GAMEPAD_BUTTON_LEFTSHOULDER, + GAMEPAD_BUTTON_RIGHTSHOULDER, + GAMEPAD_BUTTON_DPAD_UP, + GAMEPAD_BUTTON_DPAD_DOWN, + GAMEPAD_BUTTON_DPAD_LEFT, + GAMEPAD_BUTTON_DPAD_RIGHT, + + // According to SDL, this button can be: + // the Xbox Series X|S share button + // the PS5 microphone button + // the Nintendo Switch (Pro or Joy-Con) capture button + // the Amazon Luna microphone button + GAMEPAD_BUTTON_MISC1, + + // Xbox Elite paddles + GAMEPAD_BUTTON_PADDLE1, + GAMEPAD_BUTTON_PADDLE2, + GAMEPAD_BUTTON_PADDLE3, + GAMEPAD_BUTTON_PADDLE4, + + // PS4/PS5 touchpad button + GAMEPAD_BUTTON_TOUCHPAD, + + NUM_GAMEPAD_BUTTONS +} gamepad_button_e; + +typedef enum +{ + GAMEPAD_AXIS_LEFTX, + GAMEPAD_AXIS_LEFTY, + GAMEPAD_AXIS_RIGHTX, + GAMEPAD_AXIS_RIGHTY, + GAMEPAD_AXIS_TRIGGERLEFT, + GAMEPAD_AXIS_TRIGGERRIGHT, + + NUM_GAMEPAD_AXES +} gamepad_axis_e; + +extern const char *const gamepad_button_names[NUM_GAMEPAD_BUTTONS + 1]; +extern const char *const gamepad_axis_names[NUM_GAMEPAD_AXES + 1]; + +// Haptic effects +typedef struct +{ + fixed_t large_magnitude; // Magnitude of the large motor + fixed_t small_magnitude; // Magnitude of the small motor + tic_t duration; // The total duration of the effect, in tics +} haptic_t; + +// Gamepad info for each player on the system +typedef struct +{ + // Gamepad index + UINT8 num; + + // Gamepad is connected and being used by a player + boolean connected; + + // What kind of controller this is (Xbox 360, DualShock, Joy-Con, etc.) + gamepadtype_e type; + + // Treat this gamepad's axes as if it they were buttons + boolean digital; + + struct { + boolean supported; // Gamepad can rumble + boolean active; // Rumble is active + boolean paused; // Rumble is paused + haptic_t data; // Current haptic effect status + } rumble; + + UINT8 buttons[NUM_GAMEPAD_BUTTONS]; // Current state of all buttons + INT16 axes[NUM_GAMEPAD_AXES]; // Current state of all axes +} gamepad_t; + +void G_InitGamepads(void); + +typedef enum +{ + GAMEPAD_STRING_DEFAULT, // A + GAMEPAD_STRING_MENU1, // A Button + GAMEPAD_STRING_MENU2 // the A Button +} gamepad_string_e; + +const char *G_GetGamepadButtonString(gamepadtype_e type, gamepad_button_e button, gamepad_string_e strtype); +const char *G_GetGamepadAxisString(gamepadtype_e type, gamepad_axis_e button, gamepad_string_e strtype, boolean inv); + +extern gamepad_t gamepads[NUM_GAMEPADS]; // -// mouse and joystick buttons are handled as 'virtual' keys +// mouse and gamepad buttons are handled as 'virtual' keys // typedef enum { KEY_MOUSE1 = NUMKEYS, - KEY_JOY1 = KEY_MOUSE1 + MOUSEBUTTONS, - KEY_HAT1 = KEY_JOY1 + JOYBUTTONS, + KEY_GAMEPAD = KEY_MOUSE1 + MOUSEBUTTONS, + KEY_AXES = KEY_GAMEPAD + NUM_GAMEPAD_BUTTONS, // Sure, why not. + KEY_INV_AXES = KEY_AXES + NUM_GAMEPAD_AXES, - KEY_DBLMOUSE1 =KEY_HAT1 + JOYHATS*4, // double clicks - KEY_DBLJOY1 = KEY_DBLMOUSE1 + MOUSEBUTTONS, - KEY_DBLHAT1 = KEY_DBLJOY1 + JOYBUTTONS, + KEY_DBLMOUSE1 = KEY_INV_AXES + NUM_GAMEPAD_AXES, // double clicks - KEY_2MOUSE1 = KEY_DBLHAT1 + JOYHATS*4, - KEY_2JOY1 = KEY_2MOUSE1 + MOUSEBUTTONS, - KEY_2HAT1 = KEY_2JOY1 + JOYBUTTONS, + KEY_2MOUSE1 = KEY_DBLMOUSE1 + MOUSEBUTTONS, + KEY_DBL2MOUSE1 = KEY_2MOUSE1 + MOUSEBUTTONS, - KEY_DBL2MOUSE1 = KEY_2HAT1 + JOYHATS*4, - KEY_DBL2JOY1 = KEY_DBL2MOUSE1 + MOUSEBUTTONS, - KEY_DBL2HAT1 = KEY_DBL2JOY1 + JOYBUTTONS, + KEY_MOUSEWHEELUP = KEY_DBL2MOUSE1 + MOUSEBUTTONS, + KEY_MOUSEWHEELDOWN, + KEY_2MOUSEWHEELUP, + KEY_2MOUSEWHEELDOWN, - KEY_MOUSEWHEELUP = KEY_DBL2HAT1 + JOYHATS*4, - KEY_MOUSEWHEELDOWN = KEY_MOUSEWHEELUP + 1, - KEY_2MOUSEWHEELUP = KEY_MOUSEWHEELDOWN + 1, - KEY_2MOUSEWHEELDOWN = KEY_2MOUSEWHEELUP + 1, - - NUMINPUTS = KEY_2MOUSEWHEELDOWN + 1, + NUMINPUTS } key_input_e; +#define GAMEPAD_KEY(key) (KEY_GAMEPAD + GAMEPAD_BUTTON_##key) + typedef enum { GC_NULL = 0, // a key/button mapped to GC_NULL has no effect @@ -141,19 +274,22 @@ typedef struct extern mouse_t mouse; extern mouse_t mouse2; -extern INT32 joyxmove[JOYAXISSET], joyymove[JOYAXISSET], joy2xmove[JOYAXISSET], joy2ymove[JOYAXISSET]; - // current state of the keys: true if pushed extern UINT8 gamekeydown[NUMINPUTS]; // two key codes (or virtual key) per game control extern INT32 gamecontrol[NUM_GAMECONTROLS][2]; extern INT32 gamecontrolbis[NUM_GAMECONTROLS][2]; // secondary splitscreen player -extern INT32 gamecontroldefault[num_gamecontrolschemes][NUM_GAMECONTROLS][2]; // default control storage, use 0 (gcs_custom) for memory retention + +// default control storage, use 0 (gcs_custom) for memory retention +extern INT32 gamecontroldefault[num_gamecontrolschemes][NUM_GAMECONTROLS][2]; extern INT32 gamecontrolbisdefault[num_gamecontrolschemes][NUM_GAMECONTROLS][2]; -#define PLAYER1INPUTDOWN(gc) (gamekeydown[gamecontrol[gc][0]] || gamekeydown[gamecontrol[gc][1]]) -#define PLAYER2INPUTDOWN(gc) (gamekeydown[gamecontrolbis[gc][0]] || gamekeydown[gamecontrolbis[gc][1]]) -#define PLAYERINPUTDOWN(p, gc) ((p) == 2 ? PLAYER2INPUTDOWN(gc) : PLAYER1INPUTDOWN(gc)) + +boolean G_PlayerInputDown(UINT8 which, gamecontrols_e gc); +boolean G_CheckDigitalPlayerInput(UINT8 which, gamecontrols_e gc); + +SINT8 G_PlayerInputIsAnalog(UINT8 which, gamecontrols_e gc, UINT8 settings); +INT16 G_GetAnalogPlayerInput(UINT8 which, gamecontrols_e gc, UINT8 settings); #define num_gcl_tutorial_check 6 #define num_gcl_tutorial_used 8 @@ -181,13 +317,37 @@ extern const INT32 gcl_jump_spin[num_gcl_jump_spin]; // remaps the input event to a game control. void G_MapEventsToControls(event_t *ev); +boolean G_RumbleSupported(UINT8 which); +boolean G_RumbleGamepad(UINT8 which, fixed_t large_magnitude, fixed_t small_magnitude, tic_t duration); +void G_StopGamepadRumble(UINT8 which); + +fixed_t G_GetLargeMotorFreq(UINT8 which); +fixed_t G_GetSmallMotorFreq(UINT8 which); +boolean G_GetGamepadRumblePaused(UINT8 which); +boolean G_SetLargeMotorFreq(UINT8 which, fixed_t freq); +boolean G_SetSmallMotorFreq(UINT8 which, fixed_t freq); +void G_SetGamepadRumblePaused(UINT8 which, boolean pause); + +INT16 G_GamepadAxisEventValue(UINT8 which, INT16 value); +INT16 G_GetGamepadAxisValue(UINT8 which, gamepad_axis_e axis); +fixed_t G_GetAdjustedGamepadAxis(UINT8 which, gamepad_axis_e axis, boolean applyDeadzone); + +UINT16 G_GetGamepadDeadZone(UINT8 which); +UINT16 G_GetGamepadDigitalDeadZone(UINT8 which); +INT32 G_BasicDeadZoneCalculation(INT32 magnitude, const UINT16 jdeadzone); + +INT32 G_RemapGamepadEvent(event_t *event, INT32 *type); + // returns the name of a key const char *G_KeyNumToName(INT32 keynum); INT32 G_KeyNameToNum(const char *keystr); +const char *G_GetDisplayNameForKey(INT32 keynum); + // detach any keys associated to the given game control void G_ClearControlKeys(INT32 (*setupcontrols)[2], INT32 control); void G_ClearAllControlKeys(void); + void Command_Setcontrol_f(void); void Command_Setcontrol2_f(void); void G_DefineDefaultControls(void); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index c037abcd7..805aa694f 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -877,7 +877,7 @@ void HU_Ticker(void) hu_tick++; hu_tick &= 7; // currently only to blink chat input cursor - if (PLAYER1INPUTDOWN(GC_SCORES)) + if (G_PlayerInputDown(0, GC_SCORES)) hu_showscores = !chat_on; else hu_showscores = false; @@ -1052,26 +1052,6 @@ boolean HU_Responder(event_t *ev) // only KeyDown events now... - /*// Shoot, to prevent P1 chatting from ruining the game for everyone else, it's either: - // A. completely disallow opening chat entirely in online splitscreen - // or B. iterate through all controls to make sure it's bound to player 1 before eating - // You can see which one I chose. - // (Unless if you're sharing a keyboard, since you probably establish when you start chatting that you have dibs on it...) - // (Ahhh, the good ol days when I was a kid who couldn't afford an extra USB controller...) - - if (ev->key >= KEY_MOUSE1) - { - INT32 i; - for (i = 0; i < NUM_GAMECONTROLS; i++) - { - if (gamecontrol[i][0] == ev->key || gamecontrol[i][1] == ev->key) - break; - } - - if (i == NUM_GAMECONTROLS) - return false; - }*/ //We don't actually care about that unless we get splitscreen netgames. :V - #ifndef NONET c = (INT32)ev->key; diff --git a/src/i_gamepad.h b/src/i_gamepad.h new file mode 100644 index 000000000..730109b54 --- /dev/null +++ b/src/i_gamepad.h @@ -0,0 +1,58 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file i_gamepad.h +/// \brief Gamepads + +#ifndef __I_GAMEPAD_H__ +#define __I_GAMEPAD_H__ + +#include "g_input.h" +#include "p_haptic.h" + +// So m_menu knows whether to store cv_usegamepad value or string +#define GAMEPAD_HOTPLUG + +// Value range for axes +#define JOYAXISRANGE INT16_MAX +#define OLDJOYAXISRANGE 1023 + +// Starts all gamepads +void I_InitGamepads(void); + +// Returns the number of gamepads on the system +INT32 I_NumGamepads(void); + +// Changes a gamepad's device +void I_ChangeGamepad(UINT8 which); + +// Toggles a gamepad's digital axis setting +void I_SetGamepadDigital(UINT8 which, boolean enable); + +// Shuts down all gamepads +void I_ShutdownGamepads(void); + +// Returns the name of a gamepad from its index +const char *I_GetGamepadName(INT32 joyindex); + +// Gamepad rumble interface +boolean I_RumbleSupported(void); +boolean I_RumbleGamepad(UINT8 which, const haptic_t *effect); + +boolean I_GetGamepadRumblePaused(UINT8 which); + +boolean I_SetGamepadLargeMotorFreq(UINT8 which, fixed_t freq); +boolean I_SetGamepadSmallMotorFreq(UINT8 which, fixed_t freq); +void I_SetGamepadRumblePaused(UINT8 which, boolean pause); + +boolean I_GetGamepadRumbleSupported(UINT8 which); + +void I_StopGamepadRumble(UINT8 which); + +#endif // __I_GAMEPAD_H__ diff --git a/src/i_joy.h b/src/i_joy.h deleted file mode 100644 index 27584cea6..000000000 --- a/src/i_joy.h +++ /dev/null @@ -1,58 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// This program is free software distributed under the -// terms of the GNU General Public License, version 2. -// See the 'LICENSE' file for more details. -//----------------------------------------------------------------------------- -/// \file i_joy.h -/// \brief share joystick information with game control code - -#ifndef __I_JOY_H__ -#define __I_JOY_H__ - -#include "g_input.h" - -/*! - \brief -JOYAXISRANGE to +JOYAXISRANGE for each axis - - (1024-1) so we can do a right shift instead of division - (doesnt matter anyway, just give enough precision) - a gamepad will return -1, 0, or 1 in the event data - an analog type joystick will return a value - from -JOYAXISRANGE to +JOYAXISRANGE for each axis -*/ - -#define JOYAXISRANGE 1023 - -// detect a bug if we increase JOYBUTTONS above DIJOYSTATE's number of buttons -#if (JOYBUTTONS > 64) -"JOYBUTTONS is greater than INT64 bits can hold" -#endif - -/** \brief The struct JoyType_s - - share some joystick information (maybe 2 for splitscreen), to the game input code, - actually, we need to know if it is a gamepad or analog controls -*/ - -struct JoyType_s -{ - /*! if true, we MUST Poll() to get new joystick data, - that is: we NEED the DIRECTINPUTDEVICE2 ! (watchout NT compatibility) */ - INT32 bJoyNeedPoll; - /*! this joystick is a gamepad, read: digital axes - if FALSE, interpret the joystick event data as JOYAXISRANGE (see above) */ - INT32 bGamepadStyle; - -}; -typedef struct JoyType_s JoyType_t; -/** \brief Joystick info - for palyer 1 and 2's joystick/gamepad -*/ - -extern JoyType_t Joystick, Joystick2; - -#endif // __I_JOY_H__ diff --git a/src/i_system.h b/src/i_system.h index 7153aa735..f3ca5aac5 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -101,90 +101,6 @@ ticcmd_t *I_BaseTiccmd2(void); */ void I_Quit(void) FUNCNORETURN; -typedef enum -{ - EvilForce = -1, - //Constant - ConstantForce = 0, - //Ramp - RampForce, - //Periodics - SquareForce, - SineForce, - TriangleForce, - SawtoothUpForce, - SawtoothDownForce, - //MAX - NumberofForces, -} FFType; - -typedef struct JoyFF_s -{ - INT32 ForceX; ///< The X of the Force's Vel - INT32 ForceY; ///< The Y of the Force's Vel - //All - UINT32 Duration; ///< The total duration of the effect, in microseconds - INT32 Gain; //< /The gain to be applied to the effect, in the range from 0 through 10,000. - //All, CONSTANTFORCE -10,000 to 10,000 - INT32 Magnitude; ///< Magnitude of the effect, in the range from 0 through 10,000. - //RAMPFORCE - INT32 Start; ///< Magnitude at the start of the effect, in the range from -10,000 through 10,000. - INT32 End; ///< Magnitude at the end of the effect, in the range from -10,000 through 10,000. - //PERIODIC - INT32 Offset; ///< Offset of the effect. - UINT32 Phase; ///< Position in the cycle of the periodic effect at which playback begins, in the range from 0 through 35,999 - UINT32 Period; ///< Period of the effect, in microseconds. -} JoyFF_t; - -/** \brief Forcefeedback for the first joystick - - \param Type what kind of Effect - \param Effect Effect Info - - \return void -*/ - -void I_Tactile(FFType Type, const JoyFF_t *Effect); - -/** \brief Forcefeedback for the second joystick - - \param Type what kind of Effect - \param Effect Effect Info - - \return void -*/ -void I_Tactile2(FFType Type, const JoyFF_t *Effect); - -/** \brief to set up the first joystick scale -*/ -void I_JoyScale(void); - -/** \brief to set up the second joystick scale -*/ -void I_JoyScale2(void); - -// Called by D_SRB2Main. - -/** \brief to startup the first joystick -*/ -void I_InitJoystick(void); - -/** \brief to startup the second joystick -*/ -void I_InitJoystick2(void); - -/** \brief return the number of joystick on the system -*/ -INT32 I_NumJoys(void); - -/** \brief The *I_GetJoyName function - - \param joyindex which joystick - - \return joystick name -*/ -const char *I_GetJoyName(INT32 joyindex); - #ifndef NOMUMBLE #include "p_mobj.h" // mobj_t #include "s_sound.h" // listener_t @@ -293,15 +209,7 @@ const CPUInfoFlags *I_CPUInfo(void); */ const char *I_LocateWad(void); -/** \brief First Joystick's events -*/ -void I_GetJoystickEvents(void); - -/** \brief Second Joystick's events -*/ -void I_GetJoystick2Events(void); - -/** \brief Mouses events +/** \brief Mice events */ void I_GetMouseEvents(void); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 29adb478a..c94e9e91e 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -15,6 +15,7 @@ #include "p_local.h" #include "p_setup.h" // So we can have P_SetupLevelSky #include "p_slopes.h" // P_GetSlopeZAt +#include "p_haptic.h" #include "z_zone.h" #include "r_main.h" #include "r_draw.h" @@ -220,6 +221,7 @@ static const struct { {META_LUABANKS, "luabanks[]"}, {META_KEYEVENT, "keyevent_t"}, + {META_GAMEPAD, "gamepad_t"}, {META_MOUSE, "mouse_t"}, {NULL, NULL} }; @@ -1732,6 +1734,78 @@ static int lib_pPlayerShouldUseSpinHeight(lua_State *L) return 1; } +// P_HAPTIC +/////////// +#define GET_OPTIONAL_PLAYER(arg) \ + player_t *player = NULL; \ + if (!lua_isnoneornil(L, arg)) { \ + player = *((player_t **)luaL_checkudata(L, arg, META_PLAYER)); \ + if (!player) \ + return LUA_ErrInvalid(L, "player_t"); \ + } + +static int lib_pDoRumble(lua_State *L) +{ + GET_OPTIONAL_PLAYER(1); + fixed_t large_magnitude = luaL_checkfixed(L, 2); + fixed_t small_magnitude = luaL_optfixed(L, 3, large_magnitude); + tic_t duration = luaL_optinteger(L, 4, 0); + +#define CHECK_MAGNITUDE(which) \ + if (which##_magnitude < 0 || which##_magnitude > FRACUNIT) \ + return luaL_error(L, va(#which " motor frequency %f out of range (minimum is 0.0, maximum is 1.0)", \ + FixedToFloat(which##_magnitude))) + + CHECK_MAGNITUDE(large); + CHECK_MAGNITUDE(small); + +#undef CHECK_MAGNITUDE + + lua_pushboolean(L, P_DoRumble(player, large_magnitude, small_magnitude, duration)); + return 1; +} + +static int lib_pPauseRumble(lua_State *L) +{ + GET_OPTIONAL_PLAYER(1); + P_PauseRumble(player); + return 0; +} + +static int lib_pUnpauseRumble(lua_State *L) +{ + GET_OPTIONAL_PLAYER(1); + P_UnpauseRumble(player); + return 0; +} + +static int lib_pIsRumbleEnabled(lua_State *L) +{ + GET_OPTIONAL_PLAYER(1); + if (player && P_IsLocalPlayer(player)) + lua_pushboolean(L, P_IsRumbleEnabled(player)); + else + lua_pushnil(L); + return 1; +} + +static int lib_pIsRumblePaused(lua_State *L) +{ + GET_OPTIONAL_PLAYER(1); + if (player && P_IsLocalPlayer(player)) + lua_pushboolean(L, P_IsRumblePaused(player)); + else + lua_pushnil(L); + return 1; +} + +static int lib_pStopRumble(lua_State *L) +{ + GET_OPTIONAL_PLAYER(1); + P_StopRumble(player); + return 0; +} + // P_MAP /////////// @@ -3368,7 +3442,6 @@ static int lib_sResumeMusic(lua_State *L) // G_GAME //////////// -// Copypasted from lib_cvRegisterVar :] static int lib_gAddGametype(lua_State *L) { const char *k; @@ -4090,6 +4163,14 @@ static luaL_Reg lib[] = { {"P_PlayerCanEnterSpinGaps",lib_pPlayerCanEnterSpinGaps}, {"P_PlayerShouldUseSpinHeight",lib_pPlayerShouldUseSpinHeight}, + // p_haptic + {"P_DoRumble",lib_pDoRumble}, + {"P_PauseRumble",lib_pPauseRumble}, + {"P_UnpauseRumble",lib_pUnpauseRumble}, + {"P_IsRumbleEnabled",lib_pIsRumbleEnabled}, + {"P_IsRumblePaused",lib_pIsRumblePaused}, + {"P_StopRumble",lib_pStopRumble}, + // p_map {"P_CheckPosition",lib_pCheckPosition}, {"P_TryMove",lib_pTryMove}, diff --git a/src/lua_hook.h b/src/lua_hook.h index 5a14294c3..37a38e056 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -74,6 +74,10 @@ automatically. X (PlayerCanEnterSpinGaps),\ X (KeyDown),\ X (KeyUp),\ + X (GamepadButtonDown),\ + X (GamepadButtonUp),\ + X (GamepadAdded),\ + X (GamepadRemoved),\ #define STRING_HOOK_LIST(X) \ X (BotAI),/* B_BuildTailsTiccmd by skin name */\ @@ -125,6 +129,8 @@ void LUA_HookBool(boolean value, int hook); int LUA_HookPlayer(player_t *, int hook); int LUA_HookTiccmd(player_t *, ticcmd_t *, int hook); int LUA_HookKey(event_t *event, int hook); // Hooks for key events +int LUA_HookGamepadButton(event_t *event, int hook); +void LUA_HookGamepadEvent(UINT8 which, int hook); void LUA_HookThinkFrame(void); int LUA_HookMobjLineCollide(mobj_t *, line_t *); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 0b24b7b53..6506d3dc6 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -14,6 +14,7 @@ #include "doomstat.h" #include "p_mobj.h" #include "g_game.h" +#include "g_input.h" #include "r_skins.h" #include "b_bot.h" #include "z_zone.h" @@ -641,6 +642,28 @@ int LUA_HookKey(event_t *event, int hook_type) return hook.status; } +int LUA_HookGamepadButton(event_t *event, int hook_type) +{ + Hook_State hook; + if (prepare_hook(&hook, false, hook_type)) + { + LUA_PushUserdata(gL, &gamepads[event->which], META_GAMEPAD); + lua_pushstring(gL, gamepad_button_names[event->key]); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +void LUA_HookGamepadEvent(UINT8 which, int hook_type) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, hook_type)) + { + LUA_PushUserdata(gL, &gamepads[which], META_GAMEPAD); + call_hooks(&hook, 0, res_none); + } +} + void LUA_HookHUD(int hook_type, huddrawlist_h list) { const hook_t * map = &hudHookIds[hook_type]; diff --git a/src/lua_inputlib.c b/src/lua_inputlib.c index 1710b0355..e4962a0fb 100644 --- a/src/lua_inputlib.c +++ b/src/lua_inputlib.c @@ -15,6 +15,7 @@ #include "g_game.h" #include "hu_stuff.h" #include "i_system.h" +#include "i_gamepad.h" #include "lua_script.h" #include "lua_libs.h" @@ -30,7 +31,7 @@ static int lib_gameControlDown(lua_State *L) int i = luaL_checkinteger(L, 1); if (i < 0 || i >= NUM_GAMECONTROLS) return luaL_error(L, "GC_* constant %d out of range (0 - %d)", i, NUM_GAMECONTROLS-1); - lua_pushinteger(L, PLAYER1INPUTDOWN(i)); + lua_pushinteger(L, G_PlayerInputDown(0, i)); return 1; } @@ -39,7 +40,7 @@ static int lib_gameControl2Down(lua_State *L) int i = luaL_checkinteger(L, 1); if (i < 0 || i >= NUM_GAMECONTROLS) return luaL_error(L, "GC_* constant %d out of range (0 - %d)", i, NUM_GAMECONTROLS-1); - lua_pushinteger(L, PLAYER2INPUTDOWN(i)); + lua_pushinteger(L, G_PlayerInputDown(1, i)); return 1; } @@ -66,14 +67,14 @@ static int lib_gameControl2ToKeyNum(lua_State *L) static int lib_joyAxis(lua_State *L) { int i = luaL_checkinteger(L, 1); - lua_pushinteger(L, JoyAxis(i)); + lua_pushinteger(L, G_JoyAxis(0, i) / 32); return 1; } static int lib_joy2Axis(lua_State *L) { int i = luaL_checkinteger(L, 1); - lua_pushinteger(L, Joy2Axis(i)); + lua_pushinteger(L, G_JoyAxis(1, i) / 32); return 1; } @@ -128,6 +129,21 @@ static int lib_getCursorPosition(lua_State *L) return 2; } +static int lib_getPlayerGamepad(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + + INT16 which = G_GetGamepadForPlayer(player); + if (which >= 0) + LUA_PushUserdata(L, &gamepads[which], META_GAMEPAD); + else + lua_pushnil(L); + + return 1; +} + static luaL_Reg lib[] = { {"gameControlDown", lib_gameControlDown}, {"gameControl2Down", lib_gameControl2Down}, @@ -142,6 +158,7 @@ static luaL_Reg lib[] = { {"getMouseGrab", lib_getMouseGrab}, {"setMouseGrab", lib_setMouseGrab}, {"getCursorPosition", lib_getCursorPosition}, + {"getPlayerGamepad", lib_getPlayerGamepad}, {NULL, NULL} }; @@ -197,6 +214,341 @@ static int keyevent_get(lua_State *L) return 1; } +///////////// +// GAMEPAD // +///////////// + +enum gamepad_leftright_e { + gamepad_opt_left, + gamepad_opt_right +}; + +static const char *const gamepad_leftright_opt[] = { + "left", + "right", + NULL}; + +// Buttons +static int gamepad_isButtonDown(lua_State *L) +{ + gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); + gamepad_button_e button = luaL_checkoption(L, 2, NULL, gamepad_button_names); + lua_pushboolean(L, gamepad->buttons[button] == 1); + return 1; +} + +// Axes +static int gamepad_getAxis(lua_State *L) +{ + gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); + gamepad_axis_e axis = luaL_checkoption(L, 2, NULL, gamepad_axis_names); + boolean applyDeadzone = luaL_opt(L, luaL_checkboolean, 3, true); + lua_pushfixed(L, G_GetAdjustedGamepadAxis(gamepad->num, axis, applyDeadzone)); + return 1; +} + +// Sticks +static int gamepad_getStick(lua_State *L) +{ + gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); + enum gamepad_leftright_e stick = luaL_checkoption(L, 2, NULL, gamepad_leftright_opt); + boolean applyDeadzone = luaL_opt(L, luaL_checkboolean, 3, true); + + switch (stick) + { + case gamepad_opt_left: + lua_pushfixed(L, G_GetAdjustedGamepadAxis(gamepad->num, GAMEPAD_AXIS_LEFTX, applyDeadzone)); + lua_pushfixed(L, G_GetAdjustedGamepadAxis(gamepad->num, GAMEPAD_AXIS_LEFTY, applyDeadzone)); + break; + case gamepad_opt_right: + lua_pushfixed(L, G_GetAdjustedGamepadAxis(gamepad->num, GAMEPAD_AXIS_RIGHTX, applyDeadzone)); + lua_pushfixed(L, G_GetAdjustedGamepadAxis(gamepad->num, GAMEPAD_AXIS_RIGHTY, applyDeadzone)); + break; + } + + return 2; +} + +// Triggers +static int gamepad_getTrigger(lua_State *L) +{ + gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); + enum gamepad_leftright_e stick = luaL_checkoption(L, 2, NULL, gamepad_leftright_opt); + boolean applyDeadzone = luaL_opt(L, luaL_checkboolean, 3, true); + gamepad_axis_e axis = 0; + + switch (stick) + { + case gamepad_opt_left: + axis = GAMEPAD_AXIS_TRIGGERLEFT; + break; + case gamepad_opt_right: + axis = GAMEPAD_AXIS_TRIGGERRIGHT; + break; + } + + lua_pushfixed(L, G_GetAdjustedGamepadAxis(gamepad->num, axis, applyDeadzone)); + return 1; +} + +// Button and axis names +static int gamepad_getButtonName(lua_State *L) +{ + gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); + gamepad_button_e button = luaL_checkoption(L, 2, NULL, gamepad_button_names); + lua_pushstring(L, G_GetGamepadButtonString(gamepad->type, button, GAMEPAD_STRING_DEFAULT)); + return 1; +} + +static int gamepad_getAxisName(lua_State *L) +{ + gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); + gamepad_axis_e axis = luaL_checkoption(L, 2, NULL, gamepad_axis_names); + lua_pushstring(L, G_GetGamepadAxisString(gamepad->type, axis, GAMEPAD_STRING_DEFAULT, false)); + return 1; +} + +static int gamepad_getTriggerName(lua_State *L) +{ + gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); + enum gamepad_leftright_e stick = luaL_checkoption(L, 2, NULL, gamepad_leftright_opt); + gamepad_axis_e axis = 0; + + switch (stick) + { + case gamepad_opt_left: + axis = GAMEPAD_AXIS_TRIGGERLEFT; + break; + case gamepad_opt_right: + axis = GAMEPAD_AXIS_TRIGGERRIGHT; + break; + } + + lua_pushstring(L, G_GetGamepadAxisString(gamepad->type, axis, GAMEPAD_STRING_DEFAULT, false)); + return 1; +} + +// Rumble +static int gamepad_doRumble(lua_State *L) +{ + gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); + fixed_t large_magnitude = luaL_checkfixed(L, 2); + fixed_t small_magnitude = luaL_optfixed(L, 3, large_magnitude); + tic_t duration = luaL_optinteger(L, 4, 0); + +#define CHECK_MAGNITUDE(which) \ + if (which##_magnitude < 0 || which##_magnitude > FRACUNIT) \ + return luaL_error(L, va(#which " motor frequency %f out of range (minimum is 0.0, maximum is 1.0)", \ + FixedToFloat(which##_magnitude))) + + CHECK_MAGNITUDE(large); + CHECK_MAGNITUDE(small); + +#undef CHECK_MAGNITUDE + + lua_pushboolean(L, G_RumbleGamepad(gamepad->num, large_magnitude, small_magnitude, duration)); + return 1; +} + +static int gamepad_stopRumble(lua_State *L) +{ + gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); + G_StopGamepadRumble(gamepad->num); + return 0; +} + +// Accessing gamepad userdata +enum gamepad_opt_e { + gamepad_opt_connected, + gamepad_opt_type, + gamepad_opt_isXbox, + gamepad_opt_isPlayStation, + gamepad_opt_isNintendoSwitch, + gamepad_opt_isJoyCon, + gamepad_opt_hasRumble, + gamepad_opt_isRumbling, + gamepad_opt_isRumblePaused, + gamepad_opt_largeMotorFrequency, + gamepad_opt_smallMotorFrequency, + gamepad_opt_isButtonDown, + gamepad_opt_getAxis, + gamepad_opt_getStick, + gamepad_opt_getTrigger, + gamepad_opt_getButtonName, + gamepad_opt_getAxisName, + gamepad_opt_getTriggerName, + gamepad_opt_rumble, + gamepad_opt_stopRumble +}; + +static const char *const gamepad_opt[] = { + "connected", + "type", + "isXbox", + "isPlayStation", + "isNintendoSwitch", + "isJoyCon", + "hasRumble", + "isRumbling", + "isRumblePaused", + "largeMotorFrequency", + "smallMotorFrequency", + "isButtonDown", + "getAxis", + "getStick", + "getTrigger", + "getButtonName", + "getAxisName", + "getTriggerName", + "rumble", + "stopRumble", + NULL}; + +static int (*gamepad_fn_list[9])(lua_State *L) = { + gamepad_isButtonDown, + gamepad_getAxis, + gamepad_getStick, + gamepad_getTrigger, + gamepad_getButtonName, + gamepad_getAxisName, + gamepad_getTriggerName, + gamepad_doRumble, + gamepad_stopRumble +}; + +static int gamepad_get(lua_State *L) +{ + gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); + enum gamepad_opt_e field = luaL_checkoption(L, 2, NULL, gamepad_opt); + + switch (field) + { + case gamepad_opt_connected: + lua_pushboolean(L, gamepad->connected); + break; + case gamepad_opt_type: + lua_pushstring(L, G_GamepadTypeToString(gamepad->type)); + break; + case gamepad_opt_isXbox: + lua_pushboolean(L, G_GamepadTypeIsXbox(gamepad->type)); + break; + case gamepad_opt_isPlayStation: + lua_pushboolean(L, G_GamepadTypeIsPlayStation(gamepad->type)); + break; + case gamepad_opt_isNintendoSwitch: + lua_pushboolean(L, G_GamepadTypeIsNintendoSwitch(gamepad->type)); + break; + case gamepad_opt_isJoyCon: + // No, this does not include the grip. + lua_pushboolean(L, G_GamepadTypeIsJoyCon(gamepad->type)); + break; + case gamepad_opt_hasRumble: + lua_pushboolean(L, G_RumbleSupported(gamepad->num)); + break; + case gamepad_opt_isRumbling: + lua_pushboolean(L, gamepad->rumble.active); + break; + case gamepad_opt_isRumblePaused: + lua_pushboolean(L, G_GetGamepadRumblePaused(gamepad->num)); + break; + case gamepad_opt_largeMotorFrequency: + lua_pushfixed(L, G_GetLargeMotorFreq(gamepad->num)); + break; + case gamepad_opt_smallMotorFrequency: + lua_pushfixed(L, G_GetSmallMotorFreq(gamepad->num)); + break; + case gamepad_opt_isButtonDown: + case gamepad_opt_getAxis: + case gamepad_opt_getStick: + case gamepad_opt_getTrigger: + case gamepad_opt_getButtonName: + case gamepad_opt_getAxisName: + case gamepad_opt_getTriggerName: + case gamepad_opt_rumble: + case gamepad_opt_stopRumble: + lua_pushcfunction(L, gamepad_fn_list[field - gamepad_opt_isButtonDown]); + break; + } + return 1; +} + +static int gamepad_set(lua_State *L) +{ + gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); + enum gamepad_opt_e field = luaL_checkoption(L, 2, NULL, gamepad_opt); + + switch (field) + { + case gamepad_opt_isRumblePaused: + G_SetGamepadRumblePaused(gamepad->num, luaL_checkboolean(L, 3)); + break; + case gamepad_opt_largeMotorFrequency: + G_SetLargeMotorFreq(gamepad->num, luaL_checkfixed(L, 3)); + break; + case gamepad_opt_smallMotorFrequency: + G_SetSmallMotorFreq(gamepad->num, luaL_checkfixed(L, 3)); + break; + default: + return luaL_error(L, LUA_QL("gamepad") " field " LUA_QS " should not be set directly.", gamepad_opt[field]); + } + return 1; +} + +static int gamepad_num(lua_State *L) +{ + gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); + lua_pushinteger(L, gamepad->num + 1); + return 1; +} + +static int lib_iterateGamepads(lua_State *L) +{ + INT32 i = -1; + if (lua_gettop(L) < 2) + { + lua_pushcfunction(L, lib_iterateGamepads); + return 1; + } + lua_settop(L, 2); + lua_remove(L, 1); // State is unused + if (!lua_isnil(L, 1)) + i = (INT32)(*((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)) - gamepads); + for (i++; i < NUM_GAMEPADS; i++) + { + if (!gamepads[i].connected) + continue; + LUA_PushUserdata(L, &gamepads[i], META_GAMEPAD); + return 1; + } + return 0; +} + +static int lib_getGamepad(lua_State *L) +{ + if (lua_type(L, 2) == LUA_TNUMBER) + { + lua_Integer i = luaL_checkinteger(L, 2); + if (i < 1 || i > NUM_GAMEPADS) + return luaL_error(L, "gamepads[] index %d out of range (1 - %d)", i, NUM_GAMEPADS); + LUA_PushUserdata(L, &gamepads[i - 1], META_GAMEPAD); + return 1; + } + + if (fastcmp(luaL_checkstring(L, 2), "iterate")) + { + lua_pushcfunction(L, lib_iterateGamepads); + return 1; + } + + return 0; +} + +static int lib_lenGamepad(lua_State *L) +{ + lua_pushinteger(L, NUM_GAMEPADS); + return 1; +} + /////////// // MOUSE // /////////// @@ -257,6 +609,27 @@ int LUA_InputLib(lua_State *L) lua_setfield(L, -2, "__index"); lua_pop(L, 1); + luaL_newmetatable(L, META_GAMEPAD); + lua_pushcfunction(L, gamepad_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, gamepad_set); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, gamepad_num); + lua_setfield(L, -2, "__len"); + lua_pop(L, 1); + + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, lib_getGamepad); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_lenGamepad); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "gamepads"); + luaL_newmetatable(L, META_MOUSE); lua_pushcfunction(L, mouse_get); lua_setfield(L, -2, "__index"); diff --git a/src/lua_libs.h b/src/lua_libs.h index b4a891edb..68e976234 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -91,6 +91,7 @@ extern boolean mousegrabbedbylua; #define META_LUABANKS "LUABANKS[]*" #define META_KEYEVENT "KEYEVENT_T*" +#define META_GAMEPAD "GAMEPAD_T*" #define META_MOUSE "MOUSE_T*" boolean luaL_checkboolean(lua_State *L, int narg); diff --git a/src/lua_script.c b/src/lua_script.c index f166fb4e6..8d8fb295c 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -949,6 +949,7 @@ enum ARCH_MAPHEADER, ARCH_SKINCOLOR, ARCH_MOUSE, + ARCH_GAMEPAD, ARCH_TEND=0xFF, }; @@ -976,6 +977,7 @@ static const struct { {META_SLOPE, ARCH_SLOPE}, {META_MAPHEADER, ARCH_MAPHEADER}, {META_SKINCOLOR, ARCH_SKINCOLOR}, + {META_GAMEPAD, ARCH_GAMEPAD}, {META_MOUSE, ARCH_MOUSE}, {NULL, ARCH_NULL} }; @@ -1291,6 +1293,13 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex) WRITEUINT16(save_p, info - skincolors); break; } + case ARCH_GAMEPAD: + { + gamepad_t *gamepad = *((gamepad_t **)lua_touserdata(gL, myindex)); + WRITEUINT8(save_p, ARCH_GAMEPAD); + WRITEUINT8(save_p, gamepad->num); + break; + } case ARCH_MOUSE: { mouse_t *m = *((mouse_t **)lua_touserdata(gL, myindex)); @@ -1541,6 +1550,15 @@ static UINT8 UnArchiveValue(int TABLESINDEX) case ARCH_SKINCOLOR: LUA_PushUserdata(gL, &skincolors[READUINT16(save_p)], META_SKINCOLOR); break; + case ARCH_GAMEPAD: + { + UINT8 which = READUINT8(save_p); + if (which < NUM_GAMEPADS) + LUA_PushUserdata(gL, &gamepads[which], META_GAMEPAD); + else // Wait, what? + lua_pushnil(gL); + break; + } case ARCH_MOUSE: LUA_PushUserdata(gL, READUINT16(save_p) == 1 ? &mouse : &mouse2, META_MOUSE); break; diff --git a/src/lua_script.h b/src/lua_script.h index e586b04a8..1c98a32f3 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -30,11 +30,13 @@ // TODO add some distinction between fixed numbers and integer numbers // for at least the purpose of printing and maybe math. #define luaL_checkfixed(L, i) luaL_checkinteger(L, i) +#define luaL_optfixed(L, i, o) luaL_optinteger(L, i, o) #define lua_pushfixed(L, f) lua_pushinteger(L, f) // angle_t casting // TODO deal with signedness #define luaL_checkangle(L, i) ((angle_t)luaL_checkinteger(L, i)) +#define luaL_optangle(L, i, o) ((angle_t)luaL_optinteger(L, i, o)) #define lua_pushangle(L, a) lua_pushinteger(L, a) #ifdef _DEBUG diff --git a/src/m_cheat.c b/src/m_cheat.c index 89c8009ae..f94377450 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -199,39 +199,41 @@ static UINT8 cht_CheckCheat(cheatseq_t *cht, char key) boolean cht_Responder(event_t *ev) { - UINT8 ret = 0, ch = 0; - if (ev->type != ev_keydown) - return false; + UINT8 ch = 0; - if (ev->key > 0xFF) + if (ev->type == ev_gamepad_down) { - // map some fake (joy) inputs into keys - // map joy inputs into keys switch (ev->key) { - case KEY_JOY1: - case KEY_JOY1 + 2: - ch = KEY_ENTER; - break; - case KEY_HAT1: + case GAMEPAD_BUTTON_DPAD_UP: ch = KEY_UPARROW; break; - case KEY_HAT1 + 1: + case GAMEPAD_BUTTON_DPAD_DOWN: ch = KEY_DOWNARROW; break; - case KEY_HAT1 + 2: + case GAMEPAD_BUTTON_DPAD_LEFT: ch = KEY_LEFTARROW; break; - case KEY_HAT1 + 3: + case GAMEPAD_BUTTON_DPAD_RIGHT: ch = KEY_RIGHTARROW; break; + case GAMEPAD_BUTTON_START: + ch = KEY_ENTER; + break; default: // no mapping return false; } } - else + else if (ev->type == ev_keydown) + { + if (ev->key > 0xFF) + return false; + ch = (UINT8)ev->key; + } + + UINT8 ret = 0; ret += cht_CheckCheat(&cheat_ultimate, (char)ch); ret += cht_CheckCheat(&cheat_ultimate_joy, (char)ch); diff --git a/src/m_menu.c b/src/m_menu.c index 6dffe0d51..7c67cf2da 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -41,6 +41,8 @@ #include "v_video.h" #include "i_video.h" +#include "i_gamepad.h" +#include "g_input.h" #include "keys.h" #include "z_zone.h" #include "w_wad.h" @@ -62,8 +64,6 @@ #include "i_sound.h" #include "fastcmp.h" -#include "i_joy.h" // for joystick menu controls - #include "p_saveg.h" // Only for NEWSKINSAVES // Condition Sets @@ -72,13 +72,6 @@ // And just some randomness for the exits. #include "m_random.h" -#if defined(HAVE_SDL) -#include "SDL.h" -#if SDL_VERSION_ATLEAST(2,0,0) -#include "sdl/sdlmain.h" // JOYSTICK_HOTPLUG -#endif -#endif - #if defined (__GNUC__) && (__GNUC__ >= 4) #define FIXUPO0 #endif @@ -148,7 +141,12 @@ typedef enum levellist_mode_t levellistmode = LLM_CREATESERVER; UINT8 maplistoption = 0; -static char joystickInfo[MAX_JOYSTICKS+1][29]; +static struct +{ + char name[29]; + INT32 index; +} gamepadInfo[MAX_CONNECTED_GAMEPADS + 1]; + #ifndef NONET static UINT32 serverlistpage; #endif @@ -163,6 +161,7 @@ static INT16 itemOn = 1; // menu item skull is on, Hack by Tails 09-18-2002 static INT16 skullAnimCounter = 10; // skull animation counter static boolean setupcontrols_secondaryplayer; +static consvar_t *setupcontrols_joycvar = NULL; static INT32 (*setupcontrols)[2]; // pointer to the gamecontrols of the player being edited // shhh... what am I doing... nooooo! @@ -311,17 +310,17 @@ menu_t MP_MainDef; menu_t OP_ChangeControlsDef; menu_t OP_MPControlsDef, OP_MiscControlsDef; menu_t OP_P1ControlsDef, OP_P2ControlsDef, OP_MouseOptionsDef; -menu_t OP_Mouse2OptionsDef, OP_Joystick1Def, OP_Joystick2Def; +menu_t OP_Mouse2OptionsDef, OP_Gamepad1Def, OP_Gamepad2Def; menu_t OP_CameraOptionsDef, OP_Camera2OptionsDef; menu_t OP_PlaystyleDef; static void M_VideoModeMenu(INT32 choice); static void M_Setup1PControlsMenu(INT32 choice); static void M_Setup2PControlsMenu(INT32 choice); -static void M_Setup1PJoystickMenu(INT32 choice); -static void M_Setup2PJoystickMenu(INT32 choice); +static void M_Setup1PGamepadMenu(INT32 choice); +static void M_Setup2PGamepadMenu(INT32 choice); static void M_Setup1PPlaystyleMenu(INT32 choice); static void M_Setup2PPlaystyleMenu(INT32 choice); -static void M_AssignJoystick(INT32 choice); +static void M_AssignGamepad(INT32 choice); static void M_ChangeControl(INT32 choice); // Video & Sound @@ -375,7 +374,8 @@ static void M_DrawSetupChoosePlayerMenu(void); static void M_DrawControlsDefMenu(void); static void M_DrawCameraOptionsMenu(void); static void M_DrawPlaystyleMenu(void); -static void M_DrawControl(void); +static void M_DrawGamepadMenu(void); +static void M_DrawControlConfigMenu(void); static void M_DrawMainVideoMenu(void); static void M_DrawVideoMode(void); static void M_DrawColorMenu(void); @@ -386,7 +386,7 @@ static void M_DrawConnectMenu(void); static void M_DrawMPMainMenu(void); static void M_DrawRoomMenu(void); #endif -static void M_DrawJoystick(void); +static void M_DrawGamepadList(void); static void M_DrawSetupMultiPlayerMenu(void); // Handling functions @@ -1084,7 +1084,7 @@ static menuitem_t OP_P1ControlsMenu[] = { {IT_CALL | IT_STRING, NULL, "Control Configuration...", M_Setup1PControlsMenu, 10}, {IT_SUBMENU | IT_STRING, NULL, "Mouse Options...", &OP_MouseOptionsDef, 20}, - {IT_SUBMENU | IT_STRING, NULL, "Gamepad Options...", &OP_Joystick1Def , 30}, + {IT_SUBMENU | IT_STRING, NULL, "Gamepad Options...", &OP_Gamepad1Def , 30}, {IT_SUBMENU | IT_STRING, NULL, "Camera Options...", &OP_CameraOptionsDef, 50}, @@ -1096,7 +1096,7 @@ static menuitem_t OP_P2ControlsMenu[] = { {IT_CALL | IT_STRING, NULL, "Control Configuration...", M_Setup2PControlsMenu, 10}, {IT_SUBMENU | IT_STRING, NULL, "Second Mouse Options...", &OP_Mouse2OptionsDef, 20}, - {IT_SUBMENU | IT_STRING, NULL, "Second Gamepad Options...", &OP_Joystick2Def , 30}, + {IT_SUBMENU | IT_STRING, NULL, "Second Gamepad Options...", &OP_Gamepad2Def , 30}, {IT_SUBMENU | IT_STRING, NULL, "Camera Options...", &OP_Camera2OptionsDef, 50}, @@ -1160,43 +1160,43 @@ static menuitem_t OP_ChangeControlsMenu[] = {IT_CALL | IT_STRING2, NULL, "Custom Action 3", M_ChangeControl, GC_CUSTOM3 }, }; -static menuitem_t OP_Joystick1Menu[] = +static menuitem_t OP_Gamepad1Menu[] = { - {IT_STRING | IT_CALL, NULL, "Select Gamepad...", M_Setup1PJoystickMenu, 10}, - {IT_STRING | IT_CVAR, NULL, "Move \x17 Axis" , &cv_moveaxis , 30}, - {IT_STRING | IT_CVAR, NULL, "Move \x18 Axis" , &cv_sideaxis , 40}, - {IT_STRING | IT_CVAR, NULL, "Camera \x17 Axis" , &cv_lookaxis , 50}, - {IT_STRING | IT_CVAR, NULL, "Camera \x18 Axis" , &cv_turnaxis , 60}, - {IT_STRING | IT_CVAR, NULL, "Jump Axis" , &cv_jumpaxis , 70}, - {IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis , 80}, - {IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis , 90}, - {IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis ,100}, + {IT_STRING | IT_CALL, NULL, "Select Gamepad...", M_Setup1PGamepadMenu, 10}, + {IT_STRING | IT_CVAR, NULL, "Move \x17 Axis" , &cv_moveaxis[0] , 30}, + {IT_STRING | IT_CVAR, NULL, "Move \x18 Axis" , &cv_sideaxis[0] , 40}, + {IT_STRING | IT_CVAR, NULL, "Camera \x17 Axis" , &cv_lookaxis[0] , 50}, + {IT_STRING | IT_CVAR, NULL, "Camera \x18 Axis" , &cv_turnaxis[0] , 60}, + {IT_STRING | IT_CVAR, NULL, "Jump Axis" , &cv_jumpaxis[0] , 70}, + {IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis[0] , 80}, + {IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis[0] , 90}, + {IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis[0] ,100}, {IT_STRING | IT_CVAR, NULL, "First-Person Vert-Look", &cv_alwaysfreelook, 120}, {IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook, 130}, - {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Analog Deadzone", &cv_deadzone, 140}, - {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Digital Deadzone", &cv_digitaldeadzone, 150}, + {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Analog Deadzone", &cv_deadzone[0], 140}, + {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Digital Deadzone", &cv_digitaldeadzone[0], 150}, }; -static menuitem_t OP_Joystick2Menu[] = +static menuitem_t OP_Gamepad2Menu[] = { - {IT_STRING | IT_CALL, NULL, "Select Gamepad...", M_Setup2PJoystickMenu, 10}, - {IT_STRING | IT_CVAR, NULL, "Move \x17 Axis" , &cv_moveaxis2 , 30}, - {IT_STRING | IT_CVAR, NULL, "Move \x18 Axis" , &cv_sideaxis2 , 40}, - {IT_STRING | IT_CVAR, NULL, "Camera \x17 Axis" , &cv_lookaxis2 , 50}, - {IT_STRING | IT_CVAR, NULL, "Camera \x18 Axis" , &cv_turnaxis2 , 60}, - {IT_STRING | IT_CVAR, NULL, "Jump Axis" , &cv_jumpaxis2 , 70}, - {IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis2 , 80}, - {IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis2 , 90}, - {IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis2 ,100}, + {IT_STRING | IT_CALL, NULL, "Select Gamepad...", M_Setup2PGamepadMenu, 10}, + {IT_STRING | IT_CVAR, NULL, "Move \x17 Axis" , &cv_moveaxis[1] , 30}, + {IT_STRING | IT_CVAR, NULL, "Move \x18 Axis" , &cv_sideaxis[1] , 40}, + {IT_STRING | IT_CVAR, NULL, "Camera \x17 Axis" , &cv_lookaxis[1] , 50}, + {IT_STRING | IT_CVAR, NULL, "Camera \x18 Axis" , &cv_turnaxis[1] , 60}, + {IT_STRING | IT_CVAR, NULL, "Jump Axis" , &cv_jumpaxis[1] , 70}, + {IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis[1] , 80}, + {IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis[1] , 90}, + {IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis[1] ,100}, {IT_STRING | IT_CVAR, NULL, "First-Person Vert-Look", &cv_alwaysfreelook2,120}, {IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook2, 130}, - {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Analog Deadzone", &cv_deadzone2,140}, - {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Digital Deadzone", &cv_digitaldeadzone2,150}, + {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Analog Deadzone", &cv_deadzone[1],140}, + {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Digital Deadzone", &cv_digitaldeadzone[1],150}, }; -static menuitem_t OP_JoystickSetMenu[1+MAX_JOYSTICKS]; +static menuitem_t OP_GamepadSetMenu[MAX_CONNECTED_GAMEPADS + 1]; static menuitem_t OP_MouseOptionsMenu[] = { @@ -2093,21 +2093,21 @@ menu_t OP_Mouse2OptionsDef = DEFAULTMENUSTYLE( MTREE3(MN_OP_MAIN, MN_OP_P2CONTROLS, MN_OP_P2MOUSE), "M_CONTRO", OP_Mouse2OptionsMenu, &OP_P2ControlsDef, 35, 30); -menu_t OP_Joystick1Def = DEFAULTMENUSTYLE( +menu_t OP_Gamepad1Def = GAMEPADMENUSTYLE( MTREE3(MN_OP_MAIN, MN_OP_P1CONTROLS, MN_OP_P1JOYSTICK), - "M_CONTRO", OP_Joystick1Menu, &OP_P1ControlsDef, 50, 30); -menu_t OP_Joystick2Def = DEFAULTMENUSTYLE( + "M_CONTRO", OP_Gamepad1Menu, &OP_P1ControlsDef, 50, 30); +menu_t OP_Gamepad2Def = GAMEPADMENUSTYLE( MTREE3(MN_OP_MAIN, MN_OP_P2CONTROLS, MN_OP_P2JOYSTICK), - "M_CONTRO", OP_Joystick2Menu, &OP_P2ControlsDef, 50, 30); + "M_CONTRO", OP_Gamepad2Menu, &OP_P2ControlsDef, 50, 30); -menu_t OP_JoystickSetDef = +menu_t OP_GamepadSetDef = { MTREE4(MN_OP_MAIN, 0, 0, MN_OP_JOYSTICKSET), // second and third level set on runtime "M_CONTRO", - sizeof (OP_JoystickSetMenu)/sizeof (menuitem_t), - &OP_Joystick1Def, - OP_JoystickSetMenu, - M_DrawJoystick, + sizeof (OP_GamepadSetMenu)/sizeof (menuitem_t), + &OP_Gamepad1Def, + OP_GamepadSetMenu, + M_DrawGamepadList, 60, 40, 0, NULL @@ -3198,13 +3198,28 @@ static void Command_Manual_f(void) itemOn = 0; } +static INT32 RemapGamepadButton(event_t *ev) +{ + switch (ev->key) + { + case GAMEPAD_BUTTON_A: return KEY_ENTER; + case GAMEPAD_BUTTON_B: return KEY_ESCAPE; + case GAMEPAD_BUTTON_X: return KEY_BACKSPACE; + case GAMEPAD_BUTTON_DPAD_UP: return KEY_UPARROW; + case GAMEPAD_BUTTON_DPAD_DOWN: return KEY_DOWNARROW; + case GAMEPAD_BUTTON_DPAD_LEFT: return KEY_LEFTARROW; + case GAMEPAD_BUTTON_DPAD_RIGHT: return KEY_RIGHTARROW; + } + + return KEY_GAMEPAD + ev->key; +} + // // M_Responder // boolean M_Responder(event_t *ev) { INT32 ch = -1; -// INT32 i; static tic_t joywait = 0, mousewait = 0; static INT32 pjoyx = 0, pjoyy = 0; static INT32 pmousex = 0, pmousey = 0; @@ -3222,6 +3237,8 @@ boolean M_Responder(event_t *ev) if (CON_Ready() && gamestate != GS_WAITINGPLAYERS) return false; + boolean useEventHandler = false; + if (noFurtherInput) { // Ignore input after enter/escape/other buttons @@ -3230,80 +3247,69 @@ boolean M_Responder(event_t *ev) } else if (menuactive) { + if (currentMenu->menuitems[itemOn].status == IT_MSGHANDLER) + useEventHandler = currentMenu->menuitems[itemOn].alphaKey == MM_EVENTHANDLER; + if (ev->type == ev_keydown) { keydown++; ch = ev->key; - // added 5-2-98 remap virtual keys (mouse & joystick buttons) + // added 5-2-98 remap virtual keys (mouse buttons) switch (ch) { case KEY_MOUSE1: - case KEY_JOY1: ch = KEY_ENTER; break; - case KEY_JOY1 + 3: - ch = 'n'; - break; case KEY_MOUSE1 + 1: - case KEY_JOY1 + 1: ch = KEY_ESCAPE; break; - case KEY_JOY1 + 2: - ch = KEY_BACKSPACE; - break; - case KEY_HAT1: - ch = KEY_UPARROW; - break; - case KEY_HAT1 + 1: - ch = KEY_DOWNARROW; - break; - case KEY_HAT1 + 2: - ch = KEY_LEFTARROW; - break; - case KEY_HAT1 + 3: - ch = KEY_RIGHTARROW; - break; } } - else if (ev->type == ev_joystick && ev->key == 0 && joywait < I_GetTime()) + else if (ev->type == ev_gamepad_down) { - const INT32 jdeadzone = (JOYAXISRANGE * cv_digitaldeadzone.value) / FRACUNIT; - if (ev->y != INT32_MAX) + keydown++; + ch = RemapGamepadButton(ev); + } + else if (ev->type == ev_gamepad_axis && ev->which == 0 && joywait < I_GetTime()) + { + const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(0); + const INT16 value = G_GamepadAxisEventValue(0, ev->x); + + if (ev->key == GAMEPAD_AXIS_LEFTY) { - if (Joystick.bGamepadStyle || abs(ev->y) > jdeadzone) + if (abs(value) > jdeadzone) { - if (ev->y < 0 && pjoyy >= 0) + if (value < 0 && pjoyy >= 0) { ch = KEY_UPARROW; joywait = I_GetTime() + NEWTICRATE/7; } - else if (ev->y > 0 && pjoyy <= 0) + else if (value > 0 && pjoyy <= 0) { ch = KEY_DOWNARROW; joywait = I_GetTime() + NEWTICRATE/7; } - pjoyy = ev->y; + pjoyy = value; } else pjoyy = 0; } - - if (ev->x != INT32_MAX) + else if (ev->key == GAMEPAD_AXIS_LEFTX) { - if (Joystick.bGamepadStyle || abs(ev->x) > jdeadzone) + if (abs(value) > jdeadzone) { - if (ev->x < 0 && pjoyx >= 0) + if (value < 0 && pjoyx >= 0) { ch = KEY_LEFTARROW; joywait = I_GetTime() + NEWTICRATE/17; } - else if (ev->x > 0 && pjoyx <= 0) + else if (value > 0 && pjoyx <= 0) { ch = KEY_RIGHTARROW; joywait = I_GetTime() + NEWTICRATE/17; } - pjoyx = ev->x; + pjoyx = value; } else pjoyx = 0; @@ -3339,13 +3345,29 @@ boolean M_Responder(event_t *ev) pmousex = lastx += 30; } } - else if (ev->type == ev_keyup) // Preserve event for other responders + else if (ev->type == ev_keyup || ev->type == ev_gamepad_up) // Preserve event for other responders keydown = 0; } - else if (ev->type == ev_keydown) // Preserve event for other responders + // Preserve event for other responders + else if (ev->type == ev_keydown || ev->type == ev_gamepad_down) + { ch = ev->key; - if (ch == -1) + if (ev->type == ev_gamepad_down) + ch += KEY_GAMEPAD; + } + else if (ev->type == ev_gamepad_axis) + { + const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(0); + const INT16 value = G_GamepadAxisEventValue(0, ev->x); + + if (value > jdeadzone) + ch = KEY_AXES + ev->key; + else if (value < -jdeadzone) + ch = KEY_INV_AXES + ev->key; + } + + if (!useEventHandler && ch == -1) return false; else if (ch == gamecontrol[GC_SYSTEMMENU][0] || ch == gamecontrol[GC_SYSTEMMENU][1]) // allow remappable ESC key ch = KEY_ESCAPE; @@ -3433,27 +3455,25 @@ boolean M_Responder(event_t *ev) if (currentMenu->menuitems[itemOn].status == IT_MSGHANDLER) { - if (currentMenu->menuitems[itemOn].alphaKey != MM_EVENTHANDLER) + if (!useEventHandler) { - if (ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE || ch == KEY_ENTER || ch == KEY_DEL) + UINT16 type = currentMenu->menuitems[itemOn].alphaKey; + if (type == MM_YESNO && !(ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE || ch == KEY_ENTER || ch == KEY_DEL)) + return true; + if (routine) + routine(ch); + if (type == MM_YESNO) { - if (routine) - routine(ch); if (stopstopmessage) stopstopmessage = false; else M_StopMessage(0); noFurtherInput = true; - return true; } return true; } else { - // dirty hack: for customising controls, I want only buttons/keys, not moves - if (ev->type == ev_mouse || ev->type == ev_mouse2 || ev->type == ev_joystick - || ev->type == ev_joystick2) - return true; if (routine) { void (*otherroutine)(event_t *sev) = currentMenu->menuitems[itemOn].itemaction; @@ -3719,6 +3739,7 @@ void M_StartControlPanel(void) currentMenu = &SPauseDef; itemOn = spause_continue; + } else // multiplayer { @@ -3763,6 +3784,9 @@ void M_StartControlPanel(void) } CON_ToggleOff(); // move away console + + if (P_AutoPause()) + P_PauseRumble(NULL); } void M_EndModeAttackRun(void) @@ -3791,6 +3815,7 @@ void M_ClearMenus(boolean callexitmenufunc) hidetitlemap = false; I_UpdateMouseGrab(); + P_UnpauseRumble(NULL); } // @@ -3960,10 +3985,10 @@ void M_Init(void) at all if every item just calls the same function, and nothing more. Now just automate the definition. */ - for (i = 0; i <= MAX_JOYSTICKS; ++i) + for (i = 0; i < MAX_CONNECTED_GAMEPADS + 1; ++i) { - OP_JoystickSetMenu[i].status = ( IT_NOTHING|IT_CALL ); - OP_JoystickSetMenu[i].itemaction = M_AssignJoystick; + OP_GamepadSetMenu[i].status = ( IT_NOTHING|IT_CALL ); + OP_GamepadSetMenu[i].itemaction = M_AssignGamepad; } #ifndef NONET @@ -4193,26 +4218,6 @@ static void M_DrawStaticBox(fixed_t x, fixed_t y, INT32 flags, fixed_t w, fixed_ W_UnlockCachedPatch(patch); } -// -// Draw border for the savegame description -// -#if 0 // once used for joysticks and savegames, now no longer -static void M_DrawSaveLoadBorder(INT32 x,INT32 y) -{ - INT32 i; - - V_DrawScaledPatch (x-8,y+7,0,W_CachePatchName("M_LSLEFT",PU_PATCH)); - - for (i = 0;i < 24;i++) - { - V_DrawScaledPatch (x,y+7,0,W_CachePatchName("M_LSCNTR",PU_PATCH)); - x += 8; - } - - V_DrawScaledPatch (x,y+7,0,W_CachePatchName("M_LSRGHT",PU_PATCH)); -} -#endif - // horizontally centered text static void M_CentreText(INT32 y, const char *string) { @@ -6151,7 +6156,10 @@ void M_StartMessage(const char *string, void *routine, MessageDef.menuitems[0].text = message; MessageDef.menuitems[0].alphaKey = (UINT8)itemtype; - if (!routine && itemtype != MM_NOTHING) itemtype = MM_NOTHING; + + if (routine == NULL && itemtype != MM_NOTHING) + itemtype = MM_NOTHING; + switch (itemtype) { case MM_NOTHING: @@ -6159,9 +6167,7 @@ void M_StartMessage(const char *string, void *routine, MessageDef.menuitems[0].itemaction = M_StopMessage; break; case MM_YESNO: - MessageDef.menuitems[0].status = IT_MSGHANDLER; - MessageDef.menuitems[0].itemaction = routine; - break; + case MM_KEYHANDLER: case MM_EVENTHANDLER: MessageDef.menuitems[0].status = IT_MSGHANDLER; MessageDef.menuitems[0].itemaction = routine; @@ -6193,7 +6199,6 @@ void M_StartMessage(const char *string, void *routine, MessageDef.lastOn = (INT16)((strlines<<8)+max); - //M_SetupNextMenu(); currentMenu = &MessageDef; itemOn = 0; } @@ -11404,7 +11409,7 @@ static void M_ConnectMenuModChecks(INT32 choice) if (modifiedgame) { - M_StartMessage(M_GetText("You have add-ons loaded.\nYou won't be able to join netgames!\n\nTo play online, restart the game\nand don't load any addons.\nSRB2 will automatically add\neverything you need when you join.\n\n(Press a key)\n"),M_ConnectMenu,MM_EVENTHANDLER); + M_StartMessage(M_GetText("You have add-ons loaded.\nYou won't be able to join netgames!\n\nTo play online, restart the game\nand don't load any addons.\nSRB2 will automatically add\neverything you need when you join.\n\n(Press a key)\n"),M_ConnectMenu,MM_KEYHANDLER); return; } @@ -12551,183 +12556,147 @@ static void M_SetupScreenshotMenu(void) item->status = (IT_STRING | IT_CVAR); } -// ============= -// JOYSTICK MENU -// ============= +// ============ +// GAMEPAD MENU +// ============ // Start the controls menu, setting it up for either the console player, // or the secondary splitscreen player -static void M_DrawJoystick(void) +static void M_DrawGamepadList(void) { - INT32 i, compareval2, compareval; + INT32 i; + + INT32 compareval = G_GetGamepadDeviceIndex(0); + INT32 compareval2 = G_GetGamepadDeviceIndex(1); // draw title (or big pic) M_DrawMenuTitle(); - for (i = 0; i <= MAX_JOYSTICKS; i++) // See MAX_JOYSTICKS + for (i = 0; i < MAX_CONNECTED_GAMEPADS + 1; i++) { - M_DrawTextBox(OP_JoystickSetDef.x-8, OP_JoystickSetDef.y+LINEHEIGHT*i-12, 28, 1); - //M_DrawSaveLoadBorder(OP_JoystickSetDef.x+4, OP_JoystickSetDef.y+1+LINEHEIGHT*i); - -#ifdef JOYSTICK_HOTPLUG - if (atoi(cv_usejoystick2.string) > I_NumJoys()) - compareval2 = atoi(cv_usejoystick2.string); - else - compareval2 = cv_usejoystick2.value; - - if (atoi(cv_usejoystick.string) > I_NumJoys()) - compareval = atoi(cv_usejoystick.string); - else - compareval = cv_usejoystick.value; -#else - compareval2 = cv_usejoystick2.value; - compareval = cv_usejoystick.value; -#endif + M_DrawTextBox(OP_GamepadSetDef.x-8, OP_GamepadSetDef.y+LINEHEIGHT*i-12, 28, 1); if ((setupcontrols_secondaryplayer && (i == compareval2)) || (!setupcontrols_secondaryplayer && (i == compareval))) - V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i-4,V_GREENMAP,joystickInfo[i]); + V_DrawString(OP_GamepadSetDef.x, OP_GamepadSetDef.y+LINEHEIGHT*i-4,V_GREENMAP,gamepadInfo[i].name); else - V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i-4,0,joystickInfo[i]); + V_DrawString(OP_GamepadSetDef.x, OP_GamepadSetDef.y+LINEHEIGHT*i-4,0,gamepadInfo[i].name); if (i == itemOn) { - V_DrawScaledPatch(currentMenu->x - 24, OP_JoystickSetDef.y+LINEHEIGHT*i-4, 0, + V_DrawScaledPatch(currentMenu->x - 24, OP_GamepadSetDef.y+LINEHEIGHT*i-4, 0, W_CachePatchName("M_CURSOR", PU_PATCH)); } } } -void M_SetupJoystickMenu(INT32 choice) +boolean M_OnGamepadMenu(void) { - INT32 i = 0; - const char *joyNA = "Unavailable"; - INT32 n = I_NumJoys(); - (void)choice; - - strcpy(joystickInfo[i], "None"); - - for (i = 1; i <= MAX_JOYSTICKS; i++) - { - if (i <= n && (I_GetJoyName(i)) != NULL) - strncpy(joystickInfo[i], I_GetJoyName(i), 28); - else - strcpy(joystickInfo[i], joyNA); - -#ifdef JOYSTICK_HOTPLUG - // We use cv_usejoystick.string as the USER-SET var - // and cv_usejoystick.value as the INTERNAL var - // - // In practice, if cv_usejoystick.string == 0, this overrides - // cv_usejoystick.value and always disables - // - // Update cv_usejoystick.string here so that the user can - // properly change this value. - if (i == cv_usejoystick.value) - CV_SetValue(&cv_usejoystick, i); - if (i == cv_usejoystick2.value) - CV_SetValue(&cv_usejoystick2, i); -#endif - } - - M_SetupNextMenu(&OP_JoystickSetDef); + return currentMenu == &OP_GamepadSetDef; } -static void M_Setup1PJoystickMenu(INT32 choice) +void M_UpdateGamepadMenu(void) +{ + INT32 i = 0, j = 1; + INT32 n = I_NumGamepads(); + + strcpy(gamepadInfo[i].name, "None"); + gamepadInfo[i].index = 0; + + for (i = 1; i < MAX_CONNECTED_GAMEPADS + 1; i++) + { + if (i <= n && (I_GetGamepadName(i)) != NULL) + strlcpy(gamepadInfo[j].name, I_GetGamepadName(i), sizeof gamepadInfo[j].name); + else + strlcpy(gamepadInfo[j].name, "Unavailable", sizeof gamepadInfo[j].name); + + gamepadInfo[j].index = j; + +#ifdef GAMEPAD_HOTPLUG + // Update cv_usegamepad.string here so that the user can + // properly change this value. + for (INT32 jn = 0; jn < NUM_GAMEPADS; jn++) + { + if (i == cv_usegamepad[jn].value) + CV_SetValue(&cv_usegamepad[jn], i); + } +#endif + + j++; + } +} + +static void M_SetupGamepadMenu(void) +{ + M_UpdateGamepadMenu(); + M_SetupNextMenu(&OP_GamepadSetDef); + + if (setupcontrols_secondaryplayer) + itemOn = G_GetGamepadDeviceIndex(1); + else + itemOn = G_GetGamepadDeviceIndex(0); +} + +static void M_Setup1PGamepadMenu(INT32 choice) { setupcontrols_secondaryplayer = false; - OP_JoystickSetDef.prevMenu = &OP_Joystick1Def; - OP_JoystickSetDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); - OP_JoystickSetDef.menuid &= ~(((1 << MENUBITS) - 1) << (MENUBITS*2)); - OP_JoystickSetDef.menuid |= MN_OP_P1CONTROLS << MENUBITS; - OP_JoystickSetDef.menuid |= MN_OP_P1JOYSTICK << (MENUBITS*2); - M_SetupJoystickMenu(choice); + setupcontrols_joycvar = &cv_usegamepad[0]; + OP_GamepadSetDef.prevMenu = &OP_Gamepad1Def; + OP_GamepadSetDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); + OP_GamepadSetDef.menuid &= ~(((1 << MENUBITS) - 1) << (MENUBITS*2)); + OP_GamepadSetDef.menuid |= MN_OP_P1CONTROLS << MENUBITS; + OP_GamepadSetDef.menuid |= MN_OP_P1JOYSTICK << (MENUBITS*2); + + M_SetupGamepadMenu(); + (void)choice; } -static void M_Setup2PJoystickMenu(INT32 choice) +static void M_Setup2PGamepadMenu(INT32 choice) { setupcontrols_secondaryplayer = true; - OP_JoystickSetDef.prevMenu = &OP_Joystick2Def; - OP_JoystickSetDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); - OP_JoystickSetDef.menuid &= ~(((1 << MENUBITS) - 1) << (MENUBITS*2)); - OP_JoystickSetDef.menuid |= MN_OP_P2CONTROLS << MENUBITS; - OP_JoystickSetDef.menuid |= MN_OP_P2JOYSTICK << (MENUBITS*2); - M_SetupJoystickMenu(choice); + setupcontrols_joycvar = &cv_usegamepad[1]; + OP_GamepadSetDef.prevMenu = &OP_Gamepad2Def; + OP_GamepadSetDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); + OP_GamepadSetDef.menuid &= ~(((1 << MENUBITS) - 1) << (MENUBITS*2)); + OP_GamepadSetDef.menuid |= MN_OP_P2CONTROLS << MENUBITS; + OP_GamepadSetDef.menuid |= MN_OP_P2JOYSTICK << (MENUBITS*2); + + M_SetupGamepadMenu(); + (void)choice; } -static void M_AssignJoystick(INT32 choice) +static void M_AssignGamepad(INT32 choice) { -#ifdef JOYSTICK_HOTPLUG - INT32 oldchoice, oldstringchoice; - INT32 numjoys = I_NumJoys(); +#ifdef GAMEPAD_HOTPLUG + INT32 this = gamepadInfo[choice].index; - if (setupcontrols_secondaryplayer) + // Detect if other players are using this gamepad index + for (INT32 i = 0; this && i < NUM_GAMEPADS; i++) { - oldchoice = oldstringchoice = atoi(cv_usejoystick2.string) > numjoys ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value; - CV_SetValue(&cv_usejoystick2, choice); + // Ignore yourself + if (i == (INT32)setupcontrols_secondaryplayer) + continue; - // Just in case last-minute changes were made to cv_usejoystick.value, - // update the string too - // But don't do this if we're intentionally setting higher than numjoys - if (choice <= numjoys) + INT32 other = G_GetGamepadDeviceIndex(i); + + // Ignore gamepads that are disconnected + // (the game will deal with it when they are connected) + if (other > I_NumGamepads()) + continue; + + if (other == this) { - CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); - - // reset this so the comparison is valid - if (oldchoice > numjoys) - oldchoice = cv_usejoystick2.value; - - if (oldchoice != choice) - { - if (choice && oldstringchoice > numjoys) // if we did not select "None", we likely selected a used device - CV_SetValue(&cv_usejoystick2, (oldstringchoice > numjoys ? oldstringchoice : oldchoice)); - - if (oldstringchoice == - (atoi(cv_usejoystick2.string) > numjoys ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value)) - M_StartMessage("This gamepad is used by another\n" - "player. Reset the gamepad\n" - "for that player first.\n\n" - "(Press a key)\n", NULL, MM_NOTHING); - } + M_StartMessage("This gamepad is used by another\n" + "player. Reset the gamepad\n" + "for that player first.\n\n" + "(Press a key)\n", NULL, MM_NOTHING); + return; } } - else - { - oldchoice = oldstringchoice = atoi(cv_usejoystick.string) > numjoys ? atoi(cv_usejoystick.string) : cv_usejoystick.value; - CV_SetValue(&cv_usejoystick, choice); - - // Just in case last-minute changes were made to cv_usejoystick.value, - // update the string too - // But don't do this if we're intentionally setting higher than numjoys - if (choice <= numjoys) - { - CV_SetValue(&cv_usejoystick, cv_usejoystick.value); - - // reset this so the comparison is valid - if (oldchoice > numjoys) - oldchoice = cv_usejoystick.value; - - if (oldchoice != choice) - { - if (choice && oldstringchoice > numjoys) // if we did not select "None", we likely selected a used device - CV_SetValue(&cv_usejoystick, (oldstringchoice > numjoys ? oldstringchoice : oldchoice)); - - if (oldstringchoice == - (atoi(cv_usejoystick.string) > numjoys ? atoi(cv_usejoystick.string) : cv_usejoystick.value)) - M_StartMessage("This gamepad is used by another\n" - "player. Reset the gamepad\n" - "for that player first.\n\n" - "(Press a key)\n", NULL, MM_NOTHING); - } - } - } -#else - if (setupcontrols_secondaryplayer) - CV_SetValue(&cv_usejoystick2, choice); - else - CV_SetValue(&cv_usejoystick, choice); #endif + + CV_SetValue(setupcontrols_joycvar, this); } // ============= @@ -12798,10 +12767,26 @@ static void M_Setup2PControlsMenu(INT32 choice) M_SetupNextMenu(&OP_ChangeControlsDef); } +static const char *M_GetKeyName(UINT8 player, INT32 key) +{ + gamepadtype_e type = GAMEPAD_TYPE_UNKNOWN; + if (cv_usegamepad[player].value) + type = gamepads[player].type; + + if (key >= KEY_GAMEPAD && key < KEY_GAMEPAD + NUM_GAMEPAD_BUTTONS) + return G_GetGamepadButtonString(type, key - KEY_GAMEPAD, GAMEPAD_STRING_MENU1); + else if (key >= KEY_AXES && key < KEY_AXES + NUM_GAMEPAD_AXES) + return G_GetGamepadAxisString(type, key - KEY_AXES, GAMEPAD_STRING_MENU1, false); + else if (key >= KEY_INV_AXES && key < KEY_INV_AXES + NUM_GAMEPAD_AXES) + return G_GetGamepadAxisString(type, key - KEY_INV_AXES, GAMEPAD_STRING_MENU1, true); + + return G_GetDisplayNameForKey(key); +} + #define controlheight 18 // Draws the Customise Controls menu -static void M_DrawControl(void) +static void M_DrawControlConfigMenu(void) { char tmp[50]; INT32 x, y, i, max, cursory = 0, iter; @@ -12881,40 +12866,38 @@ static void M_DrawControl(void) if (currentMenu->menuitems[i].status == IT_CONTROL) { + INT32 right = x + V_StringWidth(currentMenu->menuitems[i].text, 0); V_DrawString(x, y, ((i == itemOn) ? V_YELLOWMAP : 0), currentMenu->menuitems[i].text); + keys[0] = setupcontrols[currentMenu->menuitems[i].alphaKey][0]; keys[1] = setupcontrols[currentMenu->menuitems[i].alphaKey][1]; tmp[0] ='\0'; if (keys[0] == KEY_NULL && keys[1] == KEY_NULL) - { strcpy(tmp, "---"); - } else { if (keys[0] != KEY_NULL) - strcat (tmp, G_KeyNumToName (keys[0])); - + strcat(tmp, M_GetKeyName(setupcontrols_secondaryplayer, keys[0])); if (keys[0] != KEY_NULL && keys[1] != KEY_NULL) - strcat(tmp," or "); - + strcat(tmp, " or "); if (keys[1] != KEY_NULL) - strcat (tmp, G_KeyNumToName (keys[1])); - - + strcat(tmp, M_GetKeyName(setupcontrols_secondaryplayer, keys[1])); } - V_DrawRightAlignedString(BASEVIDWIDTH-currentMenu->x, y, V_YELLOWMAP, tmp); + + INT32 left = BASEVIDWIDTH-currentMenu->x-V_StringWidth(tmp, V_ALLOWLOWERCASE); + if (left - 8 <= right) + V_DrawRightAlignedThinString(BASEVIDWIDTH-currentMenu->x, y+1, V_ALLOWLOWERCASE | V_YELLOWMAP, tmp); + else + V_DrawRightAlignedString(BASEVIDWIDTH-currentMenu->x, y, V_ALLOWLOWERCASE | V_YELLOWMAP, tmp); } - /*else if (currentMenu->menuitems[i].status == IT_GRAYEDOUT2) - V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text);*/ else if ((currentMenu->menuitems[i].status == IT_HEADER) && (i != max-1)) M_DrawLevelPlatterHeader(y, currentMenu->menuitems[i].text, true, false); y += SMALLLINEHEIGHT; } - V_DrawScaledPatch(currentMenu->x - 20, cursory, 0, - W_CachePatchName("M_CURSOR", PU_PATCH)); + V_DrawScaledPatch(currentMenu->x - 20, cursory, 0, W_CachePatchName("M_CURSOR", PU_PATCH)); } #undef controlbuffer @@ -12922,55 +12905,55 @@ static void M_DrawControl(void) static INT32 controltochange; static char controltochangetext[33]; -static void M_ChangecontrolResponse(event_t *ev) +static void M_ChangeControlResponse(event_t *ev) { - INT32 control; - INT32 found; - INT32 ch = ev->key; + // dirty hack: for customising controls, I want only buttons/keys, not moves + if (ev->type == ev_mouse || ev->type == ev_mouse2) + return; + + INT32 ch = ev->key; + + // Remap gamepad events + if (ev->type == ev_gamepad_down) + ch += KEY_GAMEPAD; + else if (ev->type == ev_gamepad_axis) + { + const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(ev->which); + const INT16 value = G_GamepadAxisEventValue(ev->which, ev->x); + + if (value > jdeadzone) + ch += KEY_AXES; + else if (value < -jdeadzone) + ch += KEY_INV_AXES; + else + return; + } + else if (ev->type != ev_keydown) + return; // ESCAPE cancels; dummy out PAUSE if (ch != KEY_ESCAPE && ch != KEY_PAUSE) { - - switch (ev->type) - { - // ignore mouse/joy movements, just get buttons - case ev_mouse: - case ev_mouse2: - case ev_joystick: - case ev_joystick2: - ch = KEY_NULL; // no key - break; - - // keypad arrows are converted for the menu in cursor arrows - // so use the event instead of ch - case ev_keydown: - ch = ev->key; - break; - - default: - break; - } - - control = controltochange; + INT32 control = controltochange; // check if we already entered this key - found = -1; - if (setupcontrols[control][0] ==ch) + INT32 found = -1; + if (setupcontrols[control][0] == ch) found = 0; - else if (setupcontrols[control][1] ==ch) + else if (setupcontrols[control][1] == ch) found = 1; + if (found >= 0) { - // replace mouse and joy clicks by double clicks - if (ch >= KEY_MOUSE1 && ch <= KEY_MOUSE1+MOUSEBUTTONS) - setupcontrols[control][found] = ch-KEY_MOUSE1+KEY_DBLMOUSE1; - else if (ch >= KEY_JOY1 && ch <= KEY_JOY1+JOYBUTTONS) - setupcontrols[control][found] = ch-KEY_JOY1+KEY_DBLJOY1; - else if (ch >= KEY_2MOUSE1 && ch <= KEY_2MOUSE1+MOUSEBUTTONS) - setupcontrols[control][found] = ch-KEY_2MOUSE1+KEY_DBL2MOUSE1; - else if (ch >= KEY_2JOY1 && ch <= KEY_2JOY1+JOYBUTTONS) - setupcontrols[control][found] = ch-KEY_2JOY1+KEY_DBL2JOY1; +#define CHECK_DBL(key, length) (ch >= key && ch <= key+length) +#define SET_DBL(key, dblkey) ch-key+dblkey + // replace mouse clicks by double clicks + if (CHECK_DBL(KEY_MOUSE1, MOUSEBUTTONS)) + setupcontrols[control][found] = SET_DBL(KEY_MOUSE1, KEY_DBLMOUSE1); + else if (CHECK_DBL(KEY_2MOUSE1, MOUSEBUTTONS)) + setupcontrols[control][found] = SET_DBL(KEY_2MOUSE1, KEY_DBL2MOUSE1); +#undef CHECK_DBL +#undef SET_DBL } else { @@ -12985,9 +12968,11 @@ static void M_ChangecontrolResponse(event_t *ev) found = 0; setupcontrols[control][1] = KEY_NULL; //replace key 1,clear key2 } - (void)G_CheckDoubleUsage(ch, true); + + G_CheckDoubleUsage(ch, true); setupcontrols[control][found] = ch; } + S_StartSound(NULL, sfx_strpst); } else if (ch == KEY_PAUSE) @@ -13003,7 +12988,7 @@ static void M_ChangecontrolResponse(event_t *ev) sprintf(tmp, M_GetText("The \x82Pause Key \x80is enabled, but \nit is not configurable. \n\nHit another key for\n%s\nESC for Cancel"), controltochangetext); - M_StartMessage(tmp, M_ChangecontrolResponse, MM_EVENTHANDLER); + M_StartMessage(tmp, M_ChangeControlResponse, MM_EVENTHANDLER); currentMenu->prevMenu = prev; S_StartSound(NULL, sfx_s3k42); @@ -13029,7 +13014,150 @@ static void M_ChangeControl(INT32 choice) currentMenu->menuitems[choice].text); strlcpy(controltochangetext, currentMenu->menuitems[choice].text, 33); - M_StartMessage(tmp, M_ChangecontrolResponse, MM_EVENTHANDLER); + M_StartMessage(tmp, M_ChangeControlResponse, MM_EVENTHANDLER); +} + +static const char *M_GetGamepadAxisName(consvar_t *cv) +{ + switch (cv->value) + { + case 0: + return "None"; + + case 1: // X + return "L. Stick X"; + case 2: // Y + return "L. Stick Y"; + case 3: // X + return "R. Stick X"; + case 4: // Y + return "R. Stick Y"; + + case -1: // X- + return "L. Stick X (inv.)"; + case -2: // Y- + return "L. Stick Y (inv.)"; + case -3: // X- + return "R. Stick X (inv.)"; + case -4: // Y- + return "R. Stick Y (inv.)"; + + case 5: + return "L. Trigger"; + case 6: + return "R. Trigger"; + + default: + return cv->string; + } +} + +static void M_DrawGamepadMenu(void) +{ + INT32 x, y, i, cursory = 0; + INT32 right, left; + + // DRAW MENU + x = currentMenu->x; + y = currentMenu->y; + + // draw title (or big pic) + M_DrawMenuTitle(); + + for (i = 0; i < currentMenu->numitems; i++) + { + if (i == itemOn) + cursory = y; + switch (currentMenu->menuitems[i].status & IT_DISPLAY) + { + case IT_NOTHING: + case IT_DYBIGSPACE: + y += LINEHEIGHT; + break; + case IT_STRING: + case IT_WHITESTRING: + if (currentMenu->menuitems[i].alphaKey) + y = currentMenu->y+currentMenu->menuitems[i].alphaKey; + if (i == itemOn) + cursory = y; + + if ((currentMenu->menuitems[i].status & IT_DISPLAY)==IT_STRING) + V_DrawString(x, y, 0, currentMenu->menuitems[i].text); + else + V_DrawString(x, y, V_YELLOWMAP, currentMenu->menuitems[i].text); + + right = x + V_StringWidth(currentMenu->menuitems[i].text, 0); + + // Cvar specific handling + switch (currentMenu->menuitems[i].status & IT_TYPE) + case IT_CVAR: + { + consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction; + switch (currentMenu->menuitems[i].status & IT_CVARTYPE) + { + case IT_CV_SLIDER: + M_DrawSlider(x, y, cv, (i == itemOn)); + break; + default: + { + const char *str = cv->string; + INT32 flags = V_YELLOWMAP; + INT32 width = V_StringWidth(str, flags); + + if (cv->PossibleValue == joyaxis_cons_t) + { + str = M_GetGamepadAxisName(cv); + flags |= V_ALLOWLOWERCASE; + + width = V_StringWidth(str, flags); + left = BASEVIDWIDTH - x - width; + + if (left - 16 <= right) + { + width = V_ThinStringWidth(str, flags); + V_DrawRightAlignedThinString(BASEVIDWIDTH - x, y + 1, flags, str); + } + else + V_DrawRightAlignedString(BASEVIDWIDTH - x, y, flags, str); + } + else + V_DrawRightAlignedString(BASEVIDWIDTH - x, y, flags, str); + + if (i == itemOn) + { + V_DrawCharacter(BASEVIDWIDTH - x - 10 - width - (skullAnimCounter/5), y, + '\x1C' | V_YELLOWMAP, false); + V_DrawCharacter(BASEVIDWIDTH - x + 2 + (skullAnimCounter/5), y, + '\x1D' | V_YELLOWMAP, false); + } + break; + } + } + break; + } + y += STRINGHEIGHT; + break; + case IT_TRANSTEXT: + if (currentMenu->menuitems[i].alphaKey) + y = currentMenu->y+currentMenu->menuitems[i].alphaKey; + V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text); + y += SMALLLINEHEIGHT; + break; + case IT_HEADERTEXT: // draws 16 pixels to the left, in yellow text + if (currentMenu->menuitems[i].alphaKey) + y = currentMenu->y+currentMenu->menuitems[i].alphaKey; + + //V_DrawString(x-16, y, V_YELLOWMAP, currentMenu->menuitems[i].text); + M_DrawLevelPlatterHeader(y - (lsheadingheight - 12), currentMenu->menuitems[i].text, true, false); + y += SMALLLINEHEIGHT; + break; + } + } + + // DRAW THE SKULL CURSOR + V_DrawScaledPatch(currentMenu->x - 24, cursory, 0, + W_CachePatchName("M_CURSOR", PU_PATCH)); + V_DrawString(currentMenu->x, cursory, V_YELLOWMAP, currentMenu->menuitems[itemOn].text); } static void M_Setup1PPlaystyleMenu(INT32 choice) diff --git a/src/m_menu.h b/src/m_menu.h index a7072b0c1..8d023811d 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -223,8 +223,9 @@ typedef enum { MM_NOTHING = 0, // is just displayed until the user do someting MM_YESNO, // routine is called with only 'y' or 'n' in param - MM_EVENTHANDLER // the same of above but without 'y' or 'n' restriction - // and routine is void routine(event_t *) (ex: set control) + MM_KEYHANDLER, // the same of above but without 'y' or 'n' restriction + MM_EVENTHANDLER // the same of above but routine is void routine(event_t *) + // (ex: set control) } menumessagetype_t; void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype); @@ -361,9 +362,11 @@ extern menu_t *currentMenu; extern menu_t MainDef; extern menu_t SP_LoadDef; -// Call upon joystick hotplug -void M_SetupJoystickMenu(INT32 choice); -extern menu_t OP_JoystickSetDef; +// Call when a gamepad is connected or disconnected +void M_UpdateGamepadMenu(void); + +// Returns true if the player is on the gamepad selection menu +boolean M_OnGamepadMenu(void); // Stuff for customizing the player select screen typedef struct @@ -538,6 +541,19 @@ void M_FreePlayerSetupColors(void); NULL\ } +#define GAMEPADMENUSTYLE(id, header, source, prev, x, y)\ +{\ + id,\ + header,\ + sizeof(source)/sizeof(menuitem_t),\ + prev,\ + source,\ + M_DrawGamepadMenu,\ + x, y,\ + 0,\ + NULL\ +} + #define MAPPLATTERMENUSTYLE(id, header, source)\ {\ id,\ @@ -558,7 +574,7 @@ void M_FreePlayerSetupColors(void); sizeof (source)/sizeof (menuitem_t),\ prev,\ source,\ - M_DrawControl,\ + M_DrawControlConfigMenu,\ 24, 40,\ 0,\ NULL\ diff --git a/src/p_haptic.c b/src/p_haptic.c new file mode 100644 index 000000000..dbfa58737 --- /dev/null +++ b/src/p_haptic.c @@ -0,0 +1,115 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2021-2022 by Jaime "Lactozilla" Passos. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file p_haptic.c +/// \brief Haptic feedback + +#include "p_haptic.h" +#include "g_game.h" +#include "d_netcmd.h" +#include "i_gamepad.h" +#include "doomstat.h" + +// Helper function: Returns the gamepad index for a player if it's enabled +static INT16 GetGamepadIndex(player_t *player) +{ + INT16 index = G_GetGamepadForPlayer(player); + + if (index >= 0 && cv_usegamepad[index].value) + return index; + + return -1; +} + +// Rumbles a player's gamepad, or all gamepads +boolean P_DoRumble(player_t *player, fixed_t large_magnitude, fixed_t small_magnitude, tic_t duration) +{ + if (!I_RumbleSupported()) + return false; + + // Rumble every gamepad + if (player == NULL) + { + for (UINT8 i = 0; i < NUM_GAMEPADS; i++) + { + if (cv_gamepad_rumble[i].value) + G_RumbleGamepad(i, large_magnitude, small_magnitude, duration); + } + + return true; + } + + INT16 which = GetGamepadIndex(player); + if (which < 0 || !cv_gamepad_rumble[which].value) + return false; + + return G_RumbleGamepad((UINT8)which, large_magnitude, small_magnitude, duration); +} + +// Pauses or unpauses gamepad rumble for a player (or all of them) +// Rumble is paused or unpaused regardless if it's enabled or not +static void SetRumblePaused(player_t *player, boolean pause) +{ + INT16 which = GetGamepadIndex(player); + + if (which >= 0) + G_SetGamepadRumblePaused((UINT8)which, pause); + else if (player == NULL) + { + // Pause or unpause every gamepad + for (UINT8 i = 0; i < NUM_GAMEPADS; i++) + G_SetGamepadRumblePaused(i, pause); + } +} + +void P_PauseRumble(player_t *player) +{ + SetRumblePaused(player, true); +} + +void P_UnpauseRumble(player_t *player) +{ + SetRumblePaused(player, false); +} + +boolean P_IsRumbleEnabled(player_t *player) +{ + INT16 which = GetGamepadIndex(player); + if (which < 0 || !cv_gamepad_rumble[which].value) + return false; + + return G_RumbleSupported((UINT8)which); +} + +boolean P_IsRumblePaused(player_t *player) +{ + INT16 which = GetGamepadIndex(player); + if (which < 0 || !cv_gamepad_rumble[which].value) + return false; + + return G_GetGamepadRumblePaused((UINT8)which); +} + +// Stops gamepad rumble for a player (or all of them) +void P_StopRumble(player_t *player) +{ + if (!I_RumbleSupported()) + return; + + if (player) + { + INT16 which = GetGamepadIndex(player); + if (which >= 0) + G_StopGamepadRumble((UINT8)which); + return; + } + + // Stop every gamepad instead + for (UINT8 i = 0; i < NUM_GAMEPADS; i++) + G_StopGamepadRumble(i); +} diff --git a/src/p_haptic.h b/src/p_haptic.h new file mode 100644 index 000000000..1bd4f9199 --- /dev/null +++ b/src/p_haptic.h @@ -0,0 +1,27 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2021-2022 by Jaime "Lactozilla" Passos. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file p_haptic.h +/// \brief Haptic feedback + +#ifndef __P_HAPTIC__ +#define __P_HAPTIC__ + +#include "doomdef.h" +#include "p_local.h" + +boolean P_DoRumble(player_t *player, fixed_t large_magnitude, fixed_t small_magnitude, tic_t duration); +void P_PauseRumble(player_t *player); +void P_UnpauseRumble(player_t *player); +boolean P_IsRumbleEnabled(player_t *player); +boolean P_IsRumblePaused(player_t *player); +void P_StopRumble(player_t *player); + +#define P_DoRumbleCombined(player, magnitude, dur) P_DoRumble(player, magnitude, magnitude, dur); + +#endif // __P_HAPTIC__ diff --git a/src/p_inter.c b/src/p_inter.c index dd3e0f9c2..f3c13e315 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -13,6 +13,7 @@ #include "doomdef.h" #include "i_system.h" +#include "i_gamepad.h" #include "am_map.h" #include "g_game.h" #include "m_random.h" @@ -24,6 +25,7 @@ #include "lua_hook.h" #include "m_cond.h" // unlockables, emblems, etc #include "p_setup.h" +#include "p_haptic.h" #include "m_cheat.h" // objectplace #include "m_misc.h" #include "v_video.h" // video flags for CEchos @@ -33,54 +35,6 @@ #define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : "" #define CTFTEAMENDCODE(pl) pl->ctfteam ? "\x80" : "" -void P_ForceFeed(const player_t *player, INT32 attack, INT32 fade, tic_t duration, INT32 period) -{ - BasicFF_t Basicfeed; - if (!player) - return; - Basicfeed.Duration = (UINT32)(duration * (100L/TICRATE)); - Basicfeed.ForceX = Basicfeed.ForceY = 1; - Basicfeed.Gain = 25000; - Basicfeed.Magnitude = period*10; - Basicfeed.player = player; - /// \todo test FFB - P_RampConstant(&Basicfeed, attack, fade); -} - -void P_ForceConstant(const BasicFF_t *FFInfo) -{ - JoyFF_t ConstantQuake; - if (!FFInfo || !FFInfo->player) - return; - ConstantQuake.ForceX = FFInfo->ForceX; - ConstantQuake.ForceY = FFInfo->ForceY; - ConstantQuake.Duration = FFInfo->Duration; - ConstantQuake.Gain = FFInfo->Gain; - ConstantQuake.Magnitude = FFInfo->Magnitude; - if (FFInfo->player == &players[consoleplayer]) - I_Tactile(ConstantForce, &ConstantQuake); - else if (splitscreen && FFInfo->player == &players[secondarydisplayplayer]) - I_Tactile2(ConstantForce, &ConstantQuake); -} -void P_RampConstant(const BasicFF_t *FFInfo, INT32 Start, INT32 End) -{ - JoyFF_t RampQuake; - if (!FFInfo || !FFInfo->player) - return; - RampQuake.ForceX = FFInfo->ForceX; - RampQuake.ForceY = FFInfo->ForceY; - RampQuake.Duration = FFInfo->Duration; - RampQuake.Gain = FFInfo->Gain; - RampQuake.Magnitude = FFInfo->Magnitude; - RampQuake.Start = Start; - RampQuake.End = End; - if (FFInfo->player == &players[consoleplayer]) - I_Tactile(ConstantForce, &RampQuake); - else if (splitscreen && FFInfo->player == &players[secondarydisplayplayer]) - I_Tactile2(ConstantForce, &RampQuake); -} - - // // GET STUFF // @@ -3057,6 +3011,8 @@ static boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, IN player_t *player = target->player; (void)damage; //unused parm + P_DoRumbleCombined(player, FRACUNIT, TICRATE / 6); + // If flashing or invulnerable, ignore the tag, if (player->powers[pw_flashing] || player->powers[pw_invulnerability]) return false; @@ -3160,6 +3116,8 @@ static boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *sou { player_t *player = target->player; + (void)damage; + if (!(damagetype & DMG_CANHURTSELF)) { // You can't kill yourself, idiot... @@ -3222,6 +3180,8 @@ static boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *sou static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage) { + (void)damage; + player->pflags &= ~PF_SLIDING; player->powers[pw_carry] = CR_NONE; @@ -3242,7 +3202,7 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage) // Get rid of emeralds player->powers[pw_emeralds] = 0; - P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); + P_DoRumbleCombined(player, FRACUNIT, TICRATE / 3); P_ResetPlayer(player); @@ -3282,7 +3242,9 @@ static void P_SuperDamage(player_t *player, mobj_t *inflictor, mobj_t *source, I fixed_t fallbackspeed; angle_t ang; - P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); + (void)damage; + + P_DoRumbleCombined(player, FRACUNIT, TICRATE / 6); if (player->mo->eflags & MFE_VERTICALFLIP) player->mo->z--; @@ -3363,12 +3325,14 @@ void P_RemoveShield(player_t *player) static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) { + (void)damage; + // Must do pain first to set flashing -- P_RemoveShield can cause damage P_DoPlayerPain(player, source, inflictor); P_RemoveShield(player); - P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); + P_DoRumbleCombined(player, FRACUNIT, TICRATE / 6); if (damagetype == DMG_SPIKE) // spikes S_StartSound(player->mo, sfx_spkdth); @@ -3397,7 +3361,7 @@ static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, IN { P_DoPlayerPain(player, source, inflictor); - P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); + P_DoRumbleCombined(player, FRACUNIT, TICRATE / 6); if (damagetype == DMG_SPIKE) // spikes S_StartSound(player->mo, sfx_spkdth); @@ -3728,8 +3692,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da damage = 1; P_KillPlayer(player, source, damage); } - - P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); } // Killing dead. Just for kicks. diff --git a/src/p_local.h b/src/p_local.h index 2b3020997..31a9e6c9d 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -453,18 +453,6 @@ extern mobj_t **blocklinks; // for thing chains // // P_INTER // -typedef struct BasicFF_s -{ - INT32 ForceX; ///< The X of the Force's Vel - INT32 ForceY; ///< The Y of the Force's Vel - const player_t *player; ///< Player of Rumble - //All - UINT32 Duration; ///< The total duration of the effect, in microseconds - INT32 Gain; ///< /The gain to be applied to the effect, in the range from 0 through 10,000. - //All, CONSTANTFORCE �10,000 to 10,000 - INT32 Magnitude; ///< Magnitude of the effect, in the range from 0 through 10,000. -} BasicFF_t; - /* Damage/death types, for P_DamageMobj and related */ //// Damage types //#define DMG_NORMAL 0 (unneeded?) @@ -485,9 +473,6 @@ typedef struct BasicFF_s #define DMG_CANHURTSELF 0x40 // Flag - can hurt self/team indirectly, such as through mines #define DMG_DEATHMASK DMG_INSTAKILL // if bit 7 is set, this is a death type instead of a damage type -void P_ForceFeed(const player_t *player, INT32 attack, INT32 fade, tic_t duration, INT32 period); -void P_ForceConstant(const BasicFF_t *FFInfo); -void P_RampConstant(const BasicFF_t *FFInfo, INT32 Start, INT32 End); void P_RemoveShield(player_t *player); void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source); boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); diff --git a/src/p_setup.c b/src/p_setup.c index 3d85de0b8..ea7230f44 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7789,6 +7789,10 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) if (rendermode == render_none || reloadinggamestate) return true; + R_ResetViewInterpolation(0); + R_ResetViewInterpolation(0); + R_UpdateMobjInterpolators(); + // Title card! G_StartTitleCard(); diff --git a/src/p_spec.c b/src/p_spec.c index 82337d2f6..5c9caa82f 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1882,7 +1882,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller // "Trigger on X calls" linedefs reset if args[2] is set if (specialtype == 321 && triggerline->args[2]) - triggerline->callcount = triggerline->args[3]; + triggerline->callcount = triggerline->args[1]; else { // These special types work only once diff --git a/src/p_user.c b/src/p_user.c index 45978d105..6fd24eb13 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -5327,9 +5327,9 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) // disabled because it seemed to disorient people and Z-targeting exists now /*if (!demoplayback) { - if (player == &players[consoleplayer] && cv_cam_turnfacingability[0].value > 0 && !(PLAYER1INPUTDOWN(GC_TURNLEFT) || PLAYER1INPUTDOWN(GC_TURNRIGHT))) + if (player == &players[consoleplayer] && cv_cam_turnfacingability[0].value > 0 && !(G_PlayerInputDown(0, GC_TURNLEFT) || G_PlayerInputDown(0, GC_TURNRIGHT))) P_SetPlayerAngle(player, player->mo->angle);; - else if (player == &players[secondarydisplayplayer] && cv_cam_turnfacingability[1].value > 0 && !(PLAYER2INPUTDOWN(GC_TURNLEFT) || PLAYER2INPUTDOWN(GC_TURNRIGHT))) + else if (player == &players[secondarydisplayplayer] && cv_cam_turnfacingability[1].value > 0 && !(G_PlayerInputDown(1, GC_TURNLEFT) || G_PlayerInputDown(1, GC_TURNRIGHT))) P_SetPlayerAngle(player, player->mo->angle); }*/ } @@ -7335,7 +7335,7 @@ static void P_NiGHTSMovement(player_t *player) else if (cmd->forwardmove < 0) newangle = 270; } - else // AngleFixed(R_PointToAngle2()) results in slight inaccuracy! Don't use it unless movement is on both axises. + else // AngleFixed(R_PointToAngle2()) results in slight inaccuracy! Don't use it unless movement is on both axes. newangle = (INT16)FixedInt(AngleFixed(R_PointToAngle2(0,0, cmd->sidemove*FRACUNIT, cmd->forwardmove*FRACUNIT))); newangle -= player->viewrollangle / ANG1; diff --git a/src/sdl/Sourcefile b/src/sdl/Sourcefile index 82d5ce073..ef6a8b0dc 100644 --- a/src/sdl/Sourcefile +++ b/src/sdl/Sourcefile @@ -2,6 +2,7 @@ i_net.c i_system.c i_main.c i_video.c +i_gamepad.c dosstr.c endtxt.c hwsym_sdl.c diff --git a/src/sdl/i_gamepad.c b/src/sdl/i_gamepad.c new file mode 100644 index 000000000..dbafc1f63 --- /dev/null +++ b/src/sdl/i_gamepad.c @@ -0,0 +1,918 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file i_gamepad.c +/// \brief Gamepads + +#ifdef HAVE_SDL +#include "../i_gamepad.h" +#include "../i_system.h" +#include "../doomdef.h" +#include "../d_main.h" +#include "../d_netcmd.h" +#include "../g_game.h" +#include "../m_argv.h" +#include "../m_menu.h" +#include "../z_zone.h" + +#include "SDL.h" +#include "SDL_joystick.h" +#include "sdlmain.h" + +static void Controller_ChangeDevice(UINT8 num); +static void Controller_Close(UINT8 num); +static void Controller_StopRumble(UINT8 num); + +static ControllerInfo controllers[NUM_GAMEPADS]; + +static boolean rumble_supported = false; +static boolean rumble_paused = false; + +// This attempts to initialize the gamepad subsystems +static boolean InitGamepadSubsystems(void) +{ + if (M_CheckParm("-noxinput")) + SDL_SetHintWithPriority(SDL_HINT_XINPUT_ENABLED, "0", SDL_HINT_OVERRIDE); + if (M_CheckParm("-nohidapi")) + SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_HIDAPI, "0", SDL_HINT_OVERRIDE); + + if (SDL_WasInit(GAMEPAD_INIT_FLAGS) == 0) + { + if (SDL_InitSubSystem(GAMEPAD_INIT_FLAGS) == -1) + { + CONS_Printf(M_GetText("Couldn't initialize game controller subsystems: %s\n"), SDL_GetError()); + return false; + } + } + + return true; +} + +void I_InitGamepads(void) +{ + if (M_CheckParm("-nojoy")) + return; + + CONS_Printf("I_InitGamepads()...\n"); + + if (!InitGamepadSubsystems()) + return; + + rumble_supported = !M_CheckParm("-norumble"); + + for (UINT8 i = 0; i < NUM_GAMEPADS; i++) + controllers[i].info = &gamepads[i]; + + for (UINT8 i = 0; i < NUM_GAMEPADS; i++) + Controller_ChangeDevice(i); +} + +INT32 I_NumGamepads(void) +{ + if (SDL_WasInit(GAMEPAD_INIT_FLAGS) == GAMEPAD_INIT_FLAGS) + return SDL_NumJoysticks(); + else + return 0; +} + +// From the SDL source code +#define USB_VENDOR_MICROSOFT 0x045e +#define USB_VENDOR_PDP 0x0e6f +#define USB_VENDOR_POWERA_ALT 0x20d6 + +#define USB_PRODUCT_XBOX_ONE_ELITE_SERIES_1 0x02e3 +#define USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2 0x0b00 +#define USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH 0x0b05 +#define USB_PRODUCT_XBOX_SERIES_X 0x0b12 +#define USB_PRODUCT_XBOX_SERIES_X_BLE 0x0b13 +#define USB_PRODUCT_XBOX_SERIES_X_VICTRIX_GAMBIT 0x02d6 +#define USB_PRODUCT_XBOX_SERIES_X_PDP_BLUE 0x02d9 +#define USB_PRODUCT_XBOX_SERIES_X_PDP_AFTERGLOW 0x02da +#define USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO2 0x4001 +#define USB_PRODUCT_XBOX_SERIES_X_POWERA_SPECTRA 0x4002 + +static boolean IsJoystickXboxOneElite(Uint16 vendor_id, Uint16 product_id) +{ + if (vendor_id == USB_VENDOR_MICROSOFT) { + if (product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_1 || + product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2 || + product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH) { + return true; + } + } + + return false; +} + +static boolean IsJoystickXboxSeriesXS(Uint16 vendor_id, Uint16 product_id) +{ + if (vendor_id == USB_VENDOR_MICROSOFT) { + if (product_id == USB_PRODUCT_XBOX_SERIES_X || + product_id == USB_PRODUCT_XBOX_SERIES_X_BLE) { + return true; + } + } + else if (vendor_id == USB_VENDOR_PDP) { + if (product_id == USB_PRODUCT_XBOX_SERIES_X_VICTRIX_GAMBIT || + product_id == USB_PRODUCT_XBOX_SERIES_X_PDP_BLUE || + product_id == USB_PRODUCT_XBOX_SERIES_X_PDP_AFTERGLOW) { + return true; + } + } + else if (vendor_id == USB_VENDOR_POWERA_ALT) { + if ((product_id >= 0x2001 && product_id <= 0x201a) || + product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO2 || + product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_SPECTRA) { + return true; + } + } + + return false; +} + +// Opens a controller device +static boolean Controller_OpenDevice(UINT8 which, INT32 devindex) +{ + if (SDL_WasInit(GAMEPAD_INIT_FLAGS) == 0) + { + CONS_Debug(DBG_GAMELOGIC, M_GetText("Game controller subsystems not started\n")); + return false; + } + + if (devindex <= 0) + return false; + + if (SDL_NumJoysticks() == 0) + { + CONS_Debug(DBG_GAMELOGIC, M_GetText("Found no controllers on this system\n")); + return false; + } + + devindex--; + + if (!SDL_IsGameController(devindex)) + { + CONS_Debug(DBG_GAMELOGIC, M_GetText("Device index %d isn't a game controller\n"), devindex); + return false; + } + + ControllerInfo *controller = &controllers[which]; + SDL_GameController *newdev = SDL_GameControllerOpen(devindex); + + // Handle the edge case where the device <-> controller index assignment can change due to hotplugging + // This indexing is SDL's responsibility and there's not much we can do about it. + // + // Example: + // 1. Plug Controller A -> Index 0 opened + // 2. Plug Controller B -> Index 1 opened + // 3. Unplug Controller A -> Index 0 closed, Index 1 active + // 4. Unplug Controller B -> Index 0 inactive, Index 1 closed + // 5. Plug Controller B -> Index 0 opened + // 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B + if (controller->dev) + { + if (controller->dev == newdev // same device, nothing to do + || (newdev == NULL && SDL_GameControllerGetAttached(controller->dev))) // we failed, but already have a working device + return true; + + // Else, we're changing devices, so close the controller + CONS_Debug(DBG_GAMELOGIC, M_GetText("Controller %d device is changing; closing controller...\n"), which); + Controller_Close(which); + } + + if (newdev == NULL) + { + CONS_Debug(DBG_GAMELOGIC, M_GetText("Controller %d: Couldn't open device - %s\n"), which, SDL_GetError()); + controller->started = false; + } + else + { + controller->dev = newdev; + controller->joydev = SDL_GameControllerGetJoystick(controller->dev); + controller->started = true; + + CONS_Debug(DBG_GAMELOGIC, M_GetText("Controller %d: %s\n"), which, SDL_GameControllerName(controller->dev)); + + #define GAMEPAD_TYPE_CASE(ctrl) \ + case SDL_CONTROLLER_TYPE_##ctrl: \ + controller->info->type = GAMEPAD_TYPE_##ctrl; \ + break + + switch (SDL_GameControllerGetType(newdev)) + { + GAMEPAD_TYPE_CASE(UNKNOWN); + GAMEPAD_TYPE_CASE(XBOX360); + GAMEPAD_TYPE_CASE(XBOXONE); + GAMEPAD_TYPE_CASE(PS3); + GAMEPAD_TYPE_CASE(PS4); + GAMEPAD_TYPE_CASE(PS5); + GAMEPAD_TYPE_CASE(NINTENDO_SWITCH_PRO); + GAMEPAD_TYPE_CASE(GOOGLE_STADIA); + GAMEPAD_TYPE_CASE(AMAZON_LUNA); + GAMEPAD_TYPE_CASE(VIRTUAL); + default: break; + } + + #undef GAMEPAD_BUTTON_CASE + + // Check the device vendor and product to find out what controller this actually is + Uint16 vendor = SDL_JoystickGetDeviceVendor(devindex); + Uint16 product = SDL_JoystickGetDeviceProduct(devindex); + + if (IsJoystickXboxSeriesXS(vendor, product)) + controller->info->type = GAMEPAD_TYPE_XBOX_SERIES_XS; + else if (IsJoystickXboxOneElite(vendor, product)) + controller->info->type = GAMEPAD_TYPE_XBOX_ELITE; + + CONS_Debug(DBG_GAMELOGIC, M_GetText(" Type: %s\n"), G_GamepadTypeToString(controller->info->type)); + + // Change the ring LEDs on Xbox 360 controllers + // FIXME: Doesn't seem to work? + SDL_GameControllerSetPlayerIndex(controller->dev, which); + + // Check if rumble is supported + if (SDL_GameControllerHasRumble(controller->dev) == SDL_TRUE) + { + controller->info->rumble.supported = true; + CONS_Debug(DBG_GAMELOGIC, M_GetText(" Rumble supported: Yes\n")); + } + else + { + controller->info->rumble.supported = false; + CONS_Debug(DBG_GAMELOGIC, M_GetText(" Rumble supported: No\n"));; + } + + if (!controller->info->connected) + { + controller->info->connected = true; + G_OnGamepadConnect(which); + } + } + + return controller->started; +} + +// Initializes a controller +static INT32 Controller_Init(SDL_GameController **newcontroller, UINT8 which, INT32 *index) +{ + ControllerInfo *info = &controllers[which]; + SDL_GameController *controller = NULL; + INT32 device = (*index); + + if (device && SDL_IsGameController(device - 1)) + controller = SDL_GameControllerOpen(device - 1); + if (newcontroller) + (*newcontroller) = controller; + + if (controller && info->dev == controller) // don't override an active device + (*index) = I_GetControllerIndex(info->dev) + 1; + else if (controller && Controller_OpenDevice(which, device)) + { + // SDL's device indexes are unstable, so cv_usegamepad may not match + // the actual device index. So let's cheat a bit and find the device's current index. + info->lastindex = I_GetControllerIndex(info->dev) + 1; + return 1; + } + else + { + (*index) = 0; + return 0; + } + + return -1; +} + +// Changes a controller's device +static void Controller_ChangeDevice(UINT8 num) +{ + SDL_GameController *newjoy = NULL; + + if (!Controller_Init(&newjoy, num, &cv_usegamepad[num].value) && controllers[num].lastindex) + Controller_Close(num); + + I_CloseInactiveController(newjoy); +} + +static boolean Controller_IsAnyUsingDevice(SDL_GameController *dev) +{ + for (UINT8 i = 0; i < NUM_GAMEPADS; i++) + { + if (controllers[i].dev == dev) + return true; + } + + return false; +} + +static boolean Controller_IsAnyOtherUsingDevice(SDL_GameController *dev, UINT8 thisjoy) +{ + for (UINT8 i = 0; i < NUM_GAMEPADS; i++) + { + if (i == thisjoy) + continue; + else if (controllers[i].dev == dev) + return true; + } + + return false; +} + +void I_ControllerDeviceAdded(INT32 which) +{ + if (!SDL_IsGameController(which)) + return; + + SDL_GameController *newjoy = SDL_GameControllerOpen(which); + + CONS_Debug(DBG_GAMELOGIC, "Gamepad device index %d added\n", which + 1); + + // Because SDL's device index is unstable, we're going to cheat here a bit: + // For the first controller setting that is NOT active: + // 1. Set cv_usegamepadX.value to the new device index (this does not change what is written to config.cfg) + // 2. Set OTHERS' cv_usegamepadX.value to THEIR new device index, because it likely changed + // * If device doesn't exist, switch cv_usegamepad back to default value (.string) + // * BUT: If that default index is being occupied, use ANOTHER cv_usegamepad's default value! + for (UINT8 this = 0; this < NUM_GAMEPADS && newjoy; this++) + { + if ((!controllers[this].dev || !SDL_GameControllerGetAttached(controllers[this].dev)) + && !Controller_IsAnyOtherUsingDevice(newjoy, this)) // don't override a currently active device + { + cv_usegamepad[this].value = which + 1; + + // Go through every other device + for (UINT8 other = 0; other < NUM_GAMEPADS; other++) + { + if (other == this) + { + // Don't change this controller's index + continue; + } + else if (controllers[other].dev) + { + // Update this controller's index if the device is open + cv_usegamepad[other].value = I_GetControllerIndex(controllers[other].dev) + 1; + } + else if (atoi(cv_usegamepad[other].string) != controllers[this].lastindex + && atoi(cv_usegamepad[other].string) != cv_usegamepad[this].value) + { + // If the user-set index for the other controller doesn't + // match this controller's current or former internal index, + // then use the other controller's internal index + cv_usegamepad[other].value = atoi(cv_usegamepad[other].string); + } + else if (atoi(cv_usegamepad[this].string) != controllers[this].lastindex + && atoi(cv_usegamepad[this].string) != cv_usegamepad[this].value) + { + // If the user-set index for this controller doesn't match + // its current or former internal index, then use this + // controller's internal index + cv_usegamepad[other].value = atoi(cv_usegamepad[this].string); + } + else + { + // Try again + cv_usegamepad[other].value = 0; + continue; + } + + break; + } + + break; + } + } + + // Was cv_usegamepad disabled in settings? + for (UINT8 i = 0; i < NUM_GAMEPADS; i++) + { + if (!strcmp(cv_usegamepad[i].string, "0") || !cv_usegamepad[i].value) + cv_usegamepad[i].value = 0; + else if (atoi(cv_usegamepad[i].string) <= I_NumGamepads() // don't mess if we intentionally set higher than NumJoys + && cv_usegamepad[i].value) // update the cvar ONLY if a device exists + CV_SetValue(&cv_usegamepad[i], cv_usegamepad[i].value); + } + + // Update all gamepads' init states + // This is a little wasteful since cv_usegamepad already calls this, but + // we need to do this in case CV_SetValue did nothing because the string was already same. + // if the device is already active, this should do nothing, effectively. + for (UINT8 i = 0; i < NUM_GAMEPADS; i++) + { + Controller_ChangeDevice(i); + CONS_Debug(DBG_GAMELOGIC, "Controller %d device index: %d\n", i, controllers[i].lastindex); + } + + if (M_OnGamepadMenu()) + M_UpdateGamepadMenu(); + + I_CloseInactiveController(newjoy); +} + +void I_ControllerDeviceRemoved(void) +{ + for (UINT8 this = 0; this < NUM_GAMEPADS; this++) + { + if (controllers[this].dev && !SDL_GameControllerGetAttached(controllers[this].dev)) + { + CONS_Debug(DBG_GAMELOGIC, "Controller %d removed, device index: %d\n", this, controllers[this].lastindex); + G_OnGamepadDisconnect(this); + Controller_Close(this); + } + + // Update the device indexes, because they likely changed + // * If device doesn't exist, switch cv_usegamepad back to default value (.string) + // * BUT: If that default index is being occupied, use ANOTHER cv_usegamepad's default value! + if (controllers[this].dev) + cv_usegamepad[this].value = controllers[this].lastindex = I_GetControllerIndex(controllers[this].dev) + 1; + else + { + for (UINT8 other = 0; other < NUM_GAMEPADS; other++) + { + if (other == this) + continue; + + if (atoi(cv_usegamepad[this].string) != controllers[other].lastindex) + { + // Update this internal index if this user-set index + // doesn't match the other's former internal index + cv_usegamepad[this].value = atoi(cv_usegamepad[this].string); + } + else if (atoi(cv_usegamepad[other].string) != controllers[other].lastindex) + { + // Otherwise, set this internal index to the other's + // user-set index, if the other user-set index is not the + // same as the other's former internal index + cv_usegamepad[this].value = atoi(cv_usegamepad[other].string); + } + else + { + // Try again + cv_usegamepad[this].value = 0; + continue; + } + + break; + } + } + + // Was cv_usegamepad disabled in settings? + if (!strcmp(cv_usegamepad[this].string, "0")) + cv_usegamepad[this].value = 0; + else if (atoi(cv_usegamepad[this].string) <= I_NumGamepads() // don't mess if we intentionally set higher than NumJoys + && cv_usegamepad[this].value) // update the cvar ONLY if a device exists + CV_SetValue(&cv_usegamepad[this], cv_usegamepad[this].value); + + CONS_Debug(DBG_GAMELOGIC, "Controller %d device index: %d\n", this, controllers[this].lastindex); + } + + if (M_OnGamepadMenu()) + M_UpdateGamepadMenu(); +} + +// Close the controller device if there isn't any controller using it +void I_CloseInactiveController(SDL_GameController *dev) +{ + if (!Controller_IsAnyUsingDevice(dev)) + SDL_GameControllerClose(dev); +} + +// Cheat to get the device index for a game controller handle +INT32 I_GetControllerIndex(SDL_GameController *dev) +{ + INT32 i, count = SDL_NumJoysticks(); + + for (i = 0; dev && i < count; i++) + { + SDL_GameController *test = SDL_GameControllerOpen(i); + if (test && test == dev) + return i; + else + I_CloseInactiveController(test); + } + + return -1; +} + +// Changes a gamepad's device +void I_ChangeGamepad(UINT8 which) +{ + if (which >= NUM_GAMEPADS) + return; + + if (controllers[which].started) + Controller_StopRumble(which); + + Controller_ChangeDevice(which); +} + +// Returns the name of a controller from its index +const char *I_GetGamepadName(INT32 joyindex) +{ + static char joyname[256]; + joyname[0] = '\0'; + + if (SDL_WasInit(GAMEPAD_INIT_FLAGS) == GAMEPAD_INIT_FLAGS) + { + const char *tempname = SDL_GameControllerNameForIndex(joyindex - 1); + if (tempname) + strlcpy(joyname, tempname, sizeof joyname); + } + + return joyname; +} + +// Toggles a gamepad's digital axis setting +void I_SetGamepadDigital(UINT8 which, boolean enable) +{ + if (which >= NUM_GAMEPADS) + return; + + gamepads[which].digital = enable; +} + +static gamepad_t *Controller_GetFromID(SDL_JoystickID which, UINT8 *found) +{ + // Determine the joystick IDs for each current open controller + for (UINT8 i = 0; i < NUM_GAMEPADS; i++) + { + if (which == SDL_JoystickInstanceID(controllers[i].joydev)) + { + (*found) = i; + return &gamepads[i]; + } + } + + (*found) = UINT8_MAX; + + return NULL; +} + +void I_HandleControllerButtonEvent(SDL_ControllerButtonEvent evt, Uint32 type) +{ + event_t event; + + gamepad_t *gamepad = Controller_GetFromID(evt.which, &event.which); + if (gamepad == NULL) + return; + + if (type == SDL_CONTROLLERBUTTONUP) + event.type = ev_gamepad_up; + else if (type == SDL_CONTROLLERBUTTONDOWN) + event.type = ev_gamepad_down; + else + return; + +#define GAMEPAD_BUTTON_CASE(btn) \ + case SDL_CONTROLLER_BUTTON_##btn: \ + event.key = GAMEPAD_BUTTON_##btn; \ + break + + switch (evt.button) + { + GAMEPAD_BUTTON_CASE(A); + GAMEPAD_BUTTON_CASE(B); + GAMEPAD_BUTTON_CASE(X); + GAMEPAD_BUTTON_CASE(Y); + GAMEPAD_BUTTON_CASE(BACK); + GAMEPAD_BUTTON_CASE(GUIDE); + GAMEPAD_BUTTON_CASE(START); + GAMEPAD_BUTTON_CASE(LEFTSTICK); + GAMEPAD_BUTTON_CASE(RIGHTSTICK); + GAMEPAD_BUTTON_CASE(LEFTSHOULDER); + GAMEPAD_BUTTON_CASE(RIGHTSHOULDER); + GAMEPAD_BUTTON_CASE(DPAD_UP); + GAMEPAD_BUTTON_CASE(DPAD_DOWN); + GAMEPAD_BUTTON_CASE(DPAD_LEFT); + GAMEPAD_BUTTON_CASE(DPAD_RIGHT); + GAMEPAD_BUTTON_CASE(MISC1); + GAMEPAD_BUTTON_CASE(PADDLE1); + GAMEPAD_BUTTON_CASE(PADDLE2); + GAMEPAD_BUTTON_CASE(PADDLE3); + GAMEPAD_BUTTON_CASE(PADDLE4); + GAMEPAD_BUTTON_CASE(TOUCHPAD); + default: return; + } + +#undef GAMEPAD_BUTTON_CASE + + D_PostEvent(&event); +} + +void I_HandleControllerAxisEvent(SDL_ControllerAxisEvent evt) +{ + event_t event; + + gamepad_t *gamepad = Controller_GetFromID(evt.which, &event.which); + if (gamepad == NULL) + return; + +#define GAMEPAD_AXIS_CASE(btn) \ + case SDL_CONTROLLER_AXIS_##btn: \ + event.key = GAMEPAD_AXIS_##btn; \ + break + + switch (evt.axis) + { + GAMEPAD_AXIS_CASE(LEFTX); + GAMEPAD_AXIS_CASE(LEFTY); + GAMEPAD_AXIS_CASE(RIGHTX); + GAMEPAD_AXIS_CASE(RIGHTY); + GAMEPAD_AXIS_CASE(TRIGGERLEFT); + GAMEPAD_AXIS_CASE(TRIGGERRIGHT); + default: return; + } + +#undef GAMEPAD_AXIS_CASE + + event.type = ev_gamepad_axis; + event.x = evt.value; + + D_PostEvent(&event); +} + +static void Controller_StopRumble(UINT8 num) +{ + ControllerInfo *controller = &controllers[num]; + + controller->rumble.large_magnitude = 0; + controller->rumble.small_magnitude = 0; + controller->rumble.time_left = 0; + controller->rumble.expiration = 0; + + gamepad_t *gamepad = controller->info; + + gamepad->rumble.active = false; + gamepad->rumble.paused = false; + gamepad->rumble.data.large_magnitude = 0; + gamepad->rumble.data.small_magnitude = 0; + gamepad->rumble.data.duration = 0; + + if (gamepad->rumble.supported) + SDL_GameControllerRumble(controller->dev, 0, 0, 0); +} + +static void Controller_Close(UINT8 num) +{ + ControllerInfo *controller = &controllers[num]; + + // Close the game controller device + if (controller->dev) + { + Controller_StopRumble(num); + SDL_GameControllerClose(controller->dev); + } + + controller->dev = NULL; + controller->joydev = NULL; + controller->lastindex = -1; + controller->started = false; + + // Reset gamepad info + gamepad_t *gamepad = controller->info; + + if (gamepad) + { + gamepad->type = GAMEPAD_TYPE_UNKNOWN; + gamepad->connected = false; + gamepad->digital = false; + gamepad->rumble.supported = false; + + for (UINT8 i = 0; i < NUM_GAMEPAD_BUTTONS; i++) + gamepad->buttons[i] = 0; + + for (UINT8 i = 0; i < NUM_GAMEPAD_AXES; i++) + gamepad->axes[i] = 0; + } +} + +void I_ShutdownGamepads(void) +{ + for (UINT8 i = 0; i < NUM_GAMEPADS; i++) + Controller_Close(i); +} + +boolean I_RumbleSupported(void) +{ + return rumble_supported; +} + +static boolean Controller_Rumble(ControllerInfo *c) +{ + if (SDL_GameControllerRumble(c->dev, c->rumble.large_magnitude, c->rumble.small_magnitude, 0) == -1) + return false; + + return true; +} + +void I_ToggleControllerRumble(boolean unpause) +{ + if (!I_RumbleSupported() || rumble_paused == !unpause) + return; + + rumble_paused = !unpause; + + for (UINT8 i = 0; i < NUM_GAMEPADS; i++) + { + ControllerInfo *controller = &controllers[i]; + if (!controller->started || !controller->info->rumble.supported) + continue; + + if (rumble_paused) + SDL_GameControllerRumble(controller->dev, 0, 0, 0); + else if (!controller->info->rumble.paused) + { + if (!Controller_Rumble(controller)) + controller->rumble.expiration = controller->rumble.time_left = 0; + } + } +} + +void I_UpdateControllers(void) +{ + if (SDL_WasInit(GAMEPAD_INIT_FLAGS) != GAMEPAD_INIT_FLAGS) + return; + + for (UINT8 i = 0; i < NUM_GAMEPADS; i++) + { + ControllerInfo *controller = &controllers[i]; + if (!controller->started || !controller->info->rumble.supported || controller->info->rumble.paused) + continue; + + if (controller->rumble.expiration && + SDL_TICKS_PASSED(SDL_GetTicks(), controller->rumble.expiration)) + { + // Enough time has passed, so stop the effect + Controller_StopRumble(i); + } + } + + SDL_JoystickUpdate(); +} + +// Converts duration in tics to milliseconds +#define TICS_TO_MS(tics) ((INT32)(tics * (1000.0f/TICRATE))) + +boolean I_RumbleGamepad(UINT8 which, const haptic_t *effect) +{ + if (!I_RumbleSupported() || which >= NUM_GAMEPADS) + return false; + + ControllerInfo *controller = &controllers[which]; + if (!controller->started || !controller->info->rumble.supported) + return false; + + UINT16 duration = min(TICS_TO_MS(effect->duration), UINT16_MAX); + UINT16 large_magnitude = max(0, min(effect->large_magnitude, UINT16_MAX)); + UINT16 small_magnitude = max(0, min(effect->small_magnitude, UINT16_MAX)); + + CONS_Debug(DBG_GAMELOGIC, "Starting rumble effect for controller %d:\n", which); + CONS_Debug(DBG_GAMELOGIC, " Large motor magnitude: %f\n", large_magnitude / 65535.0f); + CONS_Debug(DBG_GAMELOGIC, " Small motor magnitude: %f\n", small_magnitude / 65535.0f); + + if (!duration) + CONS_Debug(DBG_GAMELOGIC, " Duration: forever\n"); + else + CONS_Debug(DBG_GAMELOGIC, " Duration: %dms\n", duration); + + controller->rumble.large_magnitude = large_magnitude; + controller->rumble.small_magnitude = small_magnitude; + + if (!rumble_paused && !Controller_Rumble(controller)) + { + Controller_StopRumble(which); + return false; + } + + controller->rumble.time_left = 0; + + if (duration) + controller->rumble.expiration = SDL_GetTicks() + duration; + else + controller->rumble.expiration = 0; + + // Update gamepad rumble info + gamepad_t *gamepad = controller->info; + + gamepad->rumble.active = true; + gamepad->rumble.paused = false; + gamepad->rumble.data.large_magnitude = effect->large_magnitude; + gamepad->rumble.data.small_magnitude = effect->small_magnitude; + gamepad->rumble.data.duration = effect->duration; + + return true; +} + +#undef TICS_TO_MS + +#define SET_MOTOR_FREQ(type) \ + if (!I_RumbleSupported() || which >= NUM_GAMEPADS) \ + return false; \ + \ + ControllerInfo *controller = &controllers[which]; \ + if (!controller->started || !controller->info->rumble.supported) \ + return false; \ + \ + gamepad_t *gamepad = controller->info; \ + if (gamepad->rumble.data.type##_magnitude == freq) \ + return true; \ + \ + UINT16 frequency = max(0, min(freq, UINT16_MAX)); \ + \ + controller->rumble.type##_magnitude = frequency; \ + \ + if (!rumble_paused && !gamepad->rumble.paused && !Controller_Rumble(controller)) \ + { \ + Controller_StopRumble(which); \ + return false; \ + } \ + \ + gamepad->rumble.data.type##_magnitude = freq; \ + gamepad->rumble.active = true; \ + return true + +boolean I_SetGamepadLargeMotorFreq(UINT8 which, fixed_t freq) +{ + SET_MOTOR_FREQ(large); +} + +boolean I_SetGamepadSmallMotorFreq(UINT8 which, fixed_t freq) +{ + SET_MOTOR_FREQ(small); +} + +void I_SetGamepadRumblePaused(UINT8 which, boolean pause) +{ + if (!I_RumbleSupported() || which >= NUM_GAMEPADS) + return; + + ControllerInfo *controller = &controllers[which]; + if (!controller->started || !controller->info->rumble.supported) + return; + + if (pause == controller->info->rumble.paused) + return; + else if (pause) + { + if (!rumble_paused) + SDL_GameControllerRumble(controller->dev, 0, 0, 0); + + if (controller->rumble.expiration) + { + controller->rumble.time_left = controller->rumble.expiration - SDL_GetTicks(); + controller->rumble.expiration = 0; + } + } + else + { + if (!rumble_paused) + SDL_GameControllerRumble(controller->dev, controller->rumble.large_magnitude, controller->rumble.small_magnitude, 0); + + if (controller->rumble.time_left) + controller->rumble.expiration = SDL_GetTicks() + controller->rumble.time_left; + } + + controller->info->rumble.paused = pause; +} + +boolean I_GetGamepadRumbleSupported(UINT8 which) +{ + if (!I_RumbleSupported() || which >= NUM_GAMEPADS) + return false; + + ControllerInfo *controller = &controllers[which]; + if (!controller->started) + return false; + + return controller->info->rumble.supported; +} + +boolean I_GetGamepadRumblePaused(UINT8 which) +{ + if (!I_RumbleSupported() || which >= NUM_GAMEPADS) + return false; + + ControllerInfo *controller = &controllers[which]; + if (!controller->started || !controller->info->rumble.supported) + return false; + + return controller->info->rumble.paused; +} + +void I_StopGamepadRumble(UINT8 which) +{ + if (!I_RumbleSupported() || which >= NUM_GAMEPADS) + return; + + ControllerInfo *controller = &controllers[which]; + if (!controller->started || !controller->info->rumble.supported) + return; + + Controller_StopRumble(which); +} +#endif diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 818d0f0c4..ee082fd5d 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -185,6 +185,7 @@ static char returnWadPath[256]; #include "../i_video.h" #include "../i_sound.h" #include "../i_system.h" +#include "../i_gamepad.h" #include "../i_threads.h" #include "../screen.h" //vid.WndParent #include "../d_net.h" @@ -193,8 +194,6 @@ static char returnWadPath[256]; #include "endtxt.h" #include "sdlmain.h" -#include "../i_joy.h" - #include "../m_argv.h" #include "../r_main.h" // Frame interpolation/uncapped @@ -212,41 +211,6 @@ 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) -{ - if (JoySet->dev) - { - SDL_JoystickClose(JoySet->dev); - } - 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 = 0; - -/** \brief SDL info about joystick 1 -*/ -SDLJoyInfo_t JoyInfo; - - -/** \brief Second joystick up and running -*/ -static INT32 joystick2_started = 0; - -/** \brief SDL inof about joystick 2 -*/ -SDLJoyInfo_t JoyInfo2; - #ifdef HAVE_TERMIOS static INT32 fdmouse2 = -1; static INT32 mouse2_started = 0; @@ -939,721 +903,17 @@ INT32 I_GetKey (void) return rc; } -// -// I_JoyScale -// -void I_JoyScale(void) -{ - Joystick.bGamepadStyle = cv_joyscale.value==0; - JoyInfo.scale = Joystick.bGamepadStyle?1:cv_joyscale.value; -} - -void I_JoyScale2(void) -{ - Joystick2.bGamepadStyle = cv_joyscale2.value==0; - JoyInfo2.scale = Joystick2.bGamepadStyle?1:cv_joyscale2.value; -} - -// Cheat to get the device index for a joystick handle -INT32 I_GetJoystickDeviceIndex(SDL_Joystick *dev) -{ - INT32 i, count = SDL_NumJoysticks(); - - for (i = 0; dev && i < count; i++) - { - SDL_Joystick *test = SDL_JoystickOpen(i); - if (test && test == dev) - return i; - else if (JoyInfo.dev != test && JoyInfo2.dev != test) - SDL_JoystickClose(test); - } - - return -1; -} - -/** \brief Joystick 1 buttons states -*/ -static UINT64 lastjoybuttons = 0; - -/** \brief Joystick 1 hats state -*/ -static UINT64 lastjoyhats = 0; - -/** \brief Shuts down joystick 1 - - - \return void - - -*/ -void I_ShutdownJoystick(void) -{ - INT32 i; - event_t event; - event.type=ev_keyup; - event.x = 0; - event.y = 0; - - lastjoybuttons = lastjoyhats = 0; - - // emulate the up of all joystick buttons - for (i=0;i= 0; i--) - { - joybuttons <<= 1; - if (SDL_JoystickGetButton(JoyInfo.dev,i)) - joybuttons |= 1; - } - - if (joybuttons != lastjoybuttons) - { - INT64 j = 1; // keep only bits that changed since last time - INT64 newbuttons = joybuttons ^ lastjoybuttons; - lastjoybuttons = joybuttons; - - for (i = 0; i < JOYBUTTONS; i++, j <<= 1) - { - if (newbuttons & j) // button changed state? - { - if (joybuttons & j) - event.type = ev_keydown; - else - event.type = ev_keyup; - event.key = KEY_JOY1 + i; - D_PostEvent(&event); - } - } - } -#endif - - for (i = JoyInfo.hats - 1; i >= 0; i--) - { - Uint8 hat = SDL_JoystickGetHat(JoyInfo.dev, i); - - if (hat & SDL_HAT_UP ) joyhats|=(UINT64)0x1<<(0 + 4*i); - if (hat & SDL_HAT_DOWN ) joyhats|=(UINT64)0x1<<(1 + 4*i); - if (hat & SDL_HAT_LEFT ) joyhats|=(UINT64)0x1<<(2 + 4*i); - if (hat & SDL_HAT_RIGHT) joyhats|=(UINT64)0x1<<(3 + 4*i); - } - - if (joyhats != lastjoyhats) - { - INT64 j = 1; // keep only bits that changed since last time - INT64 newhats = joyhats ^ lastjoyhats; - lastjoyhats = joyhats; - - for (i = 0; i < JOYHATS*4; i++, j <<= 1) - { - if (newhats & j) // hat changed state? - { - if (joyhats & j) - event.type = ev_keydown; - else - event.type = ev_keyup; - event.key = KEY_HAT1 + i; - D_PostEvent(&event); - } - } - } - -#if 0 - // send joystick axis positions - event.type = ev_joystick; - - for (i = JOYAXISSET - 1; i >= 0; i--) - { - event.key = i; - if (i*2 + 1 <= JoyInfo.axises) - axisx = SDL_JoystickGetAxis(JoyInfo.dev, i*2 + 0); - else axisx = 0; - if (i*2 + 2 <= JoyInfo.axises) - axisy = SDL_JoystickGetAxis(JoyInfo.dev, i*2 + 1); - else axisy = 0; - - - // -32768 to 32767 - axisx = axisx/32; - axisy = axisy/32; - - - if (Joystick.bGamepadStyle) - { - // gamepad control type, on or off, live or die - if (axisx < -(JOYAXISRANGE/2)) - event.x = -1; - else if (axisx > (JOYAXISRANGE/2)) - event.x = 1; - else event.x = 0; - if (axisy < -(JOYAXISRANGE/2)) - event.y = -1; - else if (axisy > (JOYAXISRANGE/2)) - event.y = 1; - else event.y = 0; - } - else - { - - axisx = JoyInfo.scale?((axisx/JoyInfo.scale)*JoyInfo.scale):axisx; - axisy = JoyInfo.scale?((axisy/JoyInfo.scale)*JoyInfo.scale):axisy; - -#ifdef SDL_JDEADZONE - if (-SDL_JDEADZONE <= axisx && axisx <= SDL_JDEADZONE) axisx = 0; - if (-SDL_JDEADZONE <= axisy && axisy <= SDL_JDEADZONE) axisy = 0; -#endif - - // analog control style , just send the raw data - event.x = axisx; // x axis - event.y = axisy; // y axis - } - D_PostEvent(&event); - } -#endif -} - -/** \brief Open joystick handle - - \param fname name of joystick - - \return axises - - -*/ -static int joy_open(int joyindex) -{ - SDL_Joystick *newdev = NULL; - int num_joy = 0; - - if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) - { - CONS_Printf(M_GetText("Joystick subsystem not started\n")); - return -1; - } - - if (joyindex <= 0) - return -1; - - num_joy = SDL_NumJoysticks(); - - if (num_joy == 0) - { - CONS_Printf("%s", M_GetText("Found no joysticks on this system\n")); - return -1; - } - - newdev = SDL_JoystickOpen(joyindex-1); - - // Handle the edge case where the device <-> joystick index assignment can change due to hotplugging - // This indexing is SDL's responsibility and there's not much we can do about it. - // - // Example: - // 1. Plug Controller A -> Index 0 opened - // 2. Plug Controller B -> Index 1 opened - // 3. Unplug Controller A -> Index 0 closed, Index 1 active - // 4. Unplug Controller B -> Index 0 inactive, Index 1 closed - // 5. Plug Controller B -> Index 0 opened - // 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B - if (JoyInfo.dev) - { - if (JoyInfo.dev == newdev // same device, nothing to do - || (newdev == NULL && SDL_JoystickGetAttached(JoyInfo.dev))) // we failed, but already have a working device - return JoyInfo.axises; - // Else, we're changing devices, so send neutral joy events - CONS_Debug(DBG_GAMELOGIC, "Joystick1 device is changing; resetting events...\n"); - I_ShutdownJoystick(); - } - - JoyInfo.dev = newdev; - - if (JoyInfo.dev == NULL) - { - CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick1: Couldn't open device - %s\n"), SDL_GetError()); - return -1; - } - else - { - CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick1: %s\n"), SDL_JoystickName(JoyInfo.dev)); - JoyInfo.axises = SDL_JoystickNumAxes(JoyInfo.dev); - if (JoyInfo.axises > JOYAXISSET*2) - JoyInfo.axises = JOYAXISSET*2; - /* if (joyaxes<2) - { - I_OutputMsg("Not enought axes?\n"); - return 0; - }*/ - - JoyInfo.buttons = SDL_JoystickNumButtons(JoyInfo.dev); - if (JoyInfo.buttons > JOYBUTTONS) - JoyInfo.buttons = JOYBUTTONS; - - JoyInfo.hats = SDL_JoystickNumHats(JoyInfo.dev); - if (JoyInfo.hats > JOYHATS) - JoyInfo.hats = JOYHATS; - - JoyInfo.balls = SDL_JoystickNumBalls(JoyInfo.dev); - - //Joystick.bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo.dev), "pad"); - - return JoyInfo.axises; - } -} - -//Joystick2 - -/** \brief Joystick 2 buttons states -*/ -static UINT64 lastjoy2buttons = 0; - -/** \brief Joystick 2 hats state -*/ -static UINT64 lastjoy2hats = 0; - -/** \brief Shuts down joystick 2 - - - \return void -*/ -void I_ShutdownJoystick2(void) -{ - INT32 i; - event_t event; - event.type = ev_keyup; - event.x = 0; - event.y = 0; - - lastjoy2buttons = lastjoy2hats = 0; - - // emulate the up of all joystick buttons - for (i = 0; i < JOYBUTTONS; i++) - { - event.key = KEY_2JOY1 + i; - D_PostEvent(&event); - } - - // emulate the up of all joystick hats - for (i = 0; i < JOYHATS*4; i++) - { - event.key = KEY_2HAT1 + i; - D_PostEvent(&event); - } - - // reset joystick position - event.type = ev_joystick2; - for (i = 0; i < JOYAXISSET; i++) - { - event.key = i; - D_PostEvent(&event); - } - - joystick2_started = 0; - JoyReset(&JoyInfo2); - - // don't shut down the subsystem here, because hotplugging -} - -void I_GetJoystick2Events(void) -{ - static event_t event = {0,0,0,0,false}; - INT32 i = 0; - UINT64 joyhats = 0; -#if 0 - INT64 joybuttons = 0; - INT32 axisx, axisy; -#endif - - if (!joystick2_started) - return; - - if (!JoyInfo2.dev) //I_ShutdownJoystick2(); - return; - - -#if 0 - //faB: look for as much buttons as g_input code supports, - // we don't use the others - for (i = JoyInfo2.buttons - 1; i >= 0; i--) - { - joybuttons <<= 1; - if (SDL_JoystickGetButton(JoyInfo2.dev,i)) - joybuttons |= 1; - } - - if (joybuttons != lastjoy2buttons) - { - INT64 j = 1; // keep only bits that changed since last time - INT64 newbuttons = joybuttons ^ lastjoy2buttons; - lastjoy2buttons = joybuttons; - - for (i = 0; i < JOYBUTTONS; i++, j <<= 1) - { - if (newbuttons & j) // button changed state? - { - if (joybuttons & j) - event.type = ev_keydown; - else - event.type = ev_keyup; - event.key = KEY_2JOY1 + i; - D_PostEvent(&event); - } - } - } -#endif - - for (i = JoyInfo2.hats - 1; i >= 0; i--) - { - Uint8 hat = SDL_JoystickGetHat(JoyInfo2.dev, i); - - if (hat & SDL_HAT_UP ) joyhats|=(UINT64)0x1<<(0 + 4*i); - if (hat & SDL_HAT_DOWN ) joyhats|=(UINT64)0x1<<(1 + 4*i); - if (hat & SDL_HAT_LEFT ) joyhats|=(UINT64)0x1<<(2 + 4*i); - if (hat & SDL_HAT_RIGHT) joyhats|=(UINT64)0x1<<(3 + 4*i); - } - - if (joyhats != lastjoy2hats) - { - INT64 j = 1; // keep only bits that changed since last time - INT64 newhats = joyhats ^ lastjoy2hats; - lastjoy2hats = joyhats; - - for (i = 0; i < JOYHATS*4; i++, j <<= 1) - { - if (newhats & j) // hat changed state? - { - if (joyhats & j) - event.type = ev_keydown; - else - event.type = ev_keyup; - event.key = KEY_2HAT1 + i; - D_PostEvent(&event); - } - } - } - -#if 0 - // send joystick axis positions - event.type = ev_joystick2; - - for (i = JOYAXISSET - 1; i >= 0; i--) - { - event.key = i; - if (i*2 + 1 <= JoyInfo2.axises) - axisx = SDL_JoystickGetAxis(JoyInfo2.dev, i*2 + 0); - else axisx = 0; - if (i*2 + 2 <= JoyInfo2.axises) - axisy = SDL_JoystickGetAxis(JoyInfo2.dev, i*2 + 1); - else axisy = 0; - - // -32768 to 32767 - axisx = axisx/32; - axisy = axisy/32; - - if (Joystick2.bGamepadStyle) - { - // gamepad control type, on or off, live or die - if (axisx < -(JOYAXISRANGE/2)) - event.x = -1; - else if (axisx > (JOYAXISRANGE/2)) - event.x = 1; - else - event.x = 0; - if (axisy < -(JOYAXISRANGE/2)) - event.y = -1; - else if (axisy > (JOYAXISRANGE/2)) - event.y = 1; - else - event.y = 0; - } - else - { - - axisx = JoyInfo2.scale?((axisx/JoyInfo2.scale)*JoyInfo2.scale):axisx; - axisy = JoyInfo2.scale?((axisy/JoyInfo2.scale)*JoyInfo2.scale):axisy; - -#ifdef SDL_JDEADZONE - if (-SDL_JDEADZONE <= axisx && axisx <= SDL_JDEADZONE) axisx = 0; - if (-SDL_JDEADZONE <= axisy && axisy <= SDL_JDEADZONE) axisy = 0; -#endif - - // analog control style , just send the raw data - event.x = axisx; // x axis - event.y = axisy; // y axis - } - D_PostEvent(&event); - } -#endif -} - -/** \brief Open joystick handle - - \param fname name of joystick - - \return axises - - -*/ -static int joy_open2(int joyindex) -{ - SDL_Joystick *newdev = NULL; - int num_joy = 0; - - if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) - { - CONS_Printf(M_GetText("Joystick subsystem not started\n")); - return -1; - } - - if (joyindex <= 0) - return -1; - - num_joy = SDL_NumJoysticks(); - - if (num_joy == 0) - { - CONS_Printf("%s", M_GetText("Found no joysticks on this system\n")); - return -1; - } - - newdev = SDL_JoystickOpen(joyindex-1); - - // Handle the edge case where the device <-> joystick index assignment can change due to hotplugging - // This indexing is SDL's responsibility and there's not much we can do about it. - // - // Example: - // 1. Plug Controller A -> Index 0 opened - // 2. Plug Controller B -> Index 1 opened - // 3. Unplug Controller A -> Index 0 closed, Index 1 active - // 4. Unplug Controller B -> Index 0 inactive, Index 1 closed - // 5. Plug Controller B -> Index 0 opened - // 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B - if (JoyInfo2.dev) - { - if (JoyInfo2.dev == newdev // same device, nothing to do - || (newdev == NULL && SDL_JoystickGetAttached(JoyInfo2.dev))) // we failed, but already have a working device - return JoyInfo.axises; - // Else, we're changing devices, so send neutral joy events - CONS_Debug(DBG_GAMELOGIC, "Joystick2 device is changing; resetting events...\n"); - I_ShutdownJoystick2(); - } - - JoyInfo2.dev = newdev; - - if (JoyInfo2.dev == NULL) - { - CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick2: couldn't open device - %s\n"), SDL_GetError()); - return -1; - } - else - { - CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick2: %s\n"), SDL_JoystickName(JoyInfo2.dev)); - JoyInfo2.axises = SDL_JoystickNumAxes(JoyInfo2.dev); - if (JoyInfo2.axises > JOYAXISSET*2) - JoyInfo2.axises = JOYAXISSET*2; -/* if (joyaxes<2) - { - I_OutputMsg("Not enought axes?\n"); - return 0; - }*/ - - JoyInfo2.buttons = SDL_JoystickNumButtons(JoyInfo2.dev); - if (JoyInfo2.buttons > JOYBUTTONS) - JoyInfo2.buttons = JOYBUTTONS; - - JoyInfo2.hats = SDL_JoystickNumHats(JoyInfo2.dev); - if (JoyInfo2.hats > JOYHATS) - JoyInfo2.hats = JOYHATS; - - JoyInfo2.balls = SDL_JoystickNumBalls(JoyInfo2.dev); - - //Joystick.bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo2.dev), "pad"); - - return JoyInfo2.axises; - } -} - -// -// I_InitJoystick -// -void I_InitJoystick(void) -{ - SDL_Joystick *newjoy = NULL; - - //I_ShutdownJoystick(); - if (M_CheckParm("-nojoy")) - return; - - if (M_CheckParm("-noxinput")) - SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); - - if (M_CheckParm("-nohidapi")) - SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE); - - if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) - { - CONS_Printf("I_InitJoystick()...\n"); - - if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) - { - CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError()); - return; - } - } - - if (cv_usejoystick.value) - newjoy = SDL_JoystickOpen(cv_usejoystick.value-1); - - if (newjoy && JoyInfo2.dev == newjoy) // don't override an active device - cv_usejoystick.value = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; - else if (newjoy && joy_open(cv_usejoystick.value) != -1) - { - // SDL's device indexes are unstable, so cv_usejoystick may not match - // the actual device index. So let's cheat a bit and find the device's current index. - JoyInfo.oldjoy = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; - joystick_started = 1; - } - else - { - if (JoyInfo.oldjoy) - I_ShutdownJoystick(); - cv_usejoystick.value = 0; - joystick_started = 0; - } - - if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy) - SDL_JoystickClose(newjoy); -} - -void I_InitJoystick2(void) -{ - SDL_Joystick *newjoy = NULL; - - //I_ShutdownJoystick2(); - if (M_CheckParm("-nojoy")) - return; - - if (M_CheckParm("-noxinput")) - SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); - - if (M_CheckParm("-nohidapi")) - SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE); - - if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) - { - CONS_Printf("I_InitJoystick2()...\n"); - - if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) - { - CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError()); - return; - } - } - - if (cv_usejoystick2.value) - newjoy = SDL_JoystickOpen(cv_usejoystick2.value-1); - - if (newjoy && JoyInfo.dev == newjoy) // don't override an active device - cv_usejoystick2.value = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; - else if (newjoy && joy_open2(cv_usejoystick2.value) != -1) - { - // SDL's device indexes are unstable, so cv_usejoystick may not match - // the actual device index. So let's cheat a bit and find the device's current index. - JoyInfo2.oldjoy = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; - joystick2_started = 1; - } - else - { - if (JoyInfo2.oldjoy) - I_ShutdownJoystick2(); - cv_usejoystick2.value = 0; - joystick2_started = 0; - } - - if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy) - SDL_JoystickClose(newjoy); -} - static void I_ShutdownInput(void) { - // Yes, the name is misleading: these send neutral events to - // clean up the unplugged joystick's input - // Note these methods are internal to this file, not called elsewhere. - I_ShutdownJoystick(); - I_ShutdownJoystick2(); + I_ShutdownGamepads(); - if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) + if (SDL_WasInit(GAMEPAD_INIT_FLAGS) == GAMEPAD_INIT_FLAGS) { - CONS_Printf("Shutting down joy system\n"); - SDL_QuitSubSystem(SDL_INIT_JOYSTICK); - I_OutputMsg("I_Joystick: SDL's Joystick system has been shutdown\n"); + CONS_Printf("Shutting down game controller subsystems\n"); + SDL_QuitSubSystem(GAMEPAD_INIT_FLAGS); } } -INT32 I_NumJoys(void) -{ - INT32 numjoy = 0; - if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) - numjoy = SDL_NumJoysticks(); - return numjoy; -} - -static char joyname[255]; // joystick name is straight from the driver - -const char *I_GetJoyName(INT32 joyindex) -{ - const char *tempname = NULL; - joyname[0] = 0; - joyindex--; //SDL's Joystick System starts at 0, not 1 - if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) - { - tempname = SDL_JoystickNameForIndex(joyindex); - if (tempname) - strncpy(joyname, tempname, 255); - } - return joyname; -} - #ifndef NOMUMBLE #ifdef HAVE_MUMBLE // Best Mumble positional audio settings: @@ -2113,23 +1373,6 @@ void I_StartupMouse2(void) #endif } -// -// I_Tactile -// -void I_Tactile(FFType pFFType, const JoyFF_t *FFEffect) -{ - // UNUSED. - (void)pFFType; - (void)FFEffect; -} - -void I_Tactile2(FFType pFFType, const JoyFF_t *FFEffect) -{ - // UNUSED. - (void)pFFType; - (void)FFEffect; -} - /** \brief empty ticcmd for player 1 */ static ticcmd_t emptycmd; diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 6e971a5d8..81b20b51e 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -66,7 +66,7 @@ #include "../m_menu.h" #include "../d_main.h" #include "../s_sound.h" -#include "../i_joy.h" +#include "../i_gamepad.h" #include "../st_stuff.h" #include "../hu_stuff.h" #include "../g_game.h" @@ -449,51 +449,10 @@ static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText) static void VID_Command_Info_f (void) { -#if 0 - SDL2STUB(); -#else -#if 0 - const SDL_VideoInfo *videoInfo; - videoInfo = SDL_GetVideoInfo(); //Alam: Double-Check - if (videoInfo) - { - CONS_Printf("%s", M_GetText("Video Interface Capabilities:\n")); - if (videoInfo->hw_available) - CONS_Printf("%s", M_GetText(" Hardware surfaces\n")); - if (videoInfo->wm_available) - CONS_Printf("%s", M_GetText(" Window manager\n")); - //UnusedBits1 :6 - //UnusedBits2 :1 - if (videoInfo->blit_hw) - CONS_Printf("%s", M_GetText(" Accelerated blits HW-2-HW\n")); - if (videoInfo->blit_hw_CC) - CONS_Printf("%s", M_GetText(" Accelerated blits HW-2-HW with Colorkey\n")); - if (videoInfo->wm_available) - CONS_Printf("%s", M_GetText(" Accelerated blits HW-2-HW with Alpha\n")); - if (videoInfo->blit_sw) - { - CONS_Printf("%s", M_GetText(" Accelerated blits SW-2-HW\n")); - if (!M_CheckParm("-noblit")) videoblitok = SDL_TRUE; - } - if (videoInfo->blit_sw_CC) - CONS_Printf("%s", M_GetText(" Accelerated blits SW-2-HW with Colorkey\n")); - if (videoInfo->blit_sw_A) - CONS_Printf("%s", M_GetText(" Accelerated blits SW-2-HW with Alpha\n")); - if (videoInfo->blit_fill) - CONS_Printf("%s", M_GetText(" Accelerated Color filling\n")); - //UnusedBits3 :16 - if (videoInfo->video_mem) - CONS_Printf(M_GetText(" There is %i KB of video memory\n"), videoInfo->video_mem); - else - CONS_Printf("%s", M_GetText(" There no video memory for SDL\n")); - //*vfmt - } -#else if (!M_CheckParm("-noblit")) videoblitok = SDL_TRUE; -#endif + SurfaceInfo(bufSurface, M_GetText("Current Engine Mode")); SurfaceInfo(vidSurface, M_GetText("Current Video Mode")); -#endif } static void VID_Command_ModeList_f(void) @@ -528,61 +487,6 @@ static void VID_Command_Mode_f (void) setmodeneeded = modenum+1; // request vid mode change } -static inline void SDLJoyRemap(event_t *event) -{ - (void)event; -} - -static INT32 SDLJoyAxis(const Sint16 axis, evtype_t which) -{ - // -32768 to 32767 - INT32 raxis = axis/32; - if (which == ev_joystick) - { - if (Joystick.bGamepadStyle) - { - // gamepad control type, on or off, live or die - if (raxis < -(JOYAXISRANGE/2)) - raxis = -1; - else if (raxis > (JOYAXISRANGE/2)) - raxis = 1; - else - raxis = 0; - } - else - { - raxis = JoyInfo.scale!=1?((raxis/JoyInfo.scale)*JoyInfo.scale):raxis; - -#ifdef SDL_JDEADZONE - if (-SDL_JDEADZONE <= raxis && raxis <= SDL_JDEADZONE) - raxis = 0; -#endif - } - } - else if (which == ev_joystick2) - { - if (Joystick2.bGamepadStyle) - { - // gamepad control type, on or off, live or die - if (raxis < -(JOYAXISRANGE/2)) - raxis = -1; - else if (raxis > (JOYAXISRANGE/2)) - raxis = 1; - else raxis = 0; - } - else - { - raxis = JoyInfo2.scale!=1?((raxis/JoyInfo2.scale)*JoyInfo2.scale):raxis; - -#ifdef SDL_JDEADZONE - if (-SDL_JDEADZONE <= raxis && raxis <= SDL_JDEADZONE) - raxis = 0; -#endif - } - } - return raxis; -} - static void Impl_HandleWindowEvent(SDL_WindowEvent evt) { static SDL_bool firsttimeonmouse = SDL_TRUE; @@ -614,13 +518,13 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt) // Tell game we got focus back, resume music if necessary window_notinfocus = false; if (!paused) - S_ResumeAudio(); //resume it + S_ResumeAudio(); - if (!firsttimeonmouse) - { - if (cv_usemouse.value) I_StartupMouse(); - } - //else firsttimeonmouse = SDL_FALSE; + I_ToggleControllerRumble(true); + P_UnpauseRumble(NULL); + + if (!firsttimeonmouse && cv_usemouse.value) + I_StartupMouse(); if (USE_MOUSEINPUT && !IgnoreMouse()) SDLdoGrabMouse(); @@ -629,43 +533,45 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt) { // Tell game we lost focus, pause music window_notinfocus = true; - if (! cv_playmusicifunfocused.value) + + if (!cv_playmusicifunfocused.value) S_PauseAudio(); - if (! cv_playsoundsifunfocused.value) + if (!cv_playsoundsifunfocused.value) S_StopSounds(); if (!disable_mouse) - { SDLforceUngrabMouse(); - } + memset(gamekeydown, 0, NUMKEYS); // TODO this is a scary memset - if (MOUSE_MENU) - { - SDLdoUngrabMouse(); - } - } + I_ToggleControllerRumble(false); + if (P_AutoPause()) + P_PauseRumble(NULL); + if (MOUSE_MENU) + SDLdoUngrabMouse(); + } } static void Impl_HandleKeyboardEvent(SDL_KeyboardEvent evt, Uint32 type) { event_t event; + if (type == SDL_KEYUP) - { event.type = ev_keyup; - } else if (type == SDL_KEYDOWN) - { event.type = ev_keydown; - } else - { return; - } + event.key = Impl_SDL_Scancode_To_Keycode(evt.keysym.scancode); + if (!event.key) + return; + event.repeated = (evt.repeat != 0); - if (event.key) D_PostEvent(&event); + event.which = 0; + + D_PostEvent(&event); } static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt) @@ -730,32 +636,35 @@ static void Impl_HandleMouseButtonEvent(SDL_MouseButtonEvent evt, Uint32 type) if (SDL_GetMouseFocus() != window || IgnoreMouse()) return; - /// \todo inputEvent.button.which if (USE_MOUSEINPUT) { if (type == SDL_MOUSEBUTTONUP) - { event.type = ev_keyup; - } else if (type == SDL_MOUSEBUTTONDOWN) - { event.type = ev_keydown; - } - else return; - if (evt.button == SDL_BUTTON_MIDDLE) - event.key = KEY_MOUSE1+2; - else if (evt.button == SDL_BUTTON_RIGHT) - event.key = KEY_MOUSE1+1; - else if (evt.button == SDL_BUTTON_LEFT) - event.key = KEY_MOUSE1; - else if (evt.button == SDL_BUTTON_X1) - event.key = KEY_MOUSE1+3; - else if (evt.button == SDL_BUTTON_X2) - event.key = KEY_MOUSE1+4; - if (event.type == ev_keyup || event.type == ev_keydown) + else + return; + + switch (evt.button) { - D_PostEvent(&event); + case SDL_BUTTON_LEFT: + event.key = KEY_MOUSE1+0; + break; + case SDL_BUTTON_RIGHT: + event.key = KEY_MOUSE1+1; + break; + case SDL_BUTTON_MIDDLE: + event.key = KEY_MOUSE1+2; + break; + case SDL_BUTTON_X1: + event.key = KEY_MOUSE1+3; + break; + case SDL_BUTTON_X2: + event.key = KEY_MOUSE1+4; + break; } + + D_PostEvent(&event); } } @@ -786,111 +695,6 @@ static void Impl_HandleMouseWheelEvent(SDL_MouseWheelEvent evt) } } -static void Impl_HandleJoystickAxisEvent(SDL_JoyAxisEvent evt) -{ - event_t event; - SDL_JoystickID joyid[2]; - - // Determine the Joystick IDs for each current open joystick - joyid[0] = SDL_JoystickInstanceID(JoyInfo.dev); - joyid[1] = SDL_JoystickInstanceID(JoyInfo2.dev); - - evt.axis++; - event.key = event.x = event.y = INT32_MAX; - - if (evt.which == joyid[0]) - { - event.type = ev_joystick; - } - else if (evt.which == joyid[1]) - { - event.type = ev_joystick2; - } - else return; - //axis - if (evt.axis > JOYAXISSET*2) - return; - //vaule - if (evt.axis%2) - { - event.key = evt.axis / 2; - event.x = SDLJoyAxis(evt.value, event.type); - } - else - { - evt.axis--; - event.key = evt.axis / 2; - event.y = SDLJoyAxis(evt.value, event.type); - } - D_PostEvent(&event); -} - -#if 0 -static void Impl_HandleJoystickHatEvent(SDL_JoyHatEvent evt) -{ - event_t event; - SDL_JoystickID joyid[2]; - - // Determine the Joystick IDs for each current open joystick - joyid[0] = SDL_JoystickInstanceID(JoyInfo.dev); - joyid[1] = SDL_JoystickInstanceID(JoyInfo2.dev); - - if (evt.hat >= JOYHATS) - return; // ignore hats with too high an index - - if (evt.which == joyid[0]) - { - event.key = KEY_HAT1 + (evt.hat*4); - } - else if (evt.which == joyid[1]) - { - event.key = KEY_2HAT1 + (evt.hat*4); - } - else return; - - // NOTE: UNFINISHED -} -#endif - -static void Impl_HandleJoystickButtonEvent(SDL_JoyButtonEvent evt, Uint32 type) -{ - event_t event; - SDL_JoystickID joyid[2]; - - // Determine the Joystick IDs for each current open joystick - joyid[0] = SDL_JoystickInstanceID(JoyInfo.dev); - joyid[1] = SDL_JoystickInstanceID(JoyInfo2.dev); - - if (evt.which == joyid[0]) - { - event.key = KEY_JOY1; - } - else if (evt.which == joyid[1]) - { - event.key = KEY_2JOY1; - } - else return; - if (type == SDL_JOYBUTTONUP) - { - event.type = ev_keyup; - } - else if (type == SDL_JOYBUTTONDOWN) - { - event.type = ev_keydown; - } - else return; - if (evt.button < JOYBUTTONS) - { - event.key += evt.button; - } - else return; - - SDLJoyRemap(&event); - if (event.type != ev_console) D_PostEvent(&event); -} - - - void I_GetEvent(void) { SDL_Event evt; @@ -928,147 +732,18 @@ void I_GetEvent(void) case SDL_MOUSEWHEEL: Impl_HandleMouseWheelEvent(evt.wheel); break; - case SDL_JOYAXISMOTION: - Impl_HandleJoystickAxisEvent(evt.jaxis); + case SDL_CONTROLLERAXISMOTION: + I_HandleControllerAxisEvent(evt.caxis); break; -#if 0 - case SDL_JOYHATMOTION: - Impl_HandleJoystickHatEvent(evt.jhat) + case SDL_CONTROLLERBUTTONUP: + case SDL_CONTROLLERBUTTONDOWN: + I_HandleControllerButtonEvent(evt.cbutton, evt.type); break; -#endif - case SDL_JOYBUTTONUP: - case SDL_JOYBUTTONDOWN: - Impl_HandleJoystickButtonEvent(evt.jbutton, evt.type); + case SDL_CONTROLLERDEVICEADDED: + I_ControllerDeviceAdded(evt.cdevice.which); break; - case SDL_JOYDEVICEADDED: - { - SDL_Joystick *newjoy = SDL_JoystickOpen(evt.jdevice.which); - - CONS_Debug(DBG_GAMELOGIC, "Joystick device index %d added\n", evt.jdevice.which + 1); - - // Because SDL's device index is unstable, we're going to cheat here a bit: - // For the first joystick setting that is NOT active: - // 1. Set cv_usejoystickX.value to the new device index (this does not change what is written to config.cfg) - // 2. Set OTHERS' cv_usejoystickX.value to THEIR new device index, because it likely changed - // * If device doesn't exist, switch cv_usejoystick back to default value (.string) - // * BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value! - if (newjoy && (!JoyInfo.dev || !SDL_JoystickGetAttached(JoyInfo.dev)) - && JoyInfo2.dev != newjoy) // don't override a currently active device - { - cv_usejoystick.value = evt.jdevice.which + 1; - - if (JoyInfo2.dev) - cv_usejoystick2.value = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; - else if (atoi(cv_usejoystick2.string) != JoyInfo.oldjoy - && atoi(cv_usejoystick2.string) != cv_usejoystick.value) - cv_usejoystick2.value = atoi(cv_usejoystick2.string); - else if (atoi(cv_usejoystick.string) != JoyInfo.oldjoy - && atoi(cv_usejoystick.string) != cv_usejoystick.value) - cv_usejoystick2.value = atoi(cv_usejoystick.string); - else // we tried... - cv_usejoystick2.value = 0; - } - else if (newjoy && (!JoyInfo2.dev || !SDL_JoystickGetAttached(JoyInfo2.dev)) - && JoyInfo.dev != newjoy) // don't override a currently active device - { - cv_usejoystick2.value = evt.jdevice.which + 1; - - if (JoyInfo.dev) - cv_usejoystick.value = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; - else if (atoi(cv_usejoystick.string) != JoyInfo2.oldjoy - && atoi(cv_usejoystick.string) != cv_usejoystick2.value) - cv_usejoystick.value = atoi(cv_usejoystick.string); - else if (atoi(cv_usejoystick2.string) != JoyInfo2.oldjoy - && atoi(cv_usejoystick2.string) != cv_usejoystick2.value) - cv_usejoystick.value = atoi(cv_usejoystick2.string); - else // we tried... - cv_usejoystick.value = 0; - } - - // Was cv_usejoystick disabled in settings? - if (!strcmp(cv_usejoystick.string, "0") || !cv_usejoystick.value) - cv_usejoystick.value = 0; - else if (atoi(cv_usejoystick.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys - && cv_usejoystick.value) // update the cvar ONLY if a device exists - CV_SetValue(&cv_usejoystick, cv_usejoystick.value); - - if (!strcmp(cv_usejoystick2.string, "0") || !cv_usejoystick2.value) - cv_usejoystick2.value = 0; - else if (atoi(cv_usejoystick2.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys - && cv_usejoystick2.value) // update the cvar ONLY if a device exists - CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); - - // Update all joysticks' init states - // This is a little wasteful since cv_usejoystick already calls this, but - // we need to do this in case CV_SetValue did nothing because the string was already same. - // if the device is already active, this should do nothing, effectively. - I_InitJoystick(); - I_InitJoystick2(); - - CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index: %d\n", JoyInfo.oldjoy); - CONS_Debug(DBG_GAMELOGIC, "Joystick2 device index: %d\n", JoyInfo2.oldjoy); - - // update the menu - if (currentMenu == &OP_JoystickSetDef) - M_SetupJoystickMenu(0); - - if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy) - SDL_JoystickClose(newjoy); - } - break; - case SDL_JOYDEVICEREMOVED: - if (JoyInfo.dev && !SDL_JoystickGetAttached(JoyInfo.dev)) - { - CONS_Debug(DBG_GAMELOGIC, "Joystick1 removed, device index: %d\n", JoyInfo.oldjoy); - I_ShutdownJoystick(); - } - - if (JoyInfo2.dev && !SDL_JoystickGetAttached(JoyInfo2.dev)) - { - CONS_Debug(DBG_GAMELOGIC, "Joystick2 removed, device index: %d\n", JoyInfo2.oldjoy); - I_ShutdownJoystick2(); - } - - // Update the device indexes, because they likely changed - // * If device doesn't exist, switch cv_usejoystick back to default value (.string) - // * BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value! - if (JoyInfo.dev) - cv_usejoystick.value = JoyInfo.oldjoy = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; - else if (atoi(cv_usejoystick.string) != JoyInfo2.oldjoy) - cv_usejoystick.value = atoi(cv_usejoystick.string); - else if (atoi(cv_usejoystick2.string) != JoyInfo2.oldjoy) - cv_usejoystick.value = atoi(cv_usejoystick2.string); - else // we tried... - cv_usejoystick.value = 0; - - if (JoyInfo2.dev) - cv_usejoystick2.value = JoyInfo2.oldjoy = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; - else if (atoi(cv_usejoystick2.string) != JoyInfo.oldjoy) - cv_usejoystick2.value = atoi(cv_usejoystick2.string); - else if (atoi(cv_usejoystick.string) != JoyInfo.oldjoy) - cv_usejoystick2.value = atoi(cv_usejoystick.string); - else // we tried... - cv_usejoystick2.value = 0; - - // Was cv_usejoystick disabled in settings? - if (!strcmp(cv_usejoystick.string, "0")) - cv_usejoystick.value = 0; - else if (atoi(cv_usejoystick.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys - && cv_usejoystick.value) // update the cvar ONLY if a device exists - CV_SetValue(&cv_usejoystick, cv_usejoystick.value); - - if (!strcmp(cv_usejoystick2.string, "0")) - cv_usejoystick2.value = 0; - else if (atoi(cv_usejoystick2.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys - && cv_usejoystick2.value) // update the cvar ONLY if a device exists - CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); - - CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index: %d\n", JoyInfo.oldjoy); - CONS_Debug(DBG_GAMELOGIC, "Joystick2 device index: %d\n", JoyInfo2.oldjoy); - - // update the menu - if (currentMenu == &OP_JoystickSetDef) - M_SetupJoystickMenu(0); + case SDL_CONTROLLERDEVICEREMOVED: + I_ControllerDeviceRemoved(); break; case SDL_QUIT: LUA_HookBool(true, HOOK(GameQuit)); @@ -1086,6 +761,7 @@ void I_GetEvent(void) //SDL_memset(&event, 0, sizeof(event_t)); event.type = ev_mouse; event.key = 0; + event.which = 0; event.x = (INT32)lround(mousemovex * ((float)wwidth / (float)realwidth)); event.y = (INT32)lround(mousemovey * ((float)wheight / (float)realheight)); D_PostEvent(&event); @@ -1124,15 +800,9 @@ void I_OsPolling(void) if (consolevent) I_GetConsoleEvents(); - if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) - { - SDL_JoystickUpdate(); - I_GetJoystickEvents(); - I_GetJoystick2Events(); - } + I_UpdateControllers(); I_GetMouseEvents(); - I_GetEvent(); mod = SDL_GetModState(); diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h index 6b6e79d97..bb178b233 100644 --- a/src/sdl/sdlmain.h +++ b/src/sdl/sdlmain.h @@ -23,59 +23,41 @@ extern SDL_bool consolevent; extern SDL_bool framebuffer; #include "../m_fixed.h" +#include "../i_gamepad.h" -// SDL2 stub macro -#ifdef _MSC_VER -#define SDL2STUB() CONS_Printf("SDL2: stubbed: %s:%d\n", __FUNCTION__, __LINE__) -#else -#define SDL2STUB() CONS_Printf("SDL2: stubbed: %s:%d\n", __func__, __LINE__) -#endif - -// So m_menu knows whether to store cv_usejoystick value or string -#define JOYSTICK_HOTPLUG - -/** \brief The JoyInfo_s struct - - info about joystick -*/ -typedef struct SDLJoyInfo_s +// SDL info about all controllers +typedef struct { - /// Joystick handle - SDL_Joystick *dev; - /// number of old joystick - int oldjoy; - /// number of axies - int axises; - /// scale of axises - INT32 scale; - /// number of buttons - int buttons; - /// number of hats - int hats; - /// number of balls - int balls; + boolean started; // started + int lastindex; // last gamepad ID -} SDLJoyInfo_t; + SDL_GameController *dev; + SDL_Joystick *joydev; -/** \brief SDL info about joystick 1 -*/ -extern SDLJoyInfo_t JoyInfo; + gamepad_t *info; // pointer to gamepad info -/** \brief joystick axis deadzone -*/ -#define SDL_JDEADZONE 153 -#undef SDL_JDEADZONE + struct { + Uint16 large_magnitude; + Uint16 small_magnitude; + Uint32 expiration, time_left; + } rumble; +} ControllerInfo; -/** \brief SDL inof about joystick 2 -*/ -extern SDLJoyInfo_t JoyInfo2; +#define GAMEPAD_INIT_FLAGS (SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) -// So we can call this from i_video event loop -void I_ShutdownJoystick(void); -void I_ShutdownJoystick2(void); +void I_UpdateControllers(void); -// Cheat to get the device index for a joystick handle -INT32 I_GetJoystickDeviceIndex(SDL_Joystick *dev); +void I_ControllerDeviceAdded(INT32 which); +void I_ControllerDeviceRemoved(void); + +void I_HandleControllerButtonEvent(SDL_ControllerButtonEvent evt, Uint32 type); +void I_HandleControllerAxisEvent(SDL_ControllerAxisEvent evt); + +INT32 I_GetControllerIndex(SDL_GameController *dev); +void I_CloseInactiveController(SDL_GameController *dev); +void I_CloseInactiveHapticDevice(SDL_Haptic *dev); + +void I_ToggleControllerRumble(boolean unpause); void I_GetConsoleEvents(void);