diff --git a/Client/game_sa/CHeliSA.h b/Client/game_sa/CHeliSA.h index 497fe46a70..5bf7e6387f 100644 --- a/Client/game_sa/CHeliSA.h +++ b/Client/game_sa/CHeliSA.h @@ -16,40 +16,37 @@ class CHeliSAInterface : public CAutomobileSAInterface { public: - std::uint8_t m_heliFlags; - - std::uint8_t _pad1[3]; - std::uint32_t m_leftRightSkid; - std::uint32_t m_steeringUpDown; - std::uint32_t m_steeringLeftRight; - std::uint32_t m_accelerationBreakStatus; - std::uint32_t field_99C; - std::uint32_t m_rotorZ; - std::uint32_t m_secondRotorZ; - std::uint32_t m_maxAltitude; - std::uint32_t field_9AC; - std::uint32_t m_minAltitude; - std::uint32_t field_9B4; - std::uint8_t field_9B8; - std::uint8_t m_numSwatOccupants; - std::uint8_t m_swatIDs[4]; - - std::uint8_t _pad2[2]; - std::uint32_t field_9C0[4]; - std::uint32_t field_9D0; - - std::uint32_t m_particlesList; - std::uint8_t field_9D8[24]; - std::uint32_t field_9F0; - CVector m_searchLightTarget; - std::uint32_t m_searchLightIntensity; - std::uint32_t field_A04; - std::uint32_t field_A08; - std::uint32_t m_gunflashFx; - std::uint8_t m_firingMultiplier; - std::uint8_t m_searchLightEnabled; - std::uint8_t _pad3[2]; - std::uint32_t field_A14; + std::uint8_t m_nHeliFlags; // 0x988 + std::uint8_t ___pad1[3]; // 0x989 + float m_fLeftRightSkid; // 0x98C + float m_fSteeringUpDown; // 0x990 + float m_fSteeringLeftRight; // 0x994 + float m_fAccelerationBreakStatus; // 0x998 + std::int32_t field_99C; // 0x99C + float m_fRotorZ; // 0x9A0 + float m_fSecondRotorZ; // 0x9A4 + float m_fMaxAltitude; // 0x9A8 + std::int32_t field_9AC; // 0x9AC + float m_fMinAltitude; // 0x9B0 + std::int32_t field_9B4; // 0x9B4 + std::uint8_t field_9B8; // 0x9B8 + std::uint8_t m_nNumSwatOccupants; // 0x9B9 + std::uint8_t m_anSwatIDs[4]; // 0x9BA + std::uint8_t ___pad2[2]; // 0x9BE + std::uint32_t field_9C0[4]; // 0x9C0 + std::int32_t field_9D0; // 0x9D0 + FxSystem_c** m_pParticlesList; // 0x9D4 + std::uint8_t field_9D8[24]; // 0x9D8 + std::int32_t field_9F0; // 0x9F0 + CVector m_vecSearchLightTarget; // 0x9F4 + float m_fSearchLightIntensity; // 0xA00 + std::int32_t field_A04; // 0xA04 + std::int32_t field_A08; // 0xA08 + FxSystem_c** m_ppGunflashFx; // 0xA0C + std::uint8_t m_nFiringMultiplier; // 0xA10 + bool m_bSearchLightEnabled; // 0xA11 + std::uint8_t ___pad3[2]; // 0xA12 + std::int32_t field_A14; // 0xA14 }; static_assert(sizeof(CHeliSAInterface) == 0xA18, "Invalid size for CHeliSAInterface"); diff --git a/Client/game_sa/CVehicleSA.cpp b/Client/game_sa/CVehicleSA.cpp index b4e7a2c438..ab899723be 100644 --- a/Client/game_sa/CVehicleSA.cpp +++ b/Client/game_sa/CVehicleSA.cpp @@ -20,6 +20,7 @@ #include "CProjectileInfoSA.h" #include "CTrainSA.h" #include "CPlaneSA.h" +#include "CHeliSA.h" #include "CVehicleSA.h" #include "CBoatSA.h" #include "CVisibilityPluginsSA.h" @@ -52,6 +53,62 @@ void _declspec(naked) HOOK_Vehicle_PreRender(void) } } +static bool __fastcall CanProcessFlyingCarStuff(CAutomobileSAInterface* vehicleInterface) +{ + SClientEntity* vehicle = pGame->GetPools()->GetVehicle((DWORD*)vehicleInterface); + if (!vehicle || !vehicle->pEntity) + return true; + + return vehicle->pEntity->GetVehicleRotorState(); +} + +static constexpr DWORD CONTINUE_CHeli_ProcessFlyingCarStuff = 0x6C4E82; +static constexpr DWORD RETURN_CHeli_ProcessFlyingCarStuff = 0x6C5404; +static void _declspec(naked) HOOK_CHeli_ProcessFlyingCarStuff() +{ + _asm + { + mov esi, ecx + mov al, [esi+36h] + + pushad + call CanProcessFlyingCarStuff + test al, al + jz skip + + popad + jmp CONTINUE_CHeli_ProcessFlyingCarStuff + + skip: + popad + jmp RETURN_CHeli_ProcessFlyingCarStuff + } +} + +static constexpr DWORD CONTINUE_CPlane_ProcessFlyingCarStuff = 0x6CB7D7; +static constexpr DWORD RETURN_CPlane_ProcessFlyingCarStuff = 0x6CC482; +static void _declspec(naked) HOOK_CPlane_ProcessFlyingCarStuff() +{ + _asm + { + push esi + mov esi, ecx + fnstsw ax + + pushad + call CanProcessFlyingCarStuff + test al, al + jz skip + + popad + jmp CONTINUE_CPlane_ProcessFlyingCarStuff + + skip: + popad + jmp RETURN_CPlane_ProcessFlyingCarStuff + } +} + namespace { bool ClumpDumpCB(RpAtomic* pAtomic, void* data) @@ -484,6 +541,29 @@ void CVehicleSA::SetTrainSpeed(float fSpeed) pInterface->m_fTrainSpeed = fSpeed; } +float CVehicleSA::GetHeliRotorSpeed() const +{ + return static_cast(m_pInterface)->m_wheelSpeed[1]; +} + +void CVehicleSA::SetHeliRotorSpeed(float speed) +{ + static_cast(GetInterface())->m_wheelSpeed[1] = speed; +} + +void CVehicleSA::SetVehicleRotorState(bool state, bool stopRotor, bool isHeli) noexcept +{ + m_rotorState = state; + + if (state || !stopRotor) + return; + + if (isHeli) + SetHeliRotorSpeed(0.0f); + else + SetPlaneRotorSpeed(0.0f); +} + void CVehicleSA::SetPlaneRotorSpeed(float fSpeed) { auto pInterface = static_cast(GetInterface()); @@ -1838,6 +1918,10 @@ void CVehicleSA::StaticSetHooks() { // Setup vehicle sun glare hook HookInstall(FUNC_CAutomobile_OnVehiclePreRender, (DWORD)HOOK_Vehicle_PreRender, 5); + + // Setup hooks to handle setVehicleRotorState function + HookInstall(FUNC_CHeli_ProcessFlyingCarStuff, (DWORD)HOOK_CHeli_ProcessFlyingCarStuff, 5); + HookInstall(FUNC_CPlane_ProcessFlyingCarStuff, (DWORD)HOOK_CPlane_ProcessFlyingCarStuff, 5); } void CVehicleSA::SetVehiclesSunGlareEnabled(bool bEnabled) diff --git a/Client/game_sa/CVehicleSA.h b/Client/game_sa/CVehicleSA.h index 9674a165cd..0d65184440 100644 --- a/Client/game_sa/CVehicleSA.h +++ b/Client/game_sa/CVehicleSA.h @@ -94,6 +94,9 @@ struct RwTexture; #define FUNC_CAutomobile_OnVehiclePreRender 0x6ABCFD #define FUNC_CVehicle_DoSunGlare 0x6DD6F0 +#define FUNC_CHeli_ProcessFlyingCarStuff 0x6C4E7D +#define FUNC_CPlane_ProcessFlyingCarStuff 0x6CB7D2 + // CClumpModelInfo::GetFrameFromName #define FUNC_CClumpModelInfo_GetFrameFromName 0x4C5400 @@ -422,6 +425,7 @@ class CVehicleSA : public virtual CVehicle, public virtual CPhysicalSA unsigned char m_ucVariant2; unsigned char m_ucVariantCount{0}; bool m_doorsUndamageable{false}; + bool m_rotorState{true}; std::array m_dummyPositions; @@ -552,7 +556,8 @@ class CVehicleSA : public virtual CVehicle, public virtual CPhysicalSA bool GetTakeLessDamage() { return GetVehicleInterface()->m_nVehicleFlags.bTakeLessDamage; }; bool GetTyresDontBurst() { return GetVehicleInterface()->m_nVehicleFlags.bTyresDontBurst; }; unsigned short GetAdjustablePropertyValue() { return *reinterpret_cast(reinterpret_cast(m_pInterface) + 2156); }; - float GetHeliRotorSpeed() { return *reinterpret_cast(reinterpret_cast(m_pInterface) + 2124); }; + float GetHeliRotorSpeed() const; + bool GetVehicleRotorState() const noexcept override { return m_rotorState; } float GetPlaneRotorSpeed(); unsigned long GetExplodeTime() { return *reinterpret_cast(reinterpret_cast(m_pInterface) + 1240); }; @@ -578,7 +583,8 @@ class CVehicleSA : public virtual CVehicle, public virtual CPhysicalSA { *reinterpret_cast(reinterpret_cast(m_pInterface) + 2156) = usAdjustableProperty; }; - void SetHeliRotorSpeed(float fSpeed) { *reinterpret_cast(reinterpret_cast(m_pInterface) + 2124) = fSpeed; }; + void SetHeliRotorSpeed(float speed); + void SetVehicleRotorState(bool state, bool stopRotor, bool isHeli) noexcept override; void SetPlaneRotorSpeed(float fSpeed); bool SetVehicleWheelRotation(float fWheelRot1, float fWheelRot2, float fWheelRot3, float fWheelRot4) noexcept; void SetExplodeTime(unsigned long ulTime) { *reinterpret_cast(reinterpret_cast(m_pInterface) + 1240) = ulTime; }; diff --git a/Client/mods/deathmatch/logic/CClientVehicle.cpp b/Client/mods/deathmatch/logic/CClientVehicle.cpp index 537f9457dd..de6a612938 100644 --- a/Client/mods/deathmatch/logic/CClientVehicle.cpp +++ b/Client/mods/deathmatch/logic/CClientVehicle.cpp @@ -1576,6 +1576,19 @@ void CClientVehicle::SetHeliRotorSpeed(float fSpeed) m_fHeliRotorSpeed = fSpeed; } +bool CClientVehicle::GetVehicleRotorState() const noexcept +{ + return m_pVehicle && (m_eVehicleType == CLIENTVEHICLE_HELI || m_eVehicleType == CLIENTVEHICLE_PLANE) ? m_pVehicle->GetVehicleRotorState() : m_rotorState; +} + +void CClientVehicle::SetVehicleRotorState(bool state, bool stopRotor) noexcept +{ + if (m_pVehicle && (m_eVehicleType == CLIENTVEHICLE_HELI || m_eVehicleType == CLIENTVEHICLE_PLANE)) + m_pVehicle->SetVehicleRotorState(state, stopRotor, GetVehicleType() == CLIENTVEHICLE_HELI); + + m_rotorState = state; +} + void CClientVehicle::SetPlaneRotorSpeed(float fSpeed) { if (m_pVehicle && m_eVehicleType == CLIENTVEHICLE_PLANE) diff --git a/Client/mods/deathmatch/logic/CClientVehicle.h b/Client/mods/deathmatch/logic/CClientVehicle.h index 76ec08eff1..304d6dc291 100644 --- a/Client/mods/deathmatch/logic/CClientVehicle.h +++ b/Client/mods/deathmatch/logic/CClientVehicle.h @@ -299,10 +299,12 @@ class CClientVehicle : public CClientStreamElement // TODO: Make the class remember on virtualization float GetHeliRotorSpeed(); float GetPlaneRotorSpeed(); + bool GetVehicleRotorState() const noexcept; bool GetRotorSpeed(float&); bool SetRotorSpeed(float); bool SetWheelsRotation(float fRot1, float fRot2, float fRot3, float fRot4) noexcept; + void SetVehicleRotorState(bool state, bool stopRotor) noexcept; void SetHeliRotorSpeed(float fSpeed); void SetPlaneRotorSpeed(float fSpeed); bool IsHeliSearchLightVisible(); @@ -673,6 +675,7 @@ class CClientVehicle : public CClientStreamElement uchar m_ucTrackID; bool m_bJustStreamedIn; bool m_bWheelScaleChanged; + bool m_rotorState{true}; // Time dependent error compensation interpolation struct diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.cpp index 6ac42bf386..411aba2e56 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.cpp @@ -92,6 +92,7 @@ void CLuaVehicleDefs::LoadFunctions() {"getVehicleModelWheelSize", ArgumentParser}, {"getVehicleWheelFrictionState", ArgumentParser}, {"getVehicleEntryPoints", ArgumentParser}, + {"getVehicleRotorState", ArgumentParser}, // Vehicle set funcs {"createVehicle", CreateVehicle}, @@ -156,6 +157,7 @@ void CLuaVehicleDefs::LoadFunctions() {"setVehicleWheelScale", ArgumentParser}, {"setVehicleModelWheelSize", ArgumentParser}, {"spawnVehicleFlyingComponent", ArgumentParser}, + {"setVehicleRotorState", ArgumentParser}, }; // Add functions @@ -244,6 +246,7 @@ void CLuaVehicleDefs::AddClass(lua_State* luaVM) lua_classfunction(luaVM, "getModelWheelSize", "getVehicleModelWheelSize"); lua_classfunction(luaVM, "getWheelFrictionState", "getVehicleWheelFrictionState"); lua_classfunction(luaVM, "getEntryPoints", ArgumentParser); + lua_classfunction(luaVM, "getRotorState", "getVehicleRotorState"); lua_classfunction(luaVM, "setComponentVisible", "setVehicleComponentVisible"); lua_classfunction(luaVM, "setSirensOn", "setVehicleSirensOn"); @@ -292,6 +295,7 @@ void CLuaVehicleDefs::AddClass(lua_State* luaVM) lua_classfunction(luaVM, "setVariant", "setVehicleVariant"); lua_classfunction(luaVM, "setWheelScale", "setVehicleWheelScale"); lua_classfunction(luaVM, "setModelWheelSize", "setVehicleModelWheelSize"); + lua_classfunction(luaVM, "setRotorState", "setVehicleRotorState"); lua_classfunction(luaVM, "resetComponentPosition", "resetVehicleComponentPosition"); lua_classfunction(luaVM, "resetComponentRotation", "resetVehicleComponentRotation"); @@ -350,6 +354,7 @@ void CLuaVehicleDefs::AddClass(lua_State* luaVM) lua_classvariable(luaVM, "gravity", SetVehicleGravity, OOP_GetVehicleGravity); lua_classvariable(luaVM, "turnVelocity", SetVehicleTurnVelocity, OOP_GetVehicleTurnVelocity); lua_classvariable(luaVM, "wheelScale", "setVehicleWheelScale", "getVehicleWheelScale"); + lua_classvariable(luaVM, "rotorState", "setVehicleRotorState", "getVehicleRotorState"); lua_registerclass(luaVM, "Vehicle", "Element"); } @@ -4340,3 +4345,17 @@ bool CLuaVehicleDefs::SpawnVehicleFlyingComponent(CClientVehicle* const vehicle, return vehicle->SpawnFlyingComponent(partNodeIndex, collisionType, removalTime.value_or(-1)); } + +bool CLuaVehicleDefs::SetVehicleRotorState(CClientVehicle* vehicle, bool state, std::optional stopRotor) noexcept +{ + if (vehicle->GetVehicleType() != eClientVehicleType::CLIENTVEHICLE_HELI && vehicle->GetVehicleType() != eClientVehicleType::CLIENTVEHICLE_PLANE) + return false; + + vehicle->SetVehicleRotorState(state, stopRotor.value_or(true)); + return true; +} + +bool CLuaVehicleDefs::GetVehicleRotorState(CClientVehicle* vehicle) noexcept +{ + return vehicle->GetVehicleRotorState(); +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.h index 28c2f5e372..237d4dad78 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.h @@ -166,6 +166,9 @@ class CLuaVehicleDefs : public CLuaDefs static bool SetVehicleModelWheelSize(const unsigned short usModel, const eResizableVehicleWheelGroup eWheelGroup, const float fWheelSize); static int GetVehicleWheelFrictionState(CClientVehicle* pVehicle, unsigned char wheel); + static bool SetVehicleRotorState(CClientVehicle* const vehicle, bool state, std::optional stopRotor) noexcept; + static bool GetVehicleRotorState(CClientVehicle* const vehicle) noexcept; + // Components LUA_DECLARE(SetVehicleComponentPosition); LUA_DECLARE_OOP(GetVehicleComponentPosition); diff --git a/Client/sdk/game/CVehicle.h b/Client/sdk/game/CVehicle.h index a5a4f1cdd4..a64755fcfb 100644 --- a/Client/sdk/game/CVehicle.h +++ b/Client/sdk/game/CVehicle.h @@ -198,7 +198,8 @@ class CVehicle : public virtual CPhysical virtual bool GetTakeLessDamage() = 0; virtual bool GetTyresDontBurst() = 0; virtual unsigned short GetAdjustablePropertyValue() = 0; - virtual float GetHeliRotorSpeed() = 0; + virtual float GetHeliRotorSpeed() const = 0; + virtual bool GetVehicleRotorState() const noexcept = 0; virtual float GetPlaneRotorSpeed() = 0; virtual unsigned long GetExplodeTime() = 0; @@ -220,6 +221,7 @@ class CVehicle : public virtual CPhysical virtual void SetTyresDontBurst(bool bTyresDontBurst) = 0; virtual void SetAdjustablePropertyValue(unsigned short usAdjustableProperty) = 0; virtual void SetHeliRotorSpeed(float fSpeed) = 0; + virtual void SetVehicleRotorState(bool state, bool stopRotor, bool isHeli) noexcept = 0; virtual void SetPlaneRotorSpeed(float fSpeed) = 0; virtual bool SetVehicleWheelRotation(float fRot1, float fRot2, float fRot3, float fRot4) noexcept = 0; virtual void SetTaxiLightOn(bool bLightState) = 0;