diff --git a/SilentPatchIII/SilentPatchIII.cpp b/SilentPatchIII/SilentPatchIII.cpp index 0c49bb2..8ff438d 100644 --- a/SilentPatchIII/SilentPatchIII.cpp +++ b/SilentPatchIII/SilentPatchIII.cpp @@ -506,6 +506,30 @@ namespace SirenSwitchingFix }; +// ============= Fixed vehicles exploding twice if the driver leaves the car while it's exploding ============= +namespace RemoveDriverStatusFix +{ + __declspec(naked) void RemoveDriver_SetStatus() + { + // if (m_nStatus != STATUS_WRECKED) + // m_nStatus = STATUS_ABANDONED; + _asm + { + mov ah, [ecx+50h] + mov al, ah + and ah, 0F8h + cmp ah, 28h + je DontSetStatus + and al, 7 + or al, 20h + + DontSetStatus: + retn + } + } +} + + void InjectDelayedPatches_III_Common( bool bHasDebugMenu, const wchar_t* wcModulePath ) { using namespace Memory; @@ -1254,6 +1278,29 @@ void Patch_III_Common() Patch( initHelis.get( 9 + 3 ), &colModelChopper ); } } + + + // Fixed vehicles exploding twice if the driver leaves the car while it's exploding + { + using namespace RemoveDriverStatusFix; + + auto removeDriver = pattern("8A 41 50 24 07 0C 20 88 41 50 C7 81").get_one(); + auto processCommands1 = get_pattern("88 41 50 8B 87"); + auto processCommands2 = get_pattern("88 41 50 8B 2B"); + auto processCommands3 = get_pattern("0C 20 88 42 50", 2); + auto processCommands4 = get_pattern("88 41 50 8B BE"); + auto pedSetOutCar = get_pattern("88 41 50 8B 85"); + + Nop(removeDriver.get(), 2); + InjectHook(removeDriver.get(2), RemoveDriver_SetStatus, HookType::Call); + + // CVehicle::RemoveDriver already sets the status to STATUS_ABANDONED, these are redundant + Nop(processCommands1, 3); + Nop(processCommands2, 3); + Nop(processCommands3, 3); + Nop(processCommands4, 3); + Nop(pedSetOutCar, 3); + } } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) diff --git a/SilentPatchSA/PedSA.h b/SilentPatchSA/PedSA.h index 9859592..3893110 100644 --- a/SilentPatchSA/PedSA.h +++ b/SilentPatchSA/PedSA.h @@ -6,6 +6,7 @@ class CEntryExit; class CEvent; class CPed; +class CVehicle; // This structure is very incomplete, but it is good enough for now class __declspec(novtable) CTask @@ -33,6 +34,9 @@ public: class __declspec(novtable) CTaskComplex : public CTask { +private: + CTask* m_pSubTask; + public: virtual bool IsSimpleTask() const override { return false; } virtual void SetSubTask(CTask*); @@ -54,12 +58,20 @@ public: bool Contains(int taskID) const; public: - std::byte __pad1[8]; + std::byte __pad1[4]; CTask* m_taskSequence[8]; std::byte __pad2[16]; }; static_assert(sizeof(CTaskComplexSequence) == 0x40, "Wrong size: CTaskComplexSequence"); +class __declspec(novtable) CTaskComplexCarSlowBeDraggedOut : public CTaskComplex +{ +public: + CVehicle* m_pVehicle; + int m_status; +}; +static_assert(sizeof(CTaskComplexCarSlowBeDraggedOut) == 0x14, "Wrong size: CTaskComplexCarSlowBeDraggedOut"); + class CTaskManager { public: @@ -215,9 +227,6 @@ public: unsigned int bUsedForReplay : 1; // This ped is controlled by replay and should be removed when replay is done. }; -class CVehicle; -class CPed; - class CPlayerPedData { public: diff --git a/SilentPatchSA/SilentPatchSA.cpp b/SilentPatchSA/SilentPatchSA.cpp index 8844092..6c4ee67 100644 --- a/SilentPatchSA/SilentPatchSA.cpp +++ b/SilentPatchSA/SilentPatchSA.cpp @@ -2455,6 +2455,39 @@ namespace RiotDontTargetPlayerGroupDuringMissions } +// ============= Fixed vehicles exploding twice if the driver leaves the car while it's exploding ============= +namespace RemoveDriverStatusFix +{ + __declspec(naked) void RemoveDriver_SetStatus() + { + // if (m_nStatus != STATUS_WRECKED) + // m_nStatus = STATUS_ABANDONED; + _asm + { + mov bl, [edi+36h] + mov al, bl + and bl, 0F8h + cmp bl, 28h + je DontSetStatus + and al, 7 + or al, 20h + + DontSetStatus: + retn + } + } + + static void (__thiscall *orgPrepareVehicleForPedExit)(CTaskComplexCarSlowBeDraggedOut* task, CPed* ped); + static void __fastcall PrepareVehicleForPedExit_WreckedCheck(CTaskComplexCarSlowBeDraggedOut* task, void*, CPed* ped) + { + if (task->m_pVehicle->GetStatus() != STATUS_WRECKED) + { + orgPrepareVehicleForPedExit(task, ped); + } + } +} + + // ============= LS-RP Mode stuff ============= namespace LSRPMode { @@ -5206,6 +5239,21 @@ void Patch_SA_10(HINSTANCE hInstance) Patch(0x6D5612 + 2, &LightStatusRandomnessThreshold); } + + // Fixed vehicles exploding twice if the driver leaves the car while it's exploding + { + using namespace RemoveDriverStatusFix; + + Nop(0x6D1955, 2); + InjectHook(0x6D1955 + 2, RemoveDriver_SetStatus, HookType::Call); + + InterceptCall(0x64C8CE, orgPrepareVehicleForPedExit, PrepareVehicleForPedExit_WreckedCheck); + + // CVehicle::RemoveDriver already sets the status to STATUS_ABANDONED, these are redundant + Nop(0x48628D, 3); + Nop(0x647E21, 3); + } + #if FULL_PRECISION_D3D // Test - full precision D3D device Patch( 0x7F672B+1, *(uint8_t*)(0x7F672B+1) | D3DCREATE_FPU_PRESERVE ); @@ -6900,6 +6948,26 @@ void Patch_SA_NewBinaries_Common(HINSTANCE hInstance) static const double LightStatusRandomnessThreshold = 25000.0; Patch(getVehicleLightsStatus, &LightStatusRandomnessThreshold); } + + + // Fixed vehicles exploding twice if the driver leaves the car while it's exploding + { + using namespace RemoveDriverStatusFix; + + auto removeDriver = pattern("8A 47 36 24 07 0C 20 80 7D 08 00").get_one(); + auto removeThisPed = get_pattern("80 C9 20 88 48 36 8B 96", 3); + auto taskSimpleCarSetPedOut = get_pattern("80 C9 20 88 48 36 8B 86", 3); + auto prepareVehicleForPedExit = get_pattern("57 E8 ? ? ? ? 57 8B CE E8 ? ? ? ? 57", 1); + + Nop(removeDriver.get(), 2); + InjectHook(removeDriver.get(2), RemoveDriver_SetStatus, HookType::Call); + + InterceptCall(prepareVehicleForPedExit, orgPrepareVehicleForPedExit, PrepareVehicleForPedExit_WreckedCheck); + + // CVehicle::RemoveDriver already sets the status to STATUS_ABANDONED, these are redundant + Nop(removeThisPed, 3); + Nop(taskSimpleCarSetPedOut, 3); + } } diff --git a/SilentPatchVC/SilentPatchVC.cpp b/SilentPatchVC/SilentPatchVC.cpp index 599d384..2edc132 100644 --- a/SilentPatchVC/SilentPatchVC.cpp +++ b/SilentPatchVC/SilentPatchVC.cpp @@ -449,6 +449,31 @@ namespace FBISirenCoronaFix } } + +// ============= Fixed vehicles exploding twice if the driver leaves the car while it's exploding ============= +namespace RemoveDriverStatusFix +{ + __declspec(naked) void RemoveDriver_SetStatus() + { + // if (m_nStatus != STATUS_WRECKED) + // m_nStatus = STATUS_ABANDONED; + _asm + { + mov cl, [ebx+50h] + mov al, cl + and cl, 0F8h + cmp cl, 28h + je DontSetStatus + and al, 7 + or al, 20h + + DontSetStatus: + retn + } + } +} + + void InjectDelayedPatches_VC_Common( bool bHasDebugMenu, const wchar_t* wcModulePath ) { using namespace Memory; @@ -1158,6 +1183,27 @@ void Patch_VC_Common() Patch( getDriverOneShot.get( -8 ), { 0x90, 0x89, 0xD9 } ); InjectHook( getDriverOneShot.get( -5 ), &CVehicle::GetOneShotOwnerID_SilentPatch, HookType::Call ); } + + + // Fixed vehicles exploding twice if the driver leaves the car while it's exploding + { + using namespace RemoveDriverStatusFix; + + auto removeDriver = pattern("8A 43 50 24 07 0C 20 88 43 50 E8").get_one(); + auto processCommands1 = get_pattern("88 42 50 8B 33"); + auto processCommands2 = get_pattern("88 42 50 8B AE"); + auto removeThisPed = get_pattern("88 42 50 8B 85"); + auto pedSetOutCar = get_pattern("0C 20 88 47 50 8B 85", 2); + + Nop(removeDriver.get(), 2); + InjectHook(removeDriver.get(2), RemoveDriver_SetStatus, HookType::Call); + + // CVehicle::RemoveDriver already sets the status to STATUS_ABANDONED, these are redundant + Nop(processCommands1, 3); + Nop(processCommands2, 3); + Nop(removeThisPed, 3); + Nop(pedSetOutCar, 3); + } } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)