diff --git a/code/components/extra-natives-five/src/VehicleExtraNatives.cpp b/code/components/extra-natives-five/src/VehicleExtraNatives.cpp index 676b9ac345..d2e0bdc9b0 100644 --- a/code/components/extra-natives-five/src/VehicleExtraNatives.cpp +++ b/code/components/extra-natives-five/src/VehicleExtraNatives.cpp @@ -53,6 +53,16 @@ static hook::cdecl_stub getWheelBroken([] return hook::get_call(hook::get_pattern("E8 ? ? ? ? 48 8B CD 41 88 84 1F")); }); +static hook::cdecl_stub restoreVehicleWheelState([] +{ + return hook::get_pattern("45 33 C0 B8 00 00 7A 44 89 81 E8 01 00 00"); +}); + +static hook::cdecl_stub reattachByBoneIndex([] +{ + return hook::get_call(hook::get_pattern("0F BE 57 06 48 8B 8E C0 09 00 00 E8", 11)); +}); + static hook::cdecl_stub switchEngineOff([] { return hook::get_call(hook::get_pattern("E8 ? ? ? ? 48 8B 8B ? ? ? ? 0F 2F FE")); @@ -327,6 +337,7 @@ static char* VehicleTopSpeedModifierPtr; static int VehicleCheatPowerIncreaseOffset; static int VehicleDamageStructOffset; +static int BrokenWheelsMaskOffset; static bool* g_trainsForceDoorsOpen; static int TrainDoorCountOffset; @@ -806,6 +817,16 @@ static HookFunction initFunction([]() VehicleDamageStructOffset = *(uint32_t*)(location - 11); } + { + // read the broken-wheels bitmask offset from the IS_VEHICLE_WHEEL_BROKEN_OFF + // mov eax, [rcx+A98h] <-- bitmask offset we extract + // bt eax, edx + // setb al + // retn + auto fn = hook::get_pattern("8B 81 ? ? 00 00 0F A3 D0 0F 92 C0 C3"); + BrokenWheelsMaskOffset = *(int32_t*)(fn + 2); + } + { VehiclePitchBiasOffset = *hook::get_pattern("0F 2F F7 44 0F 28 C0 F3 44 0F 58 83", 12); VehicleRollBiasOffset = VehiclePitchBiasOffset - 4; @@ -2007,6 +2028,45 @@ static HookFunction initFunction([]() } }); + fx::ScriptEngine::RegisterNativeHandler("RESTORE_VEHICLE_WHEEL", [](fx::ScriptContext& context) + { + if (fwEntity* vehicle = getAndCheckVehicle(context, "RESTORE_VEHICLE_WHEEL")) + { + auto wheelIndex = context.GetArgument(1); + auto numWheels = readValue(vehicle, NumWheelsOffset); + + if (wheelIndex >= numWheels) + return; + + if (!getWheelBroken(vehicle, wheelIndex)) + return; + + + *reinterpret_cast((char*)vehicle + BrokenWheelsMaskOffset) &= ~(1u << wheelIndex); + + auto wheelsAddress = readValue(vehicle, WheelsPtrOffset); + auto wheelPtr = reinterpret_cast(*reinterpret_cast(wheelsAddress + 8 * wheelIndex)); + + if (!wheelPtr) + { + return; + } + + restoreVehicleWheelState(wheelPtr); + + // reattach only this wheel's fragment child not the whole vehicle (reattachVehicleFragments + // would restore hoods/doors/bumpers too) For deleteWheel=true the fragment was deleted + // so RestoreAbove recreates it from fragType data For deleteWheel=false it's a no-op + auto boneIndex = *reinterpret_cast((char*)wheelPtr + 0x10C); + if (boneIndex >= 0) + { + auto fragInst = *reinterpret_cast((char*)vehicle + 0x9C0); + if (fragInst) + reattachByBoneIndex(fragInst, boneIndex); + } + } + }); + fx::ScriptEngine::RegisterNativeHandler("SET_FUEL_CONSUMPTION_STATE", [](fx::ScriptContext& context) { g_isFuelConsumptionOn = context.GetArgument(0); diff --git a/ext/native-decls/RestoreVehicleWheel.md b/ext/native-decls/RestoreVehicleWheel.md new file mode 100644 index 0000000000..2492e28306 --- /dev/null +++ b/ext/native-decls/RestoreVehicleWheel.md @@ -0,0 +1,33 @@ +--- +ns: CFX +apiset: client +game: gta5 +--- +## RESTORE_VEHICLE_WHEEL + +```c +void RESTORE_VEHICLE_WHEEL(Vehicle vehicle, int wheelIndex); +``` + +Restores a previously broken-off vehicle wheel by index. Inverse of [BREAK_OFF_VEHICLE_WHEEL](#_0xA274CADB). + +Clears the broken-off state, restores tyre and suspension health to full, and reattaches the detached wheel fragment back to the vehicle. + +Please note that this is not synchronized across the network. + +## Examples +```lua +local vehicle = GetVehiclePedIsIn(PlayerPedId()) + +if DoesEntityExist(vehicle) then + for i = 0, GetVehicleNumberOfWheels(vehicle) - 1 do + if IsVehicleWheelBrokenOff(vehicle, i) then + RestoreVehicleWheel(vehicle, i) + end + end +end +``` + +## Parameters +* **vehicle**: The vehicle handle. +* **wheelIndex**: The wheel index to restore.