Skip to content
Open
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
9 changes: 9 additions & 0 deletions Client/game_sa/CVehicleSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ static void __fastcall RehideRhinoMiddleWheels(CAutomobileSAInterface* vehicle)
}
}

static void __cdecl OnAutomobilePostPreRender(CAutomobileSAInterface* vehicleInterface)
{
if (g_pCore && g_pCore->GetMultiplayer())
g_pCore->GetMultiplayer()->RestoreVehicleSuspensionAfterAutomobilePreRender(reinterpret_cast<CEntitySAInterface*>(vehicleInterface));
}

static void __declspec(naked) HOOK_Vehicle_PreRender(void)
{
MTA_VERIFY_HOOK_LOCAL_SIZE;
Expand All @@ -77,6 +83,9 @@ static void __declspec(naked) HOOK_Vehicle_PreRender(void)
pushad
mov ecx, esi
call RehideRhinoMiddleWheels
push esi
call OnAutomobilePostPreRender
add esp, 4
popad

mov [esp+0D4h], edi
Expand Down
14 changes: 14 additions & 0 deletions Client/mods/deathmatch/logic/CClientGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo())
g_pMultiplayer->SetPreWorldProcessHandler(CClientGame::StaticPreWorldProcessHandler);
g_pMultiplayer->SetPostWorldProcessHandler(CClientGame::StaticPostWorldProcessHandler);
g_pMultiplayer->SetPostWorldProcessPedsAfterPreRenderHandler(CClientGame::StaticPostWorldProcessPedsAfterPreRenderHandler);
g_pMultiplayer->SetVehicleAutomobilePostPreRenderHandler(CClientGame::StaticVehicleAutomobilePostPreRenderHandler);
g_pMultiplayer->SetPreFxRenderHandler(CClientGame::StaticPreFxRenderHandler);
g_pMultiplayer->SetPostColorFilterRenderHandler(CClientGame::StaticPostColorFilterRenderHandler);
g_pMultiplayer->SetPreHudRenderHandler(CClientGame::StaticPreHudRenderHandler);
Expand Down Expand Up @@ -505,6 +506,7 @@ CClientGame::~CClientGame()
g_pMultiplayer->SetPreWorldProcessHandler(NULL);
g_pMultiplayer->SetPostWorldProcessHandler(NULL);
g_pMultiplayer->SetPostWorldProcessPedsAfterPreRenderHandler(nullptr);
g_pMultiplayer->SetVehicleAutomobilePostPreRenderHandler(nullptr);
g_pMultiplayer->SetPreFxRenderHandler(NULL);
g_pMultiplayer->SetPostColorFilterRenderHandler(nullptr);
g_pMultiplayer->SetPreHudRenderHandler(NULL);
Expand Down Expand Up @@ -3661,6 +3663,18 @@ void CClientGame::StaticPostWorldProcessPedsAfterPreRenderHandler()
g_pClientGame->PostWorldProcessPedsAfterPreRenderHandler();
}

void CClientGame::StaticVehicleAutomobilePostPreRenderHandler(CEntitySAInterface* pGameEntity)
{
if (!pGameEntity || !g_pClientGame)
return;

CClientEntity* pClientEntity = g_pGame->GetPools()->GetClientEntity((DWORD*)pGameEntity);
if (!pClientEntity || pClientEntity->GetType() != CCLIENTVEHICLE)
return;

static_cast<CClientVehicle*>(pClientEntity)->ApplyWheelComponentPositionsAfterPreRender();
}

void CClientGame::StaticPreFxRenderHandler()
{
// RenderFadingInEntities is done at this point, so alpha entity list callbacks
Expand Down
1 change: 1 addition & 0 deletions Client/mods/deathmatch/logic/CClientGame.h
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,7 @@ class CClientGame
static void StaticPreWorldProcessHandler();
static void StaticPostWorldProcessHandler();
static void StaticPostWorldProcessPedsAfterPreRenderHandler();
static void StaticVehicleAutomobilePostPreRenderHandler(CEntitySAInterface* pVehicle);
static void StaticPreFxRenderHandler();
static void StaticPostColorFilterRenderHandler();
static void StaticPreHudRenderHandler();
Expand Down
20 changes: 20 additions & 0 deletions Client/mods/deathmatch/logic/CClientVehicle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4641,6 +4641,26 @@ void CClientVehicle::RemoveVehicleSirens()
m_tSirenBeaconInfo.m_ucSirenCount = 0;
}

void CClientVehicle::ApplyWheelComponentPositionsAfterPreRender()
{
if (!m_pVehicle)
return;

// PreRender overwrites wheel dummy Z from suspension lines every frame; re-apply
// script offsets after the wheel block so setVehicleComponentPosition Z sticks.
static const char* wheelComponents[] = {"wheel_lf_dummy", "wheel_rf_dummy", "wheel_lb_dummy", "wheel_rb_dummy"};

for (const char* componentName : wheelComponents)
{
const auto it = m_ComponentData.find(componentName);
if (it == m_ComponentData.end() || !it->second.m_bPositionChanged)
continue;

if (it->second.m_vecOriginalComponentPosition != it->second.m_vecComponentPosition)
SetComponentPosition(componentName, it->second.m_vecComponentPosition);
}
}

bool CClientVehicle::SetComponentPosition(const SString& vehicleComponent, CVector vecPosition, EComponentBaseType inputBase)
{
// Ensure position is parent relative
Expand Down
2 changes: 2 additions & 0 deletions Client/mods/deathmatch/logic/CClientVehicle.h
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,8 @@ class CClientVehicle : public CClientStreamElement
bool SetComponentPosition(const SString& vehicleComponent, CVector vecPosition, EComponentBaseType base = EComponentBase::PARENT);
bool GetComponentPosition(const SString& vehicleComponent, CVector& vecPosition, EComponentBaseType base = EComponentBase::PARENT);

void ApplyWheelComponentPositionsAfterPreRender();

bool ResetComponentRotation(const SString& vehicleComponent);
bool SetComponentRotation(const SString& vehicleComponent, CVector vecRotation, EComponentBaseType base = EComponentBase::PARENT);
bool GetComponentRotation(const SString& vehicleComponent, CVector& vecRotation, EComponentBaseType base = EComponentBase::PARENT);
Expand Down
78 changes: 78 additions & 0 deletions Client/multiplayer_sa/CMultiplayerSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@ DWORD RETURN_CHandlingData_isNotFWD = 0x6A04C3;
#define CALL_CMonsterTruck_ProcessEntityCollision 0x6C8B9E
DWORD RETURN_ProcessEntityCollision = 0x4185C0;

// CAutomobile::PreRender entry - swap per-model suspension lines to per-vehicle private
// data before the wheel positioning block reads them (same pattern as collision hook).
#define HOOKPOS_CAutomobile_PreRender 0x6AAB50
#define HOOKSIZE_CAutomobile_PreRender 5
DWORD RETURN_CAutomobile_PreRender = 0x6AAB55;

#define HOOKPOS_PreFxRender 0x049E650
DWORD RETURN_PreFxRender = 0x0404D1E;

Expand Down Expand Up @@ -418,6 +424,7 @@ ObjectBreakHandler* m_pObjectBreakHandler = NULL;
FxSystemDestructionHandler* m_pFxSystemDestructionHandler = NULL;
DrivebyAnimationHandler* m_pDrivebyAnimationHandler = NULL;
AudioZoneRadioSwitchHandler* m_pAudioZoneRadioSwitchHandler = NULL;
VehicleAutomobilePostPreRenderHandler* m_pVehicleAutomobilePostPreRenderHandler = nullptr;

CEntitySAInterface* dwSavedPlayerPointer = 0;
CEntitySAInterface* activeEntityForStreaming = 0; // the entity that the streaming system considers active
Expand Down Expand Up @@ -2729,6 +2736,11 @@ void CMultiplayerSA::SetAudioZoneRadioSwitchHandler(AudioZoneRadioSwitchHandler*
m_pAudioZoneRadioSwitchHandler = pHandler;
}

void CMultiplayerSA::SetVehicleAutomobilePostPreRenderHandler(VehicleAutomobilePostPreRenderHandler* pHandler)
{
m_pVehicleAutomobilePostPreRenderHandler = pHandler;
}

// What we do here is check if the idle handler has been set
bool CMultiplayerSA::IsConnected()
{
Expand Down Expand Up @@ -6628,6 +6640,55 @@ void SetModelSuspensionLines(CVehicleSAInterface* pVehicleIntf, void* pSuspensio
CModelInfo* pModelInfo = pGameInterface->GetModelInfo(pVehicleIntf->m_pVehicle->GetModelIndex());
pModelInfo->SetVehicleSuspensionData(pSuspensionLines);
}

static void* g_pPreRenderSavedSuspensionLines = nullptr;
static CVehicleSAInterface* g_pPreRenderSuspensionVehicle = nullptr;

static bool ShouldUsePrivateSuspensionLinesForPreRender(CVehicleSAInterface* pVehicleIntf)
{
if (!pVehicleIntf)
return false;

CVehicle* pVehicle = pVehicleIntf->m_pVehicle;
if (!pVehicle)
return false;

CModelInfo* pModelInfo = pGameInterface->GetModelInfo(pVehicle->GetModelIndex());
return pModelInfo && (pModelInfo->IsCar() || pModelInfo->IsMonsterTruck());
}

static void __cdecl BeginAutomobilePreRenderSuspension(CVehicleSAInterface* pVehicleIntf)
{
if (!ShouldUsePrivateSuspensionLinesForPreRender(pVehicleIntf))
return;

g_pPreRenderSavedSuspensionLines = SetModelSuspensionLinesToVehiclePrivate(pVehicleIntf);
g_pPreRenderSuspensionVehicle = pVehicleIntf;
}

static void __declspec(naked) HOOK_CAutomobile_PreRenderStart()
{
MTA_VERIFY_HOOK_LOCAL_SIZE;

// Replaced bytes at 0x6AAB50: sub esp, 74h; push ebx; push esi
// clang-format off
__asm
{
sub esp, 74h
push ebx
push esi

pushad
push ecx
call BeginAutomobilePreRenderSuspension
add esp, 4
popad

jmp RETURN_CAutomobile_PreRender
}
// clang-format on
}

// Some variables.
DWORD dwSuspensionChangedJump = 0x4185C0;
bool bSuspensionChanged = false;
Expand Down Expand Up @@ -6722,6 +6783,23 @@ void CMultiplayerSA::UpdateVehicleSuspension() const noexcept
{
HookInstallCall(CALL_CAutomobile_ProcessEntityCollision, reinterpret_cast<DWORD>(HOOK_ProcessVehicleCollision));
HookInstallCall(CALL_CMonsterTruck_ProcessEntityCollision, reinterpret_cast<DWORD>(HOOK_ProcessVehicleCollision));
HookInstall(HOOKPOS_CAutomobile_PreRender, reinterpret_cast<DWORD>(HOOK_CAutomobile_PreRenderStart), HOOKSIZE_CAutomobile_PreRender);
}

void CMultiplayerSA::RestoreVehicleSuspensionAfterAutomobilePreRender(CEntitySAInterface* pVehicleIntf)
{
auto* pVehicle = static_cast<CVehicleSAInterface*>(pVehicleIntf);

// Restore the per-model suspension lines that were swapped at PreRender entry.
if (pVehicle && pVehicle == g_pPreRenderSuspensionVehicle && g_pPreRenderSavedSuspensionLines)
{
SetModelSuspensionLines(pVehicle, g_pPreRenderSavedSuspensionLines);
g_pPreRenderSavedSuspensionLines = nullptr;
g_pPreRenderSuspensionVehicle = nullptr;
}

if (m_pVehicleAutomobilePostPreRenderHandler)
m_pVehicleAutomobilePostPreRenderHandler(pVehicleIntf);
}

// Variables
Expand Down
2 changes: 2 additions & 0 deletions Client/multiplayer_sa/CMultiplayerSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,8 @@ class CMultiplayerSA : public CMultiplayer
CLimits* GetLimits() { return &m_limits; }

void UpdateVehicleSuspension() const noexcept;
void RestoreVehicleSuspensionAfterAutomobilePreRender(CEntitySAInterface* pVehicleIntf) override;
void SetVehicleAutomobilePostPreRenderHandler(VehicleAutomobilePostPreRenderHandler* pHandler) override;

virtual void FlushClothesCache();
virtual void SetFastClothesLoading(EFastClothesLoading fastClothesLoading);
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 @@ -136,6 +136,7 @@ typedef void(FxSystemDestructionHandler)(void* pFxSA);
typedef AnimationId(DrivebyAnimationHandler)(AnimationId animGroup, AssocGroupId animId);
typedef void(PedStepHandler)(CPedSAInterface* pPed, bool bFoot);
typedef void(AudioZoneRadioSwitchHandler)(DWORD dwStationID);
typedef void(VehicleAutomobilePostPreRenderHandler)(CEntitySAInterface* pVehicle);

using VehicleWeaponHitHandler = void(SVehicleWeaponHitEvent& event);

Expand Down Expand Up @@ -432,6 +433,8 @@ class CMultiplayer
virtual CLimits* GetLimits() = 0;

virtual void UpdateVehicleSuspension() const noexcept = 0;
virtual void RestoreVehicleSuspensionAfterAutomobilePreRender(CEntitySAInterface* pVehicleIntf) = 0;
virtual void SetVehicleAutomobilePostPreRenderHandler(VehicleAutomobilePostPreRenderHandler* pHandler) = 0;

virtual void FlushClothesCache() = 0;
virtual void SetFastClothesLoading(EFastClothesLoading fastClothesLoading) = 0;
Expand Down
Loading