Skip to content

Add new world special property (vehicle_engine_autostart) #4238

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions Client/game_sa/CVehicleSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
*****************************************************************************/

#include "StdInc.h"
#include <core/CCoreInterface.h>
#include <multiplayer/CMultiplayer.h>
#include "CAutomobileSA.h"
#include "CBikeSA.h"
#include "CCameraSA.h"
Expand All @@ -28,7 +30,8 @@
#include "gamesa_renderware.h"
#include "CFireManagerSA.h"

extern CGameSA* pGame;
extern CCoreInterface* g_pCore;
extern CGameSA* pGame;

static BOOL m_bVehicleSunGlare = false;
_declspec(naked) void DoVehicleSunGlare(void* this_)
Expand All @@ -54,13 +57,39 @@ void _declspec(naked) HOOK_Vehicle_PreRender(void)
}
}

static float& fTimeStep = *(float*)(0xB7CB5C);
static bool __fastcall CanProcessFlyingCarStuff(CAutomobileSAInterface* vehicleInterface)
{
SClientEntity<CVehicleSA>* vehicle = pGame->GetPools()->GetVehicle((DWORD*)vehicleInterface);
if (!vehicle || !vehicle->pEntity)
return true;

return vehicle->pEntity->GetVehicleRotorState();
if (vehicle->pEntity->GetVehicleRotorState())
{
if (g_pCore->GetMultiplayer()->IsVehicleEngineAutoStartEnabled()) // keep default behavior
return true;

if (vehicle->pEntity->GetEntityStatus() != eEntityStatus::STATUS_PHYSICS && !vehicle->pEntity->IsBeingDriven())
{
vehicle->pEntity->SetEntityStatus(eEntityStatus::STATUS_PHYSICS); // this will make rotors spin without driver when engine is on
return false;
}
if (!vehicle->pEntity->IsEngineOn())
{
// Smoothly change rotors speed to 0
float speed = vehicle->pEntity->GetHeliRotorSpeed();
if (speed > 0)
vehicle->pEntity->SetHeliRotorSpeed(std::max(0.0f, speed - fTimeStep * 0.00055f)); // 0x6C4EB7

speed = vehicle->pEntity->GetPlaneRotorSpeed();
if (speed > 0)
vehicle->pEntity->SetPlaneRotorSpeed(std::max(0.0f, speed - fTimeStep * 0.003f)); // 0x6CC145

return false;
}
return true;
}
return false;
}

static constexpr DWORD CONTINUE_CHeli_ProcessFlyingCarStuff = 0x6C4E82;
Expand Down
20 changes: 20 additions & 0 deletions Client/mods/deathmatch/logic/CClientGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6057,6 +6057,9 @@ bool CClientGame::SetWorldSpecialProperty(const WorldSpecialProperty property, c
case WorldSpecialProperty::VEHICLEBURNEXPLOSIONS:
g_pGame->SetVehicleBurnExplosionsEnabled(enabled);
break;
case WorldSpecialProperty::VEHICLE_ENGINE_AUTOSTART:
SetVehicleEngineAutoStartEnabled(enabled);
break;
default:
return false;
}
Expand Down Expand Up @@ -6103,6 +6106,8 @@ bool CClientGame::IsWorldSpecialProperty(const WorldSpecialProperty property)
return m_pVehicleManager->IsSpawnFlyingComponentEnabled();
case WorldSpecialProperty::VEHICLEBURNEXPLOSIONS:
return g_pGame->IsVehicleBurnExplosionsEnabled();
case WorldSpecialProperty::VEHICLE_ENGINE_AUTOSTART:
return IsVehicleEngineAutoStartEnabled();
}

return false;
Expand Down Expand Up @@ -6138,6 +6143,20 @@ bool CClientGame::IsWeaponRenderEnabled() const
return g_pGame->IsWeaponRenderEnabled();
}

void CClientGame::SetVehicleEngineAutoStartEnabled(bool enabled)
{
if (enabled == g_pMultiplayer->IsVehicleEngineAutoStartEnabled())
return;

g_pMultiplayer->SetVehicleEngineAutoStartEnabled(enabled);
m_pVehicleManager->ResetNotControlledRotors(enabled);
}

bool CClientGame::IsVehicleEngineAutoStartEnabled() const
{
return g_pMultiplayer->IsVehicleEngineAutoStartEnabled();
}

#pragma code_seg(".text")
bool CClientGame::VerifySADataFiles(int iEnableClientChecks)
{
Expand Down Expand Up @@ -6823,6 +6842,7 @@ void CClientGame::ResetWorldProperties(const ResetWorldPropsInfo& resetPropsInfo
g_pGame->SetIgnoreFireStateEnabled(false);
m_pVehicleManager->SetSpawnFlyingComponentEnabled(true);
g_pGame->SetVehicleBurnExplosionsEnabled(true);
SetVehicleEngineAutoStartEnabled(true);
}

// Reset all setWorldProperty to default
Expand Down
3 changes: 3 additions & 0 deletions Client/mods/deathmatch/logic/CClientGame.h
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,9 @@ class CClientGame
void SetWeaponRenderEnabled(bool enabled);
bool IsWeaponRenderEnabled() const;

void SetVehicleEngineAutoStartEnabled(bool enabled);
bool IsVehicleEngineAutoStartEnabled() const;

void ResetWorldProperties(const ResetWorldPropsInfo& resetPropsInfo);

CTransferBox* GetTransferBox() { return m_pTransferBox; };
Expand Down
17 changes: 17 additions & 0 deletions Client/mods/deathmatch/logic/CClientVehicleManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -791,3 +791,20 @@ void CClientVehicleManager::RestreamVehicleUpgrades(unsigned short usModel)
pVehicle->GetUpgrades()->RestreamVehicleUpgrades(usModel);
}
}

void CClientVehicleManager::ResetNotControlledRotors(bool engineAutoStart)
{
// Reset rotors to original or custom state for loaded vehicles without controller
// Custom state allows rotors to spin without driver inside (if engine is on)
eEntityStatus status = engineAutoStart ? eEntityStatus::STATUS_ABANDONED : eEntityStatus::STATUS_PHYSICS;
for (auto& pVehicle : m_List)
{
if (pVehicle->GetGameEntity() && pVehicle->GetVehicleRotorState() && !pVehicle->IsDriven())
{
float speed = (!engineAutoStart && pVehicle->IsEngineOn()) ? 0.001f : 0.0f;
pVehicle->GetGameEntity()->SetEntityStatus(status);
pVehicle->SetHeliRotorSpeed(speed);
pVehicle->SetPlaneRotorSpeed(speed);
}
}
}
2 changes: 2 additions & 0 deletions Client/mods/deathmatch/logic/CClientVehicleManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ class CClientVehicleManager
bool IsSpawnFlyingComponentEnabled() const noexcept { return m_spawnFlyingComponentsDuringRecreate; }
void SetSpawnFlyingComponentEnabled(bool isEnabled) noexcept { m_spawnFlyingComponentsDuringRecreate = isEnabled; }

void ResetNotControlledRotors(bool engineAutoStart);

protected:
CClientManager* m_pManager;
bool m_bCanRemoveFromList;
Expand Down
1 change: 1 addition & 0 deletions Client/mods/deathmatch/logic/CPacketHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2402,6 +2402,7 @@ void CPacketHandler::Packet_MapInfo(NetBitStreamInterface& bitStream)
g_pClientGame->SetWorldSpecialProperty(WorldSpecialProperty::IGNOREFIRESTATE, wsProps.data6.ignoreFireState);
g_pClientGame->SetWorldSpecialProperty(WorldSpecialProperty::FLYINGCOMPONENTS, wsProps.data7.flyingcomponents);
g_pClientGame->SetWorldSpecialProperty(WorldSpecialProperty::VEHICLEBURNEXPLOSIONS, wsProps.data8.vehicleburnexplosions);
g_pClientGame->SetWorldSpecialProperty(WorldSpecialProperty::VEHICLE_ENGINE_AUTOSTART, wsProps.data9.vehicleEngineAutoStart);

float fJetpackMaxHeight = 100;
if (!bitStream.Read(fJetpackMaxHeight))
Expand Down
19 changes: 19 additions & 0 deletions Client/multiplayer_sa/CMultiplayerSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6634,6 +6634,25 @@ void CMultiplayerSA::SetAutomaticVehicleStartupOnPedEnter(bool bSet)
MemSet((char*)0x64BC0D, 0x90, 6);
}

bool CMultiplayerSA::IsVehicleEngineAutoStartEnabled() const noexcept
{
return *(unsigned char*)0x64BC03 == 0x75;
}

void CMultiplayerSA::SetVehicleEngineAutoStartEnabled(bool enabled)
{
if (enabled)
{
MemCpy((void*)0x64BC03, "\x75\x05\x80\xC9\x10", 5);
MemCpy((void*)0x6C4EA9, "\x8A\x86\x28\x04", 4);
}
else
{
MemSet((void*)0x64BC03, 0x90, 5); // prevent vehicle engine from turning on (driver enter)
MemCpy((void*)0x6C4EA9, "\xE9\x15\x03\x00", 4); // prevent aircraft engine from turning off (driver exit)
}
}

// Storage
CVehicleSAInterface* pHeliKiller = NULL;
CEntitySAInterface* pHitByHeli = NULL;
Expand Down
3 changes: 3 additions & 0 deletions Client/multiplayer_sa/CMultiplayerSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,9 @@ class CMultiplayerSA : public CMultiplayer

void SetAutomaticVehicleStartupOnPedEnter(bool bSet);

bool IsVehicleEngineAutoStartEnabled() const noexcept override;
void SetVehicleEngineAutoStartEnabled(bool enabled) override;

void SetPedTargetingMarkerEnabled(bool bEnable);
bool IsPedTargetingMarkerEnabled();
bool IsConnected();
Expand Down
3 changes: 3 additions & 0 deletions Client/sdk/multiplayer/CMultiplayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,9 @@ class CMultiplayer

virtual void SetAutomaticVehicleStartupOnPedEnter(bool bSet) = 0;

virtual bool IsVehicleEngineAutoStartEnabled() const noexcept = 0;
virtual void SetVehicleEngineAutoStartEnabled(bool enabled) = 0;

virtual void SetPedTargetingMarkerEnabled(bool bEnabled) = 0;
virtual bool IsPedTargetingMarkerEnabled() = 0;

Expand Down
5 changes: 4 additions & 1 deletion Server/mods/deathmatch/logic/CGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ CGame::CGame() : m_FloodProtect(4, 30000, 30000) // Max of 4 connecti
m_WorldSpecialProps[WorldSpecialProperty::IGNOREFIRESTATE] = false;
m_WorldSpecialProps[WorldSpecialProperty::FLYINGCOMPONENTS] = true;
m_WorldSpecialProps[WorldSpecialProperty::VEHICLEBURNEXPLOSIONS] = true;
m_WorldSpecialProps[WorldSpecialProperty::VEHICLE_ENGINE_AUTOSTART] = true;

m_JetpackWeapons[WEAPONTYPE_MICRO_UZI] = true;
m_JetpackWeapons[WEAPONTYPE_TEC9] = true;
Expand Down Expand Up @@ -3405,7 +3406,8 @@ void CGame::Packet_Vehicle_InOut(CVehicleInOutPacket& Packet)
pPed->SetVehicleAction(CPed::VEHICLEACTION_NONE);

// Update our engine State
pVehicle->SetEngineOn(true);
if (g_pGame->IsWorldSpecialPropertyEnabled(WorldSpecialProperty::VEHICLE_ENGINE_AUTOSTART))
pVehicle->SetEngineOn(true);

// Tell everyone he's in (they should warp him in)
CVehicleInOutPacket Reply(PedID, VehicleID, ucOccupiedSeat, VEHICLE_NOTIFY_IN_RETURN);
Expand Down Expand Up @@ -4524,6 +4526,7 @@ void CGame::ResetWorldProperties(const ResetWorldPropsInfo& resetPropsInfo)
g_pGame->SetWorldSpecialPropertyEnabled(WorldSpecialProperty::IGNOREFIRESTATE, false);
g_pGame->SetWorldSpecialPropertyEnabled(WorldSpecialProperty::FLYINGCOMPONENTS, true);
g_pGame->SetWorldSpecialPropertyEnabled(WorldSpecialProperty::VEHICLEBURNEXPLOSIONS, true);
g_pGame->SetWorldSpecialPropertyEnabled(WorldSpecialProperty::VEHICLE_ENGINE_AUTOSTART, true);
}

// Reset all weather stuff like heat haze, wind velocity etc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4262,7 +4262,7 @@ bool CStaticFunctionDefinitions::WarpPedIntoVehicle(CPed* pPed, CVehicle* pVehic
pPed->SetVehicleAction(CPed::VEHICLEACTION_NONE);

// If he's the driver, switch on the engine
if (uiSeat == 0)
if (uiSeat == 0 && g_pGame->IsWorldSpecialPropertyEnabled(WorldSpecialProperty::VEHICLE_ENGINE_AUTOSTART))
pVehicle->SetEngineOn(true);

// Tell all the players
Expand Down
1 change: 1 addition & 0 deletions Server/mods/deathmatch/logic/packets/CMapInfoPacket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ bool CMapInfoPacket::Write(NetBitStreamInterface& BitStream) const
wsProps.data6.ignoreFireState = g_pGame->IsWorldSpecialPropertyEnabled(WorldSpecialProperty::IGNOREFIRESTATE);
wsProps.data7.flyingcomponents = g_pGame->IsWorldSpecialPropertyEnabled(WorldSpecialProperty::FLYINGCOMPONENTS);
wsProps.data8.vehicleburnexplosions = g_pGame->IsWorldSpecialPropertyEnabled(WorldSpecialProperty::VEHICLEBURNEXPLOSIONS);
wsProps.data9.vehicleEngineAutoStart = g_pGame->IsWorldSpecialPropertyEnabled(WorldSpecialProperty::VEHICLE_ENGINE_AUTOSTART);
BitStream.Write(&wsProps);
}

Expand Down
1 change: 1 addition & 0 deletions Shared/mods/deathmatch/logic/Enums.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ ADD_ENUM(WorldSpecialProperty::TUNNELWEATHERBLEND, "tunnelweatherblend")
ADD_ENUM(WorldSpecialProperty::IGNOREFIRESTATE, "ignorefirestate")
ADD_ENUM(WorldSpecialProperty::FLYINGCOMPONENTS, "flyingcomponents")
ADD_ENUM(WorldSpecialProperty::VEHICLEBURNEXPLOSIONS, "vehicleburnexplosions")
ADD_ENUM(WorldSpecialProperty::VEHICLE_ENGINE_AUTOSTART, "vehicle_engine_autostart")
IMPLEMENT_ENUM_CLASS_END("world-special-property")

IMPLEMENT_ENUM_BEGIN(ePacketID)
Expand Down
1 change: 1 addition & 0 deletions Shared/mods/deathmatch/logic/Enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ enum class WorldSpecialProperty
IGNOREFIRESTATE,
FLYINGCOMPONENTS,
VEHICLEBURNEXPLOSIONS,
VEHICLE_ENGINE_AUTOSTART,
};
DECLARE_ENUM_CLASS(WorldSpecialProperty);

Expand Down
18 changes: 18 additions & 0 deletions Shared/sdk/net/SyncStructures.h
Original file line number Diff line number Diff line change
Expand Up @@ -2083,6 +2083,10 @@ struct SWorldSpecialPropertiesStateSync : public ISyncStructure
{
BITCOUNT8 = 1
};
enum
{
BITCOUNT9 = 1
};

bool Read(NetBitStreamInterface& bitStream)
{
Expand Down Expand Up @@ -2121,6 +2125,11 @@ struct SWorldSpecialPropertiesStateSync : public ISyncStructure
isOK &= bitStream.ReadBits(reinterpret_cast<char*>(&data8), BITCOUNT8);
else
data8.vehicleburnexplosions = true;

if (bitStream.Can(eBitStreamVersion::WorldSpecialProperty_VehicleEngineAutoStart))
isOK &= bitStream.ReadBits(reinterpret_cast<char*>(&data9), BITCOUNT9);
else
data9.vehicleEngineAutoStart = true;

//// Example for adding item:
// if (bitStream.Can(eBitStreamVersion::YourProperty))
Expand Down Expand Up @@ -2154,6 +2163,9 @@ struct SWorldSpecialPropertiesStateSync : public ISyncStructure
if (bitStream.Can(eBitStreamVersion::WorldSpecialProperty_VehicleBurnExplosions))
bitStream.WriteBits(reinterpret_cast<const char*>(&data8), BITCOUNT8);

if (bitStream.Can(eBitStreamVersion::WorldSpecialProperty_VehicleEngineAutoStart))
bitStream.WriteBits(reinterpret_cast<const char*>(&data9), BITCOUNT9);

//// Example for adding item:
// if (bitStream.Can(eBitStreamVersion::YourProperty))
// bitStream.WriteBits(reinterpret_cast<const char*>(&data9), BITCOUNT9);
Expand Down Expand Up @@ -2210,6 +2222,11 @@ struct SWorldSpecialPropertiesStateSync : public ISyncStructure
{
bool vehicleburnexplosions : 1;
} data8;

struct
{
bool vehicleEngineAutoStart : 1;
} data9;

SWorldSpecialPropertiesStateSync()
{
Expand All @@ -2233,6 +2250,7 @@ struct SWorldSpecialPropertiesStateSync : public ISyncStructure
data6.ignoreFireState = false;
data7.flyingcomponents = true;
data8.vehicleburnexplosions = true;
data9.vehicleEngineAutoStart = true;
}
};

Expand Down
4 changes: 4 additions & 0 deletions Shared/sdk/net/bitstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,10 @@ enum class eBitStreamVersion : unsigned short
// 2025-05-26
ServersideBuildingElement,

// Add "vehenginemanualmode" to setWorldSpecialPropertyEnabled
// 2025-06-02
WorldSpecialProperty_VehicleEngineAutoStart,

// This allows us to automatically increment the BitStreamVersion when things are added to this enum.
// Make sure you only add things above this comment.
Next,
Expand Down
Loading