Port Saturn's rumble and gamepad LED systems

This commit is contained in:
NepDisk 2025-11-20 14:23:52 -05:00
parent 64221c90d9
commit b337a9e69a
16 changed files with 466 additions and 22 deletions

View file

@ -94,7 +94,7 @@
#define ASSET_HASH_TEXTURES_KART 0xb4211b2f32b6a291
#define ASSET_HASH_CHARS_KART 0x1e68a3e01aa5c68b
#define ASSET_HASH_MAPS_KART 0x38558ed00da41ce9
#define ASSET_HASH_MAIN_PK3 0x07cf2f7ba7033674
#define ASSET_HASH_MAIN_PK3 0xf7afa9636053a91a
#define ASSET_HASH_MAPPATCH_PK3 0x0afd8afc6fc50175
#define ASSET_HASH_BONUSCHARS_KART 0x60e6f13d822a7461
#ifdef USE_PATCH_FILE

View file

@ -1253,6 +1253,7 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_deadzonestyle[i]);
CV_RegisterVar(&cv_litesteer[i]);
CV_RegisterVar(&cv_turnsmooth[i]);
CV_RegisterVar(&cv_rumble[i]);
}
// filesrch.c

View file

@ -564,6 +564,7 @@ struct player_t
UINT8 driftboost; // (0 to 125) - Boost you get from drifting
tic_t driftsparkGrowTimer;
tic_t driftelapsed; // Elapsed time spent during a drift.
SINT8 driftlevel; // just for controller rumble for now
fixed_t spinoutrot; // When a player spins out, this value increments modulus 360.

View file

@ -349,21 +349,6 @@ typedef struct joystickvector2_s
INT32 yaxis;
} joystickvector2_t;
consvar_t cv_litesteer[MAXSPLITSCREENPLAYERS] = {
CVAR_INIT ("litesteer", "Off", CV_SAVE, CV_OnOff, NULL),
CVAR_INIT ("litesteer2", "Off", CV_SAVE, CV_OnOff, NULL),
CVAR_INIT ("litesteer3", "Off", CV_SAVE, CV_OnOff, NULL),
CVAR_INIT ("litesteer4", "Off", CV_SAVE, CV_OnOff, NULL)
};
static CV_PossibleValue_t turnsmooth_cons_t[] = {{2, "Slow"}, {1, "Fast"}, {0, "Off"}, {0, NULL}};
consvar_t cv_turnsmooth[MAXSPLITSCREENPLAYERS] = {
CVAR_INIT ("turnsmoothing", "Off", CV_SAVE, turnsmooth_cons_t, NULL),
CVAR_INIT ("turnsmoothing2", "Off", CV_SAVE, turnsmooth_cons_t, NULL),
CVAR_INIT ("turnsmoothing3", "Off", CV_SAVE, turnsmooth_cons_t, NULL),
CVAR_INIT ("turnsmoothing4", "Off", CV_SAVE, turnsmooth_cons_t, NULL)
};
INT16 prevmap, nextmap;
INT16 kartmap2native[NEXTMAP_SPECIAL] = {0}, nativemap2kart[NEXTMAP_SPECIAL] = {0};

View file

@ -94,9 +94,6 @@ extern consvar_t cv_resetspecialmusic;
extern consvar_t cv_resume;
extern consvar_t cv_litesteer[MAXSPLITSCREENPLAYERS];
extern consvar_t cv_turnsmooth[MAXSPLITSCREENPLAYERS];
void weaponPrefChange(void);
void weaponPrefChange2(void);
void weaponPrefChange3(void);

View file

@ -21,11 +21,15 @@
#include "console.h"
#include "i_joy.h" // JOYAXISRANGE
#include "m_menu.h" // menustack
#include "i_system.h"
#include "g_game.h"
#include "v_video.h"
#define MAXMOUSESENSITIVITY 100 // sensitivity steps
static CV_PossibleValue_t mousesens_cons_t[] = {{1, "MIN"}, {MAXMOUSESENSITIVITY, "MAX"}, {0, NULL}};
static CV_PossibleValue_t onecontrolperkey_cons_t[] = {{1, "One"}, {2, "Several"}, {0, NULL}};
static CV_PossibleValue_t turnsmooth_cons_t[] = {{2, "Slow"}, {1, "Fast"}, {0, "Off"}, {0, NULL}};
// mouse values are used once
consvar_t cv_mousesens = CVAR_INIT ("mousesens", "20", CV_SAVE, mousesens_cons_t, NULL);
@ -33,6 +37,92 @@ consvar_t cv_mousesens2 = CVAR_INIT ("mousesens2", "20", CV_SAVE, mousesens_cons
consvar_t cv_mouseysens = CVAR_INIT ("mouseysens", "20", CV_SAVE, mousesens_cons_t, NULL);
consvar_t cv_mouseysens2 = CVAR_INIT ("mouseysens2", "20", CV_SAVE, mousesens_cons_t, NULL);
consvar_t cv_controlperkey = CVAR_INIT ("controlperkey", "One", CV_SAVE, onecontrolperkey_cons_t, NULL);
consvar_t cv_litesteer[MAXSPLITSCREENPLAYERS] = {
CVAR_INIT ("litesteer", "Off", CV_SAVE, CV_OnOff, NULL),
CVAR_INIT ("litesteer2", "Off", CV_SAVE, CV_OnOff, NULL),
CVAR_INIT ("litesteer3", "Off", CV_SAVE, CV_OnOff, NULL),
CVAR_INIT ("litesteer4", "Off", CV_SAVE, CV_OnOff, NULL)
};
consvar_t cv_turnsmooth[MAXSPLITSCREENPLAYERS] = {
CVAR_INIT ("turnsmoothing", "Off", CV_SAVE, turnsmooth_cons_t, NULL),
CVAR_INIT ("turnsmoothing2", "Off", CV_SAVE, turnsmooth_cons_t, NULL),
CVAR_INIT ("turnsmoothing3", "Off", CV_SAVE, turnsmooth_cons_t, NULL),
CVAR_INIT ("turnsmoothing4", "Off", CV_SAVE, turnsmooth_cons_t, NULL)
};
static void G_ResetPlayerDeviceRumble(INT32 player);
static void rumble_off_handle(void);
static void rumble_off_handle2(void);
static void rumble_off_handle3(void);
static void rumble_off_handle4(void);
static void G_ResetPlayerGamepadIndicatorColor(INT32 player);
static void led_off_handle(void);
static void led_off_handle2(void);
static void led_off_handle3(void);
static void led_off_handle4(void);
consvar_t cv_rumble[MAXSPLITSCREENPLAYERS] = {
CVAR_INIT ("rumble", "On", CV_SAVE, CV_OnOff, rumble_off_handle),
CVAR_INIT ("rumble2", "On", CV_SAVE, CV_OnOff, rumble_off_handle2),
CVAR_INIT ("rumble3", "On", CV_SAVE, CV_OnOff, rumble_off_handle3),
CVAR_INIT ("rumble4", "On", CV_SAVE, CV_OnOff, rumble_off_handle4)
};
static CV_PossibleValue_t gamepadled_cons_t[] = {{0, "Off"}, {1, "Skincolor"}, {2, "Mobjcolor"}, {0, NULL}};
consvar_t cv_gamepadled[MAXSPLITSCREENPLAYERS] = {
CVAR_INIT ("gamepadled", "Skincolor", CV_SAVE|CV_CALL|CV_NOINIT, gamepadled_cons_t, led_off_handle),
CVAR_INIT ("gamepadled2", "Skincolor", CV_SAVE|CV_CALL|CV_NOINIT, gamepadled_cons_t, led_off_handle2),
CVAR_INIT ("gamepadled3", "Skincolor", CV_SAVE|CV_CALL|CV_NOINIT, gamepadled_cons_t, led_off_handle3),
CVAR_INIT ("gamepadled4", "Skincolor", CV_SAVE|CV_CALL|CV_NOINIT, gamepadled_cons_t, led_off_handle4)
};
static void rumble_off_handle(void)
{
if (cv_rumble[0].value == 0)
G_ResetPlayerDeviceRumble(0);
}
static void rumble_off_handle2(void)
{
if (cv_rumble[1].value == 0)
G_ResetPlayerDeviceRumble(1);
}
static void rumble_off_handle3(void)
{
if (cv_rumble[2].value == 0)
G_ResetPlayerDeviceRumble(2);
}
static void rumble_off_handle4(void)
{
if (cv_rumble[3].value == 0)
G_ResetPlayerDeviceRumble(3);
}
static void led_off_handle(void)
{
G_ResetPlayerGamepadIndicatorColor(0);
}
static void led_off_handle2(void)
{
G_ResetPlayerGamepadIndicatorColor(1);
}
static void led_off_handle3(void)
{
G_ResetPlayerGamepadIndicatorColor(2);
}
static void led_off_handle4(void)
{
G_ResetPlayerGamepadIndicatorColor(3);
}
// current state of the keys
// JOYAXISRANGE for fully pressed, 0 for not pressed
INT32 gamekeydown[MAXDEVICES][NUMINPUTS];
@ -410,6 +500,118 @@ static const char *gamecontrolname[num_gamecontrols] =
#define NUMKEYNAMES (sizeof (keynames)/sizeof (keyname_t))
static INT32 G_GetDeviceForPlayer(INT32 player)
{
switch (player)
{
case 0:
return cv_usejoystick[0].value;
break;
case 1:
return cv_usejoystick[1].value;
break;
case 2:
return cv_usejoystick[2].value;
break;
case 3:
return cv_usejoystick[3].value;
break;
default:
return 0;
break;
}
}
UINT16 G_GetSkinColor(INT32 playernum)
{
if (gamestate == GS_LEVEL)
{
player_t *player = &players[displayplayers[playernum]];
if (player)
{
// make rgb rainbow vomit when invul or flash blue when grow
if ((cv_gamepadled[playernum].value == 2) && player->mo && player->mo->color)
return player->mo->color;
// take actual player skincolour when ingame
if (player->skincolor)
return player->skincolor;
}
}
// otherwise just fallback to whatever the cvar is
switch (playernum)
{
case 0:
return cv_playercolor[0].value;
case 1:
return cv_playercolor[1].value;
case 2:
return cv_playercolor[2].value;
case 3:
return cv_playercolor[3].value;
default:
return 0;
}
return 0;
}
void G_SetPlayerGamepadIndicatorColor(INT32 playernum, UINT16 color)
{
UINT16 skincolor;
RGBA_t byte_color;
I_Assert(playernum >= 0 && playernum < MAXSPLITSCREENPLAYERS);
if (cv_gamepadled[playernum].value == 0)
{
return;
}
skincolor = color ? color : G_GetSkinColor(playernum);
byte_color = V_GetColor(skincolors[skincolor].ramp[8]);
I_SetGamepadIndicatorColor(playernum, byte_color.s.red, byte_color.s.green, byte_color.s.blue);
}
static void G_ResetPlayerGamepadIndicatorColor(INT32 playernum)
{
if (cv_gamepadled[playernum].value == 0)
{
I_SetGamepadIndicatorColor(playernum, 0, 0, 255);
}
else
G_SetPlayerGamepadIndicatorColor(playernum, 0);
}
static void G_ResetPlayerDeviceRumble(INT32 player)
{
INT32 device_id;
device_id = G_GetDeviceForPlayer(player);
if (device_id < 1)
{
return;
}
I_GamepadRumble(device_id, 0, 0, 0);
}
void G_PlayerDeviceRumble(INT32 playernum, UINT16 low_strength, UINT16 high_strength, UINT32 duration)
{
I_Assert(playernum >= 0 && playernum < MAXSPLITSCREENPLAYERS);
if (cv_rumble[playernum].value == 0)
{
return;
}
I_GamepadRumble(playernum, low_strength, high_strength, duration);
}
// If keybind is necessary to navigate menus, it's on this list.
boolean G_KeyBindIsNecessary(INT32 gc)
{

View file

@ -94,6 +94,10 @@ typedef enum
extern consvar_t cv_mousesens, cv_mouseysens;
extern consvar_t cv_mousesens2, cv_mouseysens2;
extern consvar_t cv_controlperkey;
extern consvar_t cv_litesteer[MAXSPLITSCREENPLAYERS];
extern consvar_t cv_turnsmooth[MAXSPLITSCREENPLAYERS];
extern consvar_t cv_rumble[MAXSPLITSCREENPLAYERS];
extern consvar_t cv_gamepadled[MAXSPLITSCREENPLAYERS];
// current state of the keys: JOYAXISRANGE or 0 when boolean.
// Or anything inbetween for analog values
@ -124,6 +128,11 @@ extern const INT32 gcl_full[num_gcl_full];
// peace to my little coder fingers!
// check a gamecontrol being active or not
UINT16 G_GetSkinColor(INT32 playernum);
void G_SetPlayerGamepadIndicatorColor(INT32 playernum, UINT16 color);
void G_ResetAllDeviceRumbles(void);
void G_PlayerDeviceRumble(INT32 playernum, UINT16 low_strength, UINT16 high_strength, UINT32 duration);
INT32 G_GetDevicePlayer(INT32 deviceID);
INT32 G_AxisToKey(event_t *ev);

View file

@ -231,6 +231,7 @@ void I_InitJoystick4(void);
/** \brief return the number of joystick on the system
*/
INT32 I_NumJoys(void);
extern INT32 numcontrollers;
/** \brief The *I_GetJoyName function
@ -240,6 +241,9 @@ INT32 I_NumJoys(void);
*/
const char *I_GetJoyName(INT32 joyindex);
void I_GamepadRumble(INT32 playernum, UINT16 low_strength, UINT16 high_strength, UINT32 duration);
void I_SetGamepadIndicatorColor(INT32 playernum, UINT8 red, UINT8 green, UINT8 blue);
#ifndef NOMUMBLE
#include "p_mobj.h" // mobj_t
#include "s_sound.h" // listener_t

View file

@ -8895,14 +8895,17 @@ static void K_KartDrift(player_t *player, boolean onground)
{
case 1:
boost = 20;
player->driftlevel = 1;
break;
case 2:
boost = 50;
player->driftlevel = 2;
if (cv_kartdriftsounds.value)
S_StartSound(player->mo, sfx_kc5b);
break;
case 3:
boost = 80;
player->driftlevel = 3;
if (cv_kartdriftsounds.value)
{
S_StartSound(player->mo, sfx_kc5b);
@ -8913,6 +8916,7 @@ static void K_KartDrift(player_t *player, boolean onground)
break;
case 4:
boost = 125;
player->driftlevel = 4;
if (cv_kartdriftsounds.value)
{
S_StartSound(player->mo, sfx_kc5b);

View file

@ -22,6 +22,7 @@
#include "m_random.h"
#include "s_sound.h"
#include "g_game.h"
#include "g_input.h"
#include "m_menu.h"
#include "y_inter.h"
#include "hu_stuff.h" // HU_AddChatText
@ -5101,6 +5102,58 @@ static int lib_kSetPlayerItemCooldown(lua_State *L)
return 0;
}
// G_INPUT
////////////
static int lib_gSetPlayerGamepadIndicatorColor(lua_State *L)
{
INT32 player = -1;
player_t *plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); // retrieve player
UINT16 color = (UINT16)luaL_checkinteger(L, 2); // skincolor
for (int i = 0; i < MAXSPLITSCREENPLAYERS; ++i)
{
if (plr - players == displayplayers[i])
{
player = i;
break;
}
}
// Not a local player
if (player == -1) return 0;
// pls update with color 0 when youre done with changing led stuff so it can get player color again
G_SetPlayerGamepadIndicatorColor(player, color);
return 0;
}
static int lib_gPlayerDeviceRumble(lua_State *L)
{
INT32 player = -1;
player_t *plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); // retrieve player
UINT16 low_strength = (UINT16)luaL_checkinteger(L, 2); // low frequency rumble motor strenght
UINT16 high_strength = (UINT16)luaL_checkinteger(L, 3); // high frequency rumble motor strenght
UINT32 duration = (UINT32)luaL_optinteger(L, 4, 84); // duration of rumble in ms
for (int i = 0; i < MAXSPLITSCREENPLAYERS; ++i)
{
if (plr - players == displayplayers[i])
{
player = i;
break;
}
}
// Not a local player
if (player == -1) return 0;
G_PlayerDeviceRumble(player, low_strength, high_strength, duration);
return 0;
}
static luaL_Reg lib[] = {
{"print", lib_print},
{"chatprint", lib_chatprint},
@ -5490,6 +5543,10 @@ static luaL_Reg lib[] = {
// k_items
{"K_SetPlayerItemCooldown", lib_kSetPlayerItemCooldown},
//g_input
{"G_SetPlayerGamepadIndicatorColor",lib_gSetPlayerGamepadIndicatorColor},
{"G_PlayerDeviceRumble",lib_gPlayerDeviceRumble},
{NULL, NULL}
};

View file

@ -478,7 +478,14 @@ consvar_t cv_dummymultiplayer = CVAR_INIT ("dummymultiplayer", "0", CV_HIDEN, du
consvar_t cv_dummyip = CVAR_INIT ("dummyip", "", CV_HIDEN, NULL, NULL);
consvar_t cv_dummyname = CVAR_INIT ("dummyname", "", CV_HIDEN, NULL, NULL);
consvar_t cv_dummyfollower = CVAR_INIT ("dummyfollower", "-1", CV_HIDEN, dummyfollower_cons_t, NULL);
consvar_t cv_dummycolor = CVAR_INIT ("dummycolor", "0", CV_HIDEN, dummycolor_cons_t, NULL);
SINT8 dummycolorplayer = 0;
static void SetDummyColorPlayer(void)
{
G_SetPlayerGamepadIndicatorColor(dummycolorplayer, cv_dummycolor.value);
}
consvar_t cv_dummycolor = CVAR_INIT ("dummycolor", "0", CV_HIDEN, dummycolor_cons_t, SetDummyColorPlayer);
static CV_PossibleValue_t dummyserverpage_cons_t[] = {{0, "MIN"}, {0, "MAX"}, {0, NULL}};
consvar_t cv_dummyserverpage = CVAR_INIT ("dummyserverpage", "0", CV_HIDEN, dummyserverpage_cons_t, NULL);
@ -7530,6 +7537,8 @@ INT32 MR_SetupMultiPlayer(INT32 arg)
gridcss_skinmemory = cv_chooseskin.value;
CV_SetValue(&cv_dummyfollower, cv_follower[arg].value);
CV_SetValue(&cv_dummycolor, cv_playercolor[arg].value);
G_SetPlayerGamepadIndicatorColor(arg, cv_playercolor[arg].value);
dummycolorplayer = arg;
Skinsort_option_Onchange();
@ -7918,6 +7927,8 @@ INT32 MR_SetupControlsMenu(INT32 arg)
M_SetItemCvar(MN_OP_CHANGECONTROLS, "DEAZS", &cv_deadzonestyle[arg]);
M_SetItemCvar(MN_OP_CHANGECONTROLS, "TURNSMOOTHING", &cv_turnsmooth[arg]);
M_SetItemCvar(MN_OP_CHANGECONTROLS, "LITESTEER", &cv_litesteer[arg]);
M_SetItemCvar(MN_OP_CHANGECONTROLS, "LEDCOLOR", &cv_gamepadled[arg]);
M_SetItemCvar(MN_OP_CHANGECONTROLS, "RUMBLE", &cv_rumble[arg]);
M_SetItemVisible(MN_OP_CHANGECONTROLS, "TALK", player1); // Chat
//M_SetItemVisible(MN_OP_CHANGECONTROLS, "TEAM", player1); // Team-chat

View file

@ -10987,7 +10987,7 @@ void A_SPBChase(void *thing)
if (players[i].mo->health <= 0)
continue; // dead
if (players[i].kartstuff[k_respawn])
if (players[i].respawn)
continue;*/ // respawning
if ((gametyperules & GTR_WANTED) && (gametyperules & GTR_WANTEDSPB))
@ -11030,7 +11030,7 @@ void A_SPBChase(void *thing)
actor->lastlook = actor->tracer->player-players; // Save the player num for death scumming...
if (!P_IsObjectOnGround(actor->tracer) /*&& !actor->tracer->player->kartstuff[k_pogospring]*/)
if (!P_IsObjectOnGround(actor->tracer) /*&& !actor->tracer->player->pogospring*/)
{
// In the air you have no control; basically don't hit unless you make a near complete stop
defspeed = (7 * actor->tracer->player->speed) / 8;

View file

@ -501,6 +501,126 @@ static void P_RunThinkers(void)
ps_acs_time = I_GetPreciseTime() - ps_acs_time;
}
// Controller rumble!
// this keeps track of a bunch of things
// and makes your controller rumble accordingly
static void P_DeviceRumbleTick(void)
{
UINT8 i;
if (dedicated || numcontrollers == 0 || gamestate != GS_LEVEL)
{
return;
}
for (i = 0; i <= splitscreen; i++)
{
if (!cv_usejoystick[i].value || !cv_rumble[i].value)
{
continue;
}
if (camera[i].freecam)
{
continue;
}
UINT16 low = 0, high = 0;
UINT16 lenght = 57; // in ms
const player_t *player = &players[g_localplayers[i]];
// allow lua to do some crap for spectators
if (player->spectator || !player->mo)
{
continue;
}
// reset the rumble if you exit or are ded lel
if (player->exiting ||
player->playerstate == PST_DEAD ||
player->respawn > 1)
{
G_PlayerDeviceRumble(i, low, high, 0);
continue;
}
if (player->spinouttimer)
{
//low = high = FRACUNIT / 6;
low = high = FixedMul((FRACUNIT / 4), (FixedDiv(player->spinouttimer, (3*TICRATE / 2)))); // try do some some kinda fadeout
}
else if (player->sneakertimer > (sneakertime-(TICRATE/2)))
{
low = high = FRACUNIT / 8;
}
else if ((player->offroad)
&& player->speed != 0
&& P_IsObjectOnGround(player->mo))
{
// weaken this depending on if you got hyu or invinc
if (player->hyudorotimer)
{
high = FRACUNIT / 128;
}
else if (player->invincibilitytimer)
{
high = FRACUNIT / 64;
}
else
{
low = high = FRACUNIT / 64;
}
}
else if ((player->bananadrag > TICRATE)
&& player->speed != 0
&& P_IsObjectOnGround(player->mo))
{
if (leveltime & 1) // this is actually funny lel
high = FRACUNIT / 64;
}
if (player->pflags & PF_BRAKEDRIFT)
{
high = CLAMP((high + FRACUNIT / 256), 0, UINT16_MAX);
}
// pulse when gettin new driftlevel
if (player->driftcharge
&& player->driftlevel)
{
high = CLAMP((high + FRACUNIT / 256), 0, UINT16_MAX);
if (player->driftlevel == 2)
lenght = 114;
else if (player->driftlevel == 3)
lenght = 144;
else if (player->driftlevel == 4)
lenght = 174;
}
// pulse when using rings
// let this come last
if ((player->cmd.buttons & BT_ATTACK)
&& (player->itemflags & IF_USERINGS)
&& (player->rings > 0
&& player->rings > player->rings-1
&& player->rings < player->rings+1)
)
{
high = CLAMP((high + FRACUNIT / 256), 0, UINT16_MAX);
}
// hack alert! i just dont want this thing constantly resetting the rumble lol
if (low == 0 && high == 0)
{
continue;
}
G_PlayerDeviceRumble(i, low, high, lenght);
}
}
//
// P_DoAutobalanceTeams()
//
@ -792,6 +912,12 @@ void P_Ticker(boolean run)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
P_PlayerAfterThink(&players[i]);
// Apply rumble to local players
if (!demo.playback)
{
P_DeviceRumbleTick();
}
// run all the bot tickers
if (server)
{

View file

@ -3861,6 +3861,8 @@ void P_PlayerThink(player_t *player)
I_Error("p_playerthink: players[%s].mo == NULL", sizeu1(playeri));
#endif
player->driftlevel = 0; // idk where to put this
// todo: Figure out what is actually causing these problems in the first place...
if (player->mo->health <= 0 && player->playerstate == PST_LIVE) //you should be DEAD!
{

View file

@ -286,6 +286,7 @@ static INT32 joystick_started[MAXSPLITSCREENPLAYERS] = {0,0,0,0};
/** \brief SDL info about joystick 1
*/
SDLJoyInfo_t JoyInfo[MAXSPLITSCREENPLAYERS];
INT32 numcontrollers = 0;
SDL_GameController *ExJoystick[MAXGAMEPADS];
SDL_bool consolevent = SDL_FALSE;
@ -1487,6 +1488,44 @@ const char *I_GetJoyName(INT32 joyindex)
return joyname;
}
void I_GamepadRumble(INT32 playernum, UINT16 low_strength, UINT16 high_strength, UINT32 duration)
{
#if !(SDL_VERSION_ATLEAST(2,0,14))
(void)playernum;
(void)low_strength;
(void)high_strength;
(void)duration;
#else
SDL_GameController *controller = JoyInfo[playernum].dev;
if (controller == NULL)
{
return;
}
SDL_GameControllerRumble(controller, low_strength, high_strength, duration);
#endif
}
void I_SetGamepadIndicatorColor(INT32 playernum, UINT8 red, UINT8 green, UINT8 blue)
{
#if !(SDL_VERSION_ATLEAST(2,0,14))
(void)playernum;
(void)red;
(void)green;
(void)blue;
#else
SDL_GameController *controller = JoyInfo[playernum].dev;
if (controller == NULL)
{
return;
}
SDL_GameControllerSetLED(controller, red, green, blue);
#endif
}
#ifndef NOMUMBLE
#ifdef HAVE_MUMBLE
// Best Mumble positional audio settings:

View file

@ -1040,7 +1040,10 @@ void I_GetEvent(void)
////////////////////////////////////////////////////////////
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
I_InitJoystick(i);
G_SetPlayerGamepadIndicatorColor(i, G_GetSkinColor(i)); // gotta update the controller led again on reconnect
}
////////////////////////////////////////////////////////////
@ -1051,6 +1054,8 @@ void I_GetEvent(void)
if (menustack[0] == MN_OP_JOYSTICKSET)
MR_SetupJoystickMenu(0);
numcontrollers = I_NumJoys();
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (JoyInfo[i].dev == newcontroller)
@ -1110,6 +1115,7 @@ void I_GetEvent(void)
// update the menu
if (menustack[0] == MN_OP_JOYSTICKSET)
MR_SetupJoystickMenu(0);
numcontrollers = I_NumJoys();
break;
case SDL_DROPFILE:
dropped_filedir = evt.drop.file;