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 0000000..87c3a6e Binary files /dev/null and b/common/media/models_X/vehicles/Towbar.fbx differ