diff --git a/src/cdogs.c b/src/cdogs.c index 971441ca0..b786f90ea 100644 --- a/src/cdogs.c +++ b/src/cdogs.c @@ -322,7 +322,9 @@ int main(int argc, char *argv[]) } debug(D_NORMAL, "Initialising SDL...\n"); - if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | controllerFlag) != 0) + const int sdlFlags = + SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_HAPTIC; + if (SDL_Init(sdlFlags | controllerFlag) != 0) { fprintf(stderr, "Could not initialise SDL: %s\n", SDL_GetError()); err = EXIT_FAILURE; diff --git a/src/cdogs/handle_game_events.c b/src/cdogs/handle_game_events.c index b0b157e16..e17f49442 100644 --- a/src/cdogs/handle_game_events.c +++ b/src/cdogs/handle_game_events.c @@ -30,7 +30,9 @@ #include "actor_placement.h" #include "ai_utils.h" #include "damage.h" +#include "events.h" #include "game_events.h" +#include "joystick.h" #include "net_server.h" #include "objs.h" #include "particle.h" @@ -110,6 +112,10 @@ static void HandleGameEvent( camera->shake = ScreenShakeAdd( camera->shake, e.u.ShakeAmount, ConfigGetInt(&gConfig, "Graphics.ShakeMultiplier")); + // Weak rumble for all joysticks + CA_FOREACH(Joystick, j, gEventHandlers.joysticks) + JoyRumble(j->id, 0.3f, 500); + CA_FOREACH_END() break; case GAME_EVENT_SET_MESSAGE: HUDDisplayMessage( @@ -446,6 +452,16 @@ static void HandleGameEvent( AddBloodSplatter( a->Pos, e.u.ActorHit.Power, Net2Vec2i(e.u.ActorHit.Vel)); + + // Rumble if taking hit + if (a->PlayerUID >= 0) + { + const PlayerData *p = PlayerDataGetByUID(a->PlayerUID); + if (p->inputDevice == INPUT_DEVICE_JOYSTICK) + { + JoyImpact(p->deviceIndex); + } + } } } break; diff --git a/src/cdogs/joystick.c b/src/cdogs/joystick.c index 7cd6a7325..e7d69f9ec 100644 --- a/src/cdogs/joystick.c +++ b/src/cdogs/joystick.c @@ -68,10 +68,16 @@ void JoyReset(CArray *joys) CA_FOREACH_END() } +static void JoyTerminateOne(Joystick *j) +{ + SDL_GameControllerClose(j->gc); + SDL_HapticClose(j->haptic); +} + void JoyTerminate(CArray *joys) { CA_FOREACH(Joystick, j, *joys) - SDL_GameControllerClose(j->gc); + JoyTerminateOne(j); CA_FOREACH_END() CArrayTerminate(joys); } @@ -119,6 +125,7 @@ void JoyAdded(const Sint32 which) Joystick j; memset(&j, 0, sizeof j); + j.hapticEffectId = -1; j.gc = SDL_GameControllerOpen(which); if (j.gc == NULL) { @@ -140,6 +147,44 @@ void JoyAdded(const Sint32 which) SDL_GetError()); return; } + const int isHaptic = SDL_JoystickIsHaptic(j.j); + if (isHaptic > 0) + { + j.haptic = SDL_HapticOpenFromJoystick(j.j); + if (j.haptic == NULL) + { + LOG(LM_INPUT, LL_ERROR, "Failed to open haptic: %s", + SDL_GetError()); + } + else + { + if (SDL_HapticRumbleInit(j.haptic) != 0) + { + LOG(LM_INPUT, LL_ERROR, "Failed to init rumble: %s", + SDL_GetError()); + } + const int hapticQuery = SDL_HapticQuery(j.haptic); + LOG(LM_INPUT, LL_INFO, "Haptic support: %x", hapticQuery); + if (hapticQuery & SDL_HAPTIC_CONSTANT) + { + SDL_HapticEffect he; + memset(&he, 0, sizeof he); + he.type = SDL_HAPTIC_CONSTANT; + he.constant.length = 100; + he.constant.level = 20000; // 20000/32767 strength + j.hapticEffectId = SDL_HapticNewEffect(j.haptic, &he); + if (j.hapticEffectId == -1) + { + LOG(LM_INPUT, LL_ERROR, + "Failed to create haptic effect: %s", SDL_GetError()); + } + } + } + } + else if (isHaptic < 0) + { + LOG(LM_INPUT, LL_ERROR, "Failed to query haptic: %s", SDL_GetError()); + } CArrayPushBack(&gEventHandlers.joysticks, &j); LOG(LM_INPUT, LL_INFO, "Added joystick index %d id %d", which, j.id); } @@ -149,7 +194,7 @@ void JoyRemoved(const Sint32 which) CA_FOREACH(Joystick, j, gEventHandlers.joysticks) if (j->id == which) { - SDL_GameControllerClose(j->gc); + JoyTerminateOne(j); CArrayDelete(&gEventHandlers.joysticks, i); return; } @@ -184,7 +229,6 @@ int ControllerButtonToCmd(const Uint8 button) case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return CMD_RIGHT; default: return 0; } - // TODO: check button mappings } #define DEADZONE 16384 void JoyOnAxis(const SDL_ControllerAxisEvent e) @@ -210,7 +254,6 @@ void JoyOnAxis(const SDL_ControllerAxisEvent e) // Ignore axis break; } - // TODO: check other controllers } static void JoyOnCmd(Joystick *j, const int cmd, const bool isDown) @@ -229,6 +272,27 @@ static void JoyOnCmd(Joystick *j, const int cmd, const bool isDown) } } +void JoyRumble( + const SDL_JoystickID id, const float strength, const Uint32 length) +{ + Joystick *j = GetJoystick(id); + if (j->haptic == NULL) return; + if (SDL_HapticRumblePlay(j->haptic, strength, length) < 0) + { + LOG(LM_INPUT, LL_ERROR, "Failed to rumble: %s", SDL_GetError()); + } +} +void JoyImpact(const SDL_JoystickID id) +{ + Joystick *j = GetJoystick(id); + if (j->hapticEffectId != -1 && + SDL_HapticRunEffect(j->haptic, j->hapticEffectId, 1) != 0) + { + LOG(LM_INPUT, LL_ERROR, "Failed to run haptic effect: %s", + SDL_GetError()); + } +} + const char *JoyName(const SDL_JoystickID id) { const Joystick *j = GetJoystick(id); diff --git a/src/cdogs/joystick.h b/src/cdogs/joystick.h index bfb6d4456..fc5b891f0 100644 --- a/src/cdogs/joystick.h +++ b/src/cdogs/joystick.h @@ -32,6 +32,7 @@ #include #include +#include #include "c_array.h" #include "color.h" @@ -42,6 +43,8 @@ typedef struct SDL_GameController *gc; SDL_Joystick *j; SDL_JoystickID id; + SDL_Haptic *haptic; + int hapticEffectId; int currentCmd; int previousCmd; int pressedCmd; @@ -63,6 +66,10 @@ void JoyOnButtonDown(const SDL_ControllerButtonEvent e); void JoyOnButtonUp(const SDL_ControllerButtonEvent e); void JoyOnAxis(const SDL_ControllerAxisEvent e); +void JoyRumble( + const SDL_JoystickID id, const float strength, const Uint32 length); +void JoyImpact(const SDL_JoystickID id); + const char *JoyName(const SDL_JoystickID id); const char *JoyButtonNameColor( const SDL_JoystickID id, const int cmd, color_t *color);