diff --git a/42.13/media/lua/client/Landtrain/LandtrainTowSyncClient.lua b/42.13/media/lua/client/Landtrain/LandtrainTowSyncClient.lua index 8dd5060..9cf8341 100644 --- a/42.13/media/lua/client/Landtrain/LandtrainTowSyncClient.lua +++ b/42.13/media/lua/client/Landtrain/LandtrainTowSyncClient.lua @@ -9,6 +9,28 @@ local function log(msg) print("[Landtrain][TowSyncClient] " .. tostring(msg)) end +local function isTrailerScriptName(scriptName) + if not scriptName then return false end + return string.find(string.lower(scriptName), "trailer", 1, true) ~= nil +end + +local function shouldForceNotTowingScript(vehicle) + if not vehicle then return false end + local scriptName = vehicle:getScriptName() + return not isTrailerScriptName(scriptName) +end + +local function restoreTrailerScriptIfNeeded(vehicle) + if not vehicle then return end + if vehicle:getScriptName() ~= "notTowingA_Trailer" then return end + + local md = vehicle:getModData() + local originalScript = md and md.towBarOriginalScriptName or nil + if originalScript and isTrailerScriptName(originalScript) then + vehicle:setScriptName(originalScript) + end +end + local function ensureAttachment(vehicle, attachmentId) if not vehicle or not attachmentId then return false end @@ -47,6 +69,7 @@ end local function reconcilePairState(vehicleA, vehicleB, attachmentA, attachmentB) if not vehicleA or not vehicleB then return end + restoreTrailerScriptIfNeeded(vehicleA) if TowBarMod.Utils and TowBarMod.Utils.updateAttachmentsForRigidTow then TowBarMod.Utils.updateAttachmentsForRigidTow(vehicleA, vehicleB, attachmentA, attachmentB) diff --git a/42.13/media/lua/client/Landtrain/LandtrainTowbarChainsRebuild.lua b/42.13/media/lua/client/Landtrain/LandtrainTowbarChainsRebuild.lua index 5ecd3c3..db2559f 100644 --- a/42.13/media/lua/client/Landtrain/LandtrainTowbarChainsRebuild.lua +++ b/42.13/media/lua/client/Landtrain/LandtrainTowbarChainsRebuild.lua @@ -37,6 +37,28 @@ local function hasAttachment(vehicle, attachmentId) return script ~= nil and script:getAttachmentById(attachmentId) ~= nil end +local function isTrailerScriptName(scriptName) + if not scriptName then return false end + return string.find(string.lower(scriptName), "trailer", 1, true) ~= nil +end + +local function shouldForceNotTowingScript(vehicle) + if not vehicle then return false end + local scriptName = vehicle:getScriptName() + return not isTrailerScriptName(scriptName) +end + +local function restoreTrailerScriptIfNeeded(vehicle) + if not vehicle then return end + if vehicle:getScriptName() ~= "notTowingA_Trailer" then return end + + local md = vehicle:getModData() + local originalScript = md and md.towBarOriginalScriptName or nil + if originalScript and isTrailerScriptName(originalScript) then + vehicle:setScriptName(originalScript) + end +end + local function sideFree(vehicle, attachmentId) if vehicle == nil then return false end if attachmentId == "trailer" then return vehicle:getVehicleTowing() == nil end @@ -121,6 +143,27 @@ local function resolvePair(towingVehicle, towedVehicle, preferredA, preferredB) return "trailer", "trailerfront" end +local function resolveMenuPair(towingVehicle, towedVehicle, preferredA, preferredB) + local candidates = { + { preferredA, preferredB }, + { "trailer", "trailerfront" }, + { towingVehicle and towingVehicle:getTowAttachmentSelf() or nil, towedVehicle and towedVehicle:getTowAttachmentSelf() or nil }, + { "trailerfront", "trailer" } + } + + for _, pair in ipairs(candidates) do + local a, b = pair[1], pair[2] + if a and b and a ~= b + and hasAttachment(towingVehicle, a) + and hasAttachment(towedVehicle, b) + and canTowByLandtrain(towingVehicle, towedVehicle, a, b, false) then + return a, b + end + end + + return nil, nil +end + local function clearChangedOffset(vehicle) if not vehicle then return end end @@ -534,6 +577,9 @@ local function ensurePairAttached(playerObj, towingVehicle, towedVehicle, prefer log("ensurePairAttached skipped: identical vehicles id=" .. tostring(vehicleId(towingVehicle))) return false end + + restoreTrailerScriptIfNeeded(towingVehicle) + if wouldCreateTowLoop(towingVehicle, towedVehicle) then log("ensurePairAttached skipped: loop A=" .. tostring(vehicleId(towingVehicle)) .. " B=" .. tostring(vehicleId(towedVehicle))) return false @@ -670,22 +716,30 @@ local function getLandtrainHookTypeVariants(vehicleA, vehicleB, hasTowBar) local variants = {} if not hasTowBar or not vehicleA or not vehicleB or vehicleA == vehicleB then return variants end - if canTowByLandtrain(vehicleA, vehicleB, "trailerfront", "trailer", false) then + restoreTrailerScriptIfNeeded(vehicleA) + restoreTrailerScriptIfNeeded(vehicleB) + + local function addVariant(towingVehicle, towedVehicle) + local attachmentA, attachmentB = resolveMenuPair(towingVehicle, towedVehicle) + if not attachmentA or not attachmentB then + return false + end + table.insert(variants, { - name = getText("UI_Text_Towing_attach") .. "\n" .. ISVehicleMenu.getVehicleDisplayName(vehicleB) .. "\n" .. getText("UI_Text_Towing_byTowBar"), + name = getText("UI_Text_Towing_attach") .. "\n" .. ISVehicleMenu.getVehicleDisplayName(towedVehicle) .. "\n" .. getText("UI_Text_Towing_byTowBar"), func = TowBarMod.Hook.attachByTowBarAction, - towingVehicle = vehicleB, - towedVehicle = vehicleA, - textureName = "tow_bar_icon" - }) - elseif canTowByLandtrain(vehicleA, vehicleB, "trailer", "trailerfront", false) then - table.insert(variants, { - name = getText("UI_Text_Towing_attach") .. "\n" .. ISVehicleMenu.getVehicleDisplayName(vehicleB) .. "\n" .. getText("UI_Text_Towing_byTowBar"), - func = TowBarMod.Hook.attachByTowBarAction, - towingVehicle = vehicleA, - towedVehicle = vehicleB, + towingVehicle = towingVehicle, + towedVehicle = towedVehicle, + towingPoint = attachmentA, + towedPoint = attachmentB, textureName = "tow_bar_icon" }) + return true + end + + local addedForward = addVariant(vehicleA, vehicleB) + if not addedForward then + addVariant(vehicleB, vehicleA) end return variants @@ -1060,8 +1114,7 @@ local function sortPersistedLinks(links, byRearSql) end local function isTowbarPairStateComplete(frontVehicle, rearVehicle) - if not isLinkedPair(frontVehicle, rearVehicle) then return false end - return rearVehicle:getScriptName() == "notTowingA_Trailer" + return isLinkedPair(frontVehicle, rearVehicle) end local function restoreViaAttachWrapper(playerObj, frontVehicle, rearVehicle, attachmentA, attachmentB) @@ -1129,7 +1182,8 @@ local function restorePersistedLink(playerObj, link, bySql, byHeadOrder) end if wouldCreateTowLoop(frontVehicle, rearVehicle) then return false end - if not isLinkedPair(frontVehicle, rearVehicle) then + local linkedNow = isLinkedPair(frontVehicle, rearVehicle) + if not linkedNow then if not canTowByLandtrain(frontVehicle, rearVehicle, link.attachmentA, link.attachmentB, true) then return false end @@ -1138,7 +1192,7 @@ local function restorePersistedLink(playerObj, link, bySql, byHeadOrder) end end - return ensurePairAttached(playerObj, frontVehicle, rearVehicle, link.attachmentA, link.attachmentB, true) + return ensurePairAttached(playerObj, frontVehicle, rearVehicle, link.attachmentA, link.attachmentB, not linkedNow) end local function runPersistedLoadRebuildPass()