diff --git a/src/xrEngine/FDemoRecord.cpp b/src/xrEngine/FDemoRecord.cpp index 26779dff6fd..93e7fb66e5b 100644 --- a/src/xrEngine/FDemoRecord.cpp +++ b/src/xrEngine/FDemoRecord.cpp @@ -810,6 +810,18 @@ void CDemoRecord::IR_OnControllerRelease(int key, float x, float y) } } +void CDemoRecord::IR_OnControllerAttitudeChange(Fvector change) +{ + if (m_b_redirect_input_to_level) + { + g_pGameLevel->IR_OnControllerAttitudeChange(change); + return; + } + + const float scale = 5.f; // psControllerSensorSens; + OnAxisMove(change.x, change.y, scale, psControllerInvertY.test(1)); +} + void CDemoRecord::RecordKey() { Fmatrix g_matView; diff --git a/src/xrEngine/FDemoRecord.h b/src/xrEngine/FDemoRecord.h index f325707bc59..4e01d19aab8 100644 --- a/src/xrEngine/FDemoRecord.h +++ b/src/xrEngine/FDemoRecord.h @@ -75,6 +75,8 @@ class ENGINE_API CDemoRecord : public CEffectorCam, public IInputReceiver, publi void IR_OnControllerHold(int key, float x, float y) override; void IR_OnControllerRelease(int key, float x, float y) override; + void IR_OnControllerAttitudeChange(Fvector change) override; + virtual bool ProcessCam(SCamEffectorInfo& info); static void SetGlobalPosition(const Fvector& p) { g_position.p.set(p), g_position.set_position = true; } static void GetGlobalPosition(Fvector& p) { p.set(g_position.p); } diff --git a/src/xrEngine/IInputReceiver.h b/src/xrEngine/IInputReceiver.h index 9e0e15a0792..9b5446dac25 100644 --- a/src/xrEngine/IInputReceiver.h +++ b/src/xrEngine/IInputReceiver.h @@ -43,6 +43,8 @@ class ENGINE_API IInputReceiver virtual void IR_OnControllerPress(int /*dik*/, float /*x*/, float /*y*/) {} virtual void IR_OnControllerRelease(int /*dik*/, float /*x*/, float /*y*/) {} virtual void IR_OnControllerHold(int /*dik*/, float /*x*/, float /*y*/) {} + + virtual void IR_OnControllerAttitudeChange(Fvector /*change*/) {} }; ENGINE_API extern float psMouseSens; @@ -52,6 +54,8 @@ ENGINE_API extern Flags32 psMouseInvert; ENGINE_API extern float psControllerStickSens; ENGINE_API extern float psControllerStickSensScale; ENGINE_API extern float psControllerStickDeadZone; +ENGINE_API extern float psControllerSensorSens; +ENGINE_API extern float psControllerSensorDeadZone; ENGINE_API extern Flags32 psControllerInvertY; #endif diff --git a/src/xrEngine/xr_input.cpp b/src/xrEngine/xr_input.cpp index 7e3896f7979..4829611241a 100644 --- a/src/xrEngine/xr_input.cpp +++ b/src/xrEngine/xr_input.cpp @@ -17,6 +17,8 @@ ENGINE_API Flags32 psMouseInvert = {false}; ENGINE_API float psControllerStickSens = 1.f; ENGINE_API float psControllerStickSensScale = 1.f; ENGINE_API float psControllerStickDeadZone = 0.f; +ENGINE_API float psControllerSensorSens = 1.f; +ENGINE_API float psControllerSensorDeadZone = 0.f; ENGINE_API Flags32 psControllerInvertY = { false }; static bool AltF4Pressed = false; @@ -32,12 +34,6 @@ CInput::CInput(const bool exclusive) Log("Starting INPUT device..."); - if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == 0) - { - for (int i = 0; i < SDL_NumJoysticks(); ++i) - OpenController(i); - } - m_mouseDelta = 25; mouseState.reset(); @@ -56,6 +52,12 @@ CInput::CInput(const bool exclusive) Device.seqAppActivate.Add(this); Device.seqAppDeactivate.Add(this, REG_PRIORITY_HIGH); Device.seqFrame.Add(this, REG_PRIORITY_HIGH); + + if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == 0) + { + for (int i = 0; i < SDL_NumJoysticks(); ++i) + OpenController(i); + } } CInput::~CInput() @@ -80,6 +82,7 @@ void CInput::OpenController(int idx) if (!controller) return; + SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE); controllers.emplace_back(controller); } @@ -272,7 +275,7 @@ void CInput::ControllerUpdate() CopyMemory(controllerAxisStatePrev, controllerAxisState, sizeof(controllerAxisState)); count = SDL_PeepEvents(events, MAX_CONTROLLER_EVENTS, - SDL_GETEVENT, SDL_CONTROLLERAXISMOTION, SDL_CONTROLLERDEVICEREMOVED); + SDL_GETEVENT, SDL_CONTROLLERAXISMOTION, SDL_CONTROLLERSENSORUPDATE); for (int i = 0; i < count; ++i) { @@ -299,6 +302,9 @@ void CInput::ControllerUpdate() if (event.cbutton.button >= XR_CONTROLLER_BUTTON_COUNT) break; // SDL added new button, not supported by engine yet + if (last_input_controller != event.cbutton.which) // don't write if don't really need to + last_input_controller = event.cbutton.which; + controllerState[event.cbutton.button] = true; cbStack.back()->IR_OnControllerPress(ControllerButtonToKey[event.cbutton.button], 1.f, 0.f); break; @@ -307,6 +313,9 @@ void CInput::ControllerUpdate() if (event.cbutton.button >= XR_CONTROLLER_BUTTON_COUNT) break; // SDL added new button, not supported by engine yet + if (last_input_controller != event.cbutton.which) // don't write if don't really need to + last_input_controller = event.cbutton.which; + controllerState[event.cbutton.button] = false; cbStack.back()->IR_OnControllerRelease(ControllerButtonToKey[event.cbutton.button], 0.f, 0.f); break; @@ -323,6 +332,19 @@ void CInput::ControllerUpdate() controllers.erase(it); break; } + + case SDL_CONTROLLERSENSORUPDATE: + { + if (last_input_controller != event.csensor.which) // only use data from the recently used controller + break; + if (event.csensor.sensor != SDL_SENSOR_GYRO) + break; + + const auto gyro = Fvector { -event.csensor.data[1], -event.csensor.data[0], -event.csensor.data[2] }; + if (!gyro.similar(Fvector{ 0.f, 0.f, 0.f }, psControllerSensorDeadZone)) + cbStack.back()->IR_OnControllerAttitudeChange(gyro); + break; + } } // switch (event.type) } diff --git a/src/xrEngine/xr_ioc_cmd.cpp b/src/xrEngine/xr_ioc_cmd.cpp index e4f927a2830..49f09ad8958 100644 --- a/src/xrEngine/xr_ioc_cmd.cpp +++ b/src/xrEngine/xr_ioc_cmd.cpp @@ -843,6 +843,10 @@ void CCC_Register() CMD4(CCC_Float, "gamepad_stick_sens", &psControllerStickSens, 0.001f, 0.6f); psControllerStickDeadZone = 15.f; CMD4(CCC_Float, "gamepad_stick_deadzone", &psControllerStickDeadZone, 1.f, 35.f); + psControllerSensorSens = 0.5f; + CMD4(CCC_Float, "gamepad_sensor_sens", &psControllerSensorSens, 0.01f, 2.f); + psControllerSensorDeadZone = 0.05f; + CMD4(CCC_Float, "gamepad_sensor_deadzone", &psControllerSensorDeadZone, 0.001f, 1.f); // Camera CMD2(CCC_Float, "cam_inert", &psCamInert); diff --git a/src/xrGame/Actor.h b/src/xrGame/Actor.h index 81e625e7409..e226c204cc3 100644 --- a/src/xrGame/Actor.h +++ b/src/xrGame/Actor.h @@ -449,6 +449,8 @@ class CActor : public CEntityAlive, void IR_OnControllerRelease(int dik, float x, float y) override; void IR_OnControllerHold(int dik, float x, float y) override; + void IR_OnControllerAttitudeChange(Fvector change) override; + virtual float GetLookFactor(); private: diff --git a/src/xrGame/ActorInput.cpp b/src/xrGame/ActorInput.cpp index f86cd1752ab..8b40105dac2 100644 --- a/src/xrGame/ActorInput.cpp +++ b/src/xrGame/ActorInput.cpp @@ -544,6 +544,29 @@ void CActor::IR_OnControllerHold(int cmd, float x, float y) } // switch (GetBindedAction(axis)) } +void CActor::IR_OnControllerAttitudeChange(Fvector change) +{ + PIItem iitem = inventory().ActiveItem(); + if (iitem && iitem->cast_hud_item()) + iitem->cast_hud_item()->ResetSubStateTime(); + + if (Remote()) + return; + + if (!IsZoomAimingMode() && !psActorFlags.test(AF_ALWAYS_USE_ATTITUDE_SENSORS)) + return; + + if (m_holder) + { + m_holder->OnControllerAttitudeChange(change); + return; + } + + const float LookFactor = GetLookFactor(); + const float scale = (cam_Active()->f_fov / g_fov) * psControllerSensorSens / 50.f / LookFactor; + OnAxisMove(change.x, change.y, scale, psControllerInvertY.test(1)); +} + #include "HudItem.h" bool CActor::use_Holder(CHolderCustom* holder) { diff --git a/src/xrGame/Actor_Flags.h b/src/xrGame/Actor_Flags.h index 3af281b7eb7..58f44fa36b6 100644 --- a/src/xrGame/Actor_Flags.h +++ b/src/xrGame/Actor_Flags.h @@ -14,6 +14,7 @@ enum AF_CROUCH_TOGGLE = (1 << 10), AF_MULTI_ITEM_PICKUP = (1 << 11), AF_LOADING_STAGES = (1 << 12), + AF_ALWAYS_USE_ATTITUDE_SENSORS = (1 << 13), // or only when zooming if false }; extern Flags32 psActorFlags; diff --git a/src/xrGame/Car.h b/src/xrGame/Car.h index eb876018880..14db9e0c574 100644 --- a/src/xrGame/Car.h +++ b/src/xrGame/Car.h @@ -586,15 +586,22 @@ class CCar : public CEntity, virtual bool net_Relevant() { return getLocal(); }; // relevant for export to server virtual bool UsedAI_Locations(); virtual void net_Relcase(IGameObject* O); + // Input void OnAxisMove(float x, float y, float scale, bool invert); + virtual void OnMouseMove(int x, int y); + virtual void OnKeyboardPress(int dik); virtual void OnKeyboardRelease(int dik); virtual void OnKeyboardHold(int dik); + void OnControllerPress(int cmd, float x, float y) override; void OnControllerHold(int cmd, float x, float y) override; void OnControllerRelease(int cmd, float x, float y) override; + + void OnControllerAttitudeChange(Fvector change) override; + virtual void vfProcessInputKey(int iCommand, bool bPressed); virtual void OnEvent(NET_Packet& P, u16 type); virtual void OnAfterExplosion(); diff --git a/src/xrGame/CarInput.cpp b/src/xrGame/CarInput.cpp index c217ab0d900..8e117d7bc41 100644 --- a/src/xrGame/CarInput.cpp +++ b/src/xrGame/CarInput.cpp @@ -354,6 +354,11 @@ void CCar::OnControllerHold(int cmd, float x, float y) } } +void CCar::OnControllerAttitudeChange(Fvector change) +{ + const float scale = (active_camera->f_fov / g_fov) * psControllerSensorSens / 50.f; + OnAxisMove(change.x, change.y, scale, psControllerInvertY.test(1)); +} void CCar::Action(u16 id, u32 flags) { diff --git a/src/xrGame/Level.h b/src/xrGame/Level.h index e0c11784f31..b31972e4c09 100644 --- a/src/xrGame/Level.h +++ b/src/xrGame/Level.h @@ -322,6 +322,8 @@ class CLevel : public IGame_Level, public IPureClient void IR_OnControllerRelease(int key, float x, float y) override; void IR_OnControllerHold(int key, float x, float y) override; + void IR_OnControllerAttitudeChange(Fvector change) override; + void IR_OnActivate(void) override; // Returns respawn point ID diff --git a/src/xrGame/Level_input.cpp b/src/xrGame/Level_input.cpp index 1def85260e6..3eb60308a65 100644 --- a/src/xrGame/Level_input.cpp +++ b/src/xrGame/Level_input.cpp @@ -731,6 +731,32 @@ void CLevel::IR_OnControllerHold(int key, float x, float y) } } +void CLevel::IR_OnControllerAttitudeChange(Fvector change) +{ + if (g_bDisableAllInput) + return; + + if (g_actor) + { + g_actor->callback(GameObject::eControllerAttitudeChange)(change); + } + +#ifndef MASTER_GOLD + if (!psActorFlags.test(AF_NO_CLIP)) +#endif + { + if (Device.Paused() && !IsDemoPlay()) + return; + } + + if (CURRENT_ENTITY()) + { + IInputReceiver* IR = smart_cast(smart_cast(CURRENT_ENTITY())); + if (IR) + IR->IR_OnControllerAttitudeChange(change); + } +} + void CLevel::IR_OnActivate() { if (!pInput) diff --git a/src/xrGame/WeaponStatMgun.h b/src/xrGame/WeaponStatMgun.h index 78bf8609208..0ecb24dc052 100644 --- a/src/xrGame/WeaponStatMgun.h +++ b/src/xrGame/WeaponStatMgun.h @@ -75,17 +75,25 @@ class CWeaponStatMgun : public CPhysicsShellHolder, public CHolderCustom, public void RemoveShotEffector(); void SetDesiredDir(float h, float p); virtual bool IsHudModeNow() { return false; }; - // HolderCustom + public: + // HolderCustom virtual bool Use(const Fvector& pos, const Fvector& dir, const Fvector& foot_pos) { return !Owner(); }; + void OnAxisMove(float x, float y, float scale, bool invert); + virtual void OnMouseMove(int x, int y); + virtual void OnKeyboardPress(int dik); virtual void OnKeyboardRelease(int dik); virtual void OnKeyboardHold(int dik); + void OnControllerPress(int cmd, float x, float y) override; void OnControllerHold(int cmd, float x, float y) override; void OnControllerRelease(int cmd, float x, float y) override; + + void OnControllerAttitudeChange(Fvector change) override; + virtual CInventory* GetInventory() { return NULL; }; virtual void cam_Update(float dt, float fov = 90.0f); diff --git a/src/xrGame/WeaponStatMgunIR.cpp b/src/xrGame/WeaponStatMgunIR.cpp index 96fcc08045f..06914ca4e0c 100644 --- a/src/xrGame/WeaponStatMgunIR.cpp +++ b/src/xrGame/WeaponStatMgunIR.cpp @@ -106,3 +106,9 @@ void CWeaponStatMgun::OnControllerHold(int cmd, float x, float y) break; }; // switch (cmd) } + +void CWeaponStatMgun::OnControllerAttitudeChange(Fvector change) +{ + const float scale = psControllerSensorSens / 50.f; + OnAxisMove(change.x, change.y, scale, psControllerInvertY.test(1)); +} diff --git a/src/xrGame/console_commands.cpp b/src/xrGame/console_commands.cpp index 9eb22b1f51b..2e6046386e8 100644 --- a/src/xrGame/console_commands.cpp +++ b/src/xrGame/console_commands.cpp @@ -2114,6 +2114,7 @@ void CCC_RegisterCommands() CMD3(CCC_Mask, "g_dynamic_music", &psActorFlags, AF_DYNAMIC_MUSIC); CMD3(CCC_Mask, "g_important_save", &psActorFlags, AF_IMPORTANT_SAVE); CMD3(CCC_Mask, "g_loading_stages", &psActorFlags, AF_LOADING_STAGES); + CMD3(CCC_Mask, "g_always_use_attitude_sensors", &psActorFlags, AF_ALWAYS_USE_ATTITUDE_SENSORS); CMD4(CCC_Integer, "g_inv_highlight_equipped", &g_inv_highlight_equipped, 0, 1); CMD4(CCC_Integer, "g_first_person_death", &g_first_person_death, 0, 1); diff --git a/src/xrGame/game_object_space.h b/src/xrGame/game_object_space.h index 774a722cfbd..7e31cd11226 100644 --- a/src/xrGame/game_object_space.h +++ b/src/xrGame/game_object_space.h @@ -64,6 +64,7 @@ enum ECallbackType : u32 eControllerPress, eControllerRelease, eControllerHold, + eControllerAttitudeChange, // Inventory eItemToBelt, eItemToSlot, diff --git a/src/xrGame/holder_custom.h b/src/xrGame/holder_custom.h index 1f154dbeffe..01ee9449ee4 100644 --- a/src/xrGame/holder_custom.h +++ b/src/xrGame/holder_custom.h @@ -24,13 +24,19 @@ class CHolderCustom virtual void UpdateEx(float fov){}; // called by owner virtual CHolderCustom* cast_holder_custom() { return this; } bool Engaged() { return m_owner != NULL; } + virtual void OnMouseMove(int x, int y) = 0; + virtual void OnKeyboardPress(int dik) = 0; virtual void OnKeyboardRelease(int dik) = 0; virtual void OnKeyboardHold(int dik) = 0; + virtual void OnControllerPress(int cmd, float x, float y) = 0; virtual void OnControllerRelease(int cmd, float x, float y) = 0; virtual void OnControllerHold(int cmd, float x, float y) = 0; + + virtual void OnControllerAttitudeChange(Fvector change) = 0; + // Inventory for the car virtual CInventory* GetInventory() = 0;