From 227428d4ab0e10faebcdc0bced5c761289ea6795 Mon Sep 17 00:00:00 2001 From: HRiggs Date: Fri, 6 Feb 2026 19:02:36 -0500 Subject: [PATCH] update detaching state --- .../media/lua/client/TowBar/TowingHooking.lua | 46 ++++++++++++++---- common/media/models_X/vehicles/Towbar.fbx | Bin 0 -> 13468 bytes 2 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 common/media/models_X/vehicles/Towbar.fbx diff --git a/42.13/media/lua/client/TowBar/TowingHooking.lua b/42.13/media/lua/client/TowBar/TowingHooking.lua index 2120266..e222fd8 100644 --- a/42.13/media/lua/client/TowBar/TowingHooking.lua +++ b/42.13/media/lua/client/TowBar/TowingHooking.lua @@ -304,6 +304,12 @@ function TowBarMod.Hook.hasTowBarTowData(vehicle) return modData and modData["isTowingByTowBar"] and modData["towed"] end +function TowBarMod.Hook.isTowBarLinkActive(towingVehicle, towedVehicle) + if not towingVehicle or not towedVehicle then return false end + return towingVehicle:getVehicleTowing() == towedVehicle + and towedVehicle:getVehicleTowedBy() == towingVehicle +end + function TowBarMod.Hook.queueTowBarReapply(vehicle) if not TowBarMod.Hook.hasTowBarTowData(vehicle) then return end local vehicleId = vehicle:getId() @@ -311,24 +317,40 @@ function TowBarMod.Hook.queueTowBarReapply(vehicle) end function TowBarMod.Hook.tryTowBarReapply(vehicle, playerObj) - if not TowBarMod.Hook.hasTowBarTowData(vehicle) then return false end + if not TowBarMod.Hook.hasTowBarTowData(vehicle) then + if vehicle then + TowBarMod.Hook.pendingReapplyVehicleIds[vehicle:getId()] = nil + end + return false + end + local vehicleId = vehicle:getId() - if TowBarMod.Hook.reappliedVehicleIds[vehicleId] then return true end + if TowBarMod.Hook.reappliedVehicleIds[vehicleId] then + TowBarMod.Hook.pendingReapplyVehicleIds[vehicleId] = nil + return true + end local towingVehicle = vehicle:getVehicleTowedBy() if not towingVehicle then return false end local towingModData = towingVehicle:getModData() if not towingModData or towingModData["isTowingByTowBar"] ~= true then return false end - playerObj = playerObj or getPlayer() - if not playerObj then return false end - local modData = vehicle:getModData() if not modData.towBarOriginalScriptName then modData.towBarOriginalScriptName = vehicle:getScriptName() vehicle:transmitModData() end + -- Save/load already restores a valid tow link. Do not stack another attach on top. + if TowBarMod.Hook.isTowBarLinkActive(towingVehicle, vehicle) then + TowBarMod.Hook.setVehiclePostAttach(playerObj, vehicle) + TowBarMod.Hook.markReapplied(vehicle) + return true + end + + playerObj = playerObj or getPlayer() + if not playerObj then return false end + local attachmentA = towingVehicle:getTowAttachmentSelf() or "trailer" local attachmentB = vehicle:getTowAttachmentSelf() or "trailerfront" @@ -359,12 +381,17 @@ function TowBarMod.Hook.processPendingReapplies() local playerObj = getPlayer() if not playerObj then return end + local resolvedVehicleIds = {} for vehicleId, _ in pairs(TowBarMod.Hook.pendingReapplyVehicleIds) do local vehicle = TowBarMod.Hook.getVehicleByIdSafe(vehicleId) - if vehicle then - TowBarMod.Hook.tryTowBarReapply(vehicle, playerObj) + if vehicle and (TowBarMod.Hook.tryTowBarReapply(vehicle, playerObj) or not TowBarMod.Hook.hasTowBarTowData(vehicle)) then + table.insert(resolvedVehicleIds, vehicleId) end end + + for _, vehicleId in ipairs(resolvedVehicleIds) do + TowBarMod.Hook.pendingReapplyVehicleIds[vehicleId] = nil + end end function TowBarMod.Hook.OnSpawnVehicle(vehicle) @@ -468,8 +495,9 @@ end function TowBarMod.Hook.performDeattachTowBar(playerObj, towingVehicle, towedVehicle) TowBarMod.Utils.updateAttachmentsOnDefaultValues(towingVehicle, towedVehicle) - local args = { vehicle = towedVehicle:getId() } - sendClientCommand(playerObj, "towbar", "detachConstraint", args) + -- Detach from the towing side to mirror vanilla trailer detach behavior. + local args = { vehicle = towingVehicle:getId() } + sendClientCommand(playerObj, "vehicle", "detachTrailer", args) local towedModData = towedVehicle:getModData() if towedModData.towBarOriginalScriptName then diff --git a/common/media/models_X/vehicles/Towbar.fbx b/common/media/models_X/vehicles/Towbar.fbx new file mode 100644 index 0000000000000000000000000000000000000000..87c3a6e9a7a89ca38652b703257e57a2445fd55a GIT binary patch literal 13468 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 IuOF-TKQtbqiU0rr literal 0 HcmV?d00001