diff --git a/SilentPatchSA/SilentPatchSA.cpp b/SilentPatchSA/SilentPatchSA.cpp index fe220c0..494db0d 100644 --- a/SilentPatchSA/SilentPatchSA.cpp +++ b/SilentPatchSA/SilentPatchSA.cpp @@ -5294,6 +5294,24 @@ void Patch_SA_10(HINSTANCE hInstance) InterceptCall(0x6A8BBE, orgWorldAdd, WorldAdd_SetLightObjectFlag); } + + // Fix the logic behind exploding cars losing wheels + // Right now, they lose one wheel at random according to the damage manager, but they always lose the front left wheel visually. + // This change matches the visuals to the physics + // Also make it possible for the rear right wheel to be randomly picked + { + std::array spawnFlyingComponent = { 0x6B38CA, 0x6B3CCB, 0x6C6EBE, 0x6CCEF9 }; + CAutomobile::HookEach_SpawnFlyingComponent(spawnFlyingComponent, InterceptCall); + + Nop(0x6B38E4, 5); + Nop(0x6B3CF1, 5); + Nop(0x6C6ED8, 5); + Nop(0x6CCF17, 5); + + static const float fRandomness = -4.0f; + Patch(0x6C25F5 + 2, &fRandomness); + } + #if FULL_PRECISION_D3D // Test - full precision D3D device Patch( 0x7F672B+1, *(uint8_t*)(0x7F672B+1) | D3DCREATE_FPU_PRESERVE ); @@ -7028,6 +7046,36 @@ void Patch_SA_NewBinaries_Common(HINSTANCE hInstance) InterceptCall(worldAdd, orgWorldAdd, WorldAdd_SetLightObjectFlag); } + + + // Fix the logic behind exploding cars losing wheels + // Right now, they lose one wheel at random according to the damage manager, but they always lose the front left wheel visually. + // This change matches the visuals to the physics + // Also make it possible for the rear right wheel to be randomly picked + { + auto automobileBlowUp = pattern("E8 ? ? ? ? 8B 8E ? ? ? ? 8D 45 08").get_one(); + auto automobileBlowUpCutscene = pattern("E8 ? ? ? ? 80 7D 10 00 C7 45").get_one(); + auto heliBlowUp = pattern("E8 ? ? ? ? 8B 86 ? ? ? ? 8D 55 08").get_one(); + auto planeBlowUp = pattern("E8 ? ? ? ? 8B 86 ? ? ? ? 85 C0 74 24").get_one(); + auto wheelDetachRandomness = get_pattern("DC 0D ? ? ? ? E8 ? ? ? ? 8B CE", 2); + + std::array spawnFlyingComponent = { + automobileBlowUp.get(), + automobileBlowUpCutscene.get(), + heliBlowUp.get(), + planeBlowUp.get(), + }; + CAutomobile::HookEach_SpawnFlyingComponent(spawnFlyingComponent, InterceptCall); + + Nop(automobileBlowUp.get(0x1C), 5); + Nop(automobileBlowUpCutscene.get(0x22), 5); + Nop(heliBlowUp.get(0x1C), 5); + Nop(planeBlowUp.get(0x20), 5); + + static const double fRandomness = -4.0; + Patch(wheelDetachRandomness, &fRandomness); + } + } diff --git a/SilentPatchSA/VehicleSA.cpp b/SilentPatchSA/VehicleSA.cpp index 3f7a0da..eb5b40c 100644 --- a/SilentPatchSA/VehicleSA.cpp +++ b/SilentPatchSA/VehicleSA.cpp @@ -573,6 +573,69 @@ void CAutomobile::AfterPreRender() } } +void CAutomobile::HideDestroyedWheels_SilentPatch(void (CAutomobile::*spawnFlyingComponentCB)(int, unsigned int), int nodeID, unsigned int modelID) +{ + auto hideWheel = [this](int nodeID) + { + bool bHasWheel = false; + + RwFrame* wheelNode = m_pCarNode[nodeID]; + if (wheelNode != nullptr) + { + RwFrameForAllObjects(wheelNode, [&bHasWheel](RwObject* object) + { + if ((rwObjectGetFlags(object) & rpATOMICRENDER) != 0) + { + rwObjectSetFlags(object, 0); + bHasWheel = true; + } + return object; + }); + } + return bHasWheel; + }; + + + if (m_DamageManager.GetWheelStatus(0) == 2) + { + if (hideWheel(5)) + { + std::invoke(spawnFlyingComponentCB, this, 5, modelID); + } + } + if (m_DamageManager.GetWheelStatus(2) == 2) + { + if (hideWheel(2)) + { + std::invoke(spawnFlyingComponentCB, this, 2, modelID); + } + } + + // For rear wheels, also hide and spawn the middle wheel (if it exists) + if (m_DamageManager.GetWheelStatus(1) == 2) + { + if (hideWheel(6)) + { + std::invoke(spawnFlyingComponentCB, this, 6, modelID); + } + if (hideWheel(7)) + { + std::invoke(spawnFlyingComponentCB, this, 7, modelID); + } + } + if (m_DamageManager.GetWheelStatus(3) == 2) + { + if (hideWheel(3)) + { + std::invoke(spawnFlyingComponentCB, this, 3, modelID); + } + if (hideWheel(4)) + { + std::invoke(spawnFlyingComponentCB, this, 4, modelID); + } + } +} + void CAutomobile::Fix_SilentPatch() { ResetFrames(); diff --git a/SilentPatchSA/VehicleSA.h b/SilentPatchSA/VehicleSA.h index de799be..0fc8bb7 100644 --- a/SilentPatchSA/VehicleSA.h +++ b/SilentPatchSA/VehicleSA.h @@ -135,6 +135,24 @@ public: } }; +class CDamageManager +{ +private: + float WheelDamageEffect; + uint8_t EngineStatus; + uint8_t Wheel[4]; + uint8_t Door[6]; + uint32_t Lights; + uint32_t Panels; + +public: + uint32_t GetWheelStatus(int wheelID) const + { + return Wheel[wheelID]; + } +}; +static_assert(sizeof(CDamageManager) == 0x18, "Wrong size: CDamageManager"); + enum eRotAxis { ROT_AXIS_X = 0, @@ -299,7 +317,7 @@ public: class NOVMT CAutomobile : public CVehicle { public: - BYTE paddd[24]; + CDamageManager m_DamageManager; CDoor Door[NUM_DOORS]; RwFrame* m_pCarNode[25]; CBouncingPanel m_aBouncingPanel[3]; @@ -327,7 +345,20 @@ public: AfterPreRender(); } - HOOK_EACH_FUNC_CTR(PreRender, 1, orgAutomobilePreRender, &PreRender_SilentPatch); + HOOK_EACH_FUNC(PreRender, orgAutomobilePreRender, &PreRender_SilentPatch); + + void HideDestroyedWheels_SilentPatch(void (CAutomobile::*spawnFlyingComponentCB)(int, unsigned int), int nodeID, unsigned int modelID); + + template + static void (CAutomobile::*orgSpawnFlyingComponent)(int, unsigned int); + + template + void SpawnFlyingComponent_HideWheels(int nodeID, unsigned int modelID) + { + HideDestroyedWheels_SilentPatch(orgSpawnFlyingComponent, nodeID, modelID); + } + + HOOK_EACH_FUNC(SpawnFlyingComponent, orgSpawnFlyingComponent, &SpawnFlyingComponent_HideWheels); void Fix_SilentPatch(); RwFrame* GetTowBarFrame() const;