From afadd2e07ce14b29b7d5a8ad7f2adf32dc7fa910 Mon Sep 17 00:00:00 2001 From: HRiggs Date: Fri, 6 Feb 2026 23:36:44 -0500 Subject: [PATCH] Finalized --- .../Landtrain/UnlimitedTowbarChains.lua | 137 ++++++++++++++++-- 1 file changed, 125 insertions(+), 12 deletions(-) diff --git a/42.13/media/lua/client/Landtrain/UnlimitedTowbarChains.lua b/42.13/media/lua/client/Landtrain/UnlimitedTowbarChains.lua index 7843492..925a8cc 100644 --- a/42.13/media/lua/client/Landtrain/UnlimitedTowbarChains.lua +++ b/42.13/media/lua/client/Landtrain/UnlimitedTowbarChains.lua @@ -13,6 +13,7 @@ local LANDTRAIN_SAVED_POS_X_KEY = "landtrainSavedPosX" local LANDTRAIN_SAVED_POS_Y_KEY = "landtrainSavedPosY" local LANDTRAIN_SAVED_POS_Z_KEY = "landtrainSavedPosZ" local LANDTRAIN_SAVED_DIR_KEY = "landtrainSavedDir" +local LANDTRAIN_ATTACHMENT_BASE_OFFSETS = {} local function emitLandtrainLog(line) print(line) @@ -48,6 +49,42 @@ local function hasAttachmentById(vehicle, attachmentId) return script:getAttachmentById(attachmentId) ~= nil end +local function isUsableAttachmentPair(towingVehicle, towedVehicle, attachmentA, attachmentB) + if attachmentA == nil or attachmentB == nil or attachmentA == attachmentB then + return false + end + return hasAttachmentById(towingVehicle, attachmentA) and hasAttachmentById(towedVehicle, attachmentB) +end + +local function getAttachmentBaseOffsetKey(script, attachmentId) + if script == nil or attachmentId == nil then return nil end + local scriptName = script.getName and script:getName() or tostring(script) + return tostring(scriptName) .. "|" .. tostring(attachmentId) +end + +local function cacheAttachmentBaseOffset(script, attachmentId) + local key = getAttachmentBaseOffsetKey(script, attachmentId) + if key == nil or LANDTRAIN_ATTACHMENT_BASE_OFFSETS[key] ~= nil then + return + end + local attachment = script and script:getAttachmentById(attachmentId) or nil + if attachment == nil then + return + end + local offset = attachment:getOffset() + LANDTRAIN_ATTACHMENT_BASE_OFFSETS[key] = { + x = offset:x(), + y = offset:y(), + z = offset:z() + } +end + +local function getAttachmentBaseOffset(script, attachmentId) + local key = getAttachmentBaseOffsetKey(script, attachmentId) + if key == nil then return nil end + return LANDTRAIN_ATTACHMENT_BASE_OFFSETS[key] +end + local function getTowbarStyleAttachmentPair(towingVehicle, towedVehicle) local attachmentA = nil local attachmentB = nil @@ -133,20 +170,73 @@ local function chooseLandtrainAttachmentPair(towingVehicle, towedVehicle, prefer end local function chooseLandtrainTowbarPair(towingVehicle, towedVehicle, preferredA, preferredB) - local towbarA, towbarB = getTowbarStyleAttachmentPair(towingVehicle, towedVehicle) - if towbarA ~= towbarB and hasAttachmentById(towingVehicle, towbarA) and hasAttachmentById(towedVehicle, towbarB) then - return towbarA, towbarB - end - - if preferredA ~= nil and preferredB ~= nil and preferredA ~= preferredB - and hasAttachmentById(towingVehicle, preferredA) - and hasAttachmentById(towedVehicle, preferredB) then + if isUsableAttachmentPair(towingVehicle, towedVehicle, preferredA, preferredB) then return preferredA, preferredB end + -- Match Towbar's load reapply pair selection before Landtrain-specific fallbacks. + local towbarA = (towingVehicle and towingVehicle:getTowAttachmentSelf()) or "trailer" + local towbarB = (towedVehicle and towedVehicle:getTowAttachmentSelf()) or "trailerfront" + if isUsableAttachmentPair(towingVehicle, towedVehicle, towbarA, towbarB) then + return towbarA, towbarB + end + + local styleA, styleB = getTowbarStyleAttachmentPair(towingVehicle, towedVehicle) + if isUsableAttachmentPair(towingVehicle, towedVehicle, styleA, styleB) then + return styleA, styleB + end + return chooseLandtrainAttachmentPair(towingVehicle, towedVehicle, preferredA, preferredB) end +local function clearChangedTowedAttachmentOffset(vehicle) + if vehicle == nil then return end + local modData = vehicle:getModData() + if modData == nil or modData["isChangedTowedAttachment"] ~= true then return end + + local script = vehicle:getScript() + local changedAttachmentId = modData["towBarChangedAttachmentId"] + if changedAttachmentId == nil and vehicle.getTowAttachmentSelf then + changedAttachmentId = vehicle:getTowAttachmentSelf() + end + + if script ~= nil and changedAttachmentId ~= nil then + cacheAttachmentBaseOffset(script, changedAttachmentId) + local baseOffset = getAttachmentBaseOffset(script, changedAttachmentId) + local towedAttachment = script:getAttachmentById(changedAttachmentId) + if towedAttachment ~= nil then + if baseOffset ~= nil then + towedAttachment:getOffset():set(baseOffset.x, baseOffset.y, baseOffset.z) + else + local offset = towedAttachment:getOffset() + local storedShift = tonumber(modData["towBarChangedOffsetZShift"]) + if storedShift ~= nil then + towedAttachment:getOffset():set(offset:x(), offset:y(), offset:z() - storedShift) + else + local zShift = offset:z() > 0 and -1 or 1 + towedAttachment:getOffset():set(offset:x(), offset:y(), offset:z() + zShift) + end + end + end + end + + modData["isChangedTowedAttachment"] = false + modData["towBarChangedAttachmentId"] = nil + modData["towBarChangedOffsetZShift"] = nil + vehicle:transmitModData() +end + +local function updateAttachmentsForRigidTowNoStack(towingVehicle, towedVehicle, attachmentA, attachmentB) + if TowBarMod == nil or TowBarMod.Utils == nil or TowBarMod.Utils.updateAttachmentsForRigidTow == nil then + return false + end + local towedScript = towedVehicle and towedVehicle:getScript() or nil + cacheAttachmentBaseOffset(towedScript, attachmentB) + clearChangedTowedAttachmentOffset(towedVehicle) + TowBarMod.Utils.updateAttachmentsForRigidTow(towingVehicle, towedVehicle, attachmentA, attachmentB) + return true +end + local function dumpTowState(prefix, vehicle) if not LANDTRAIN_DEBUG then return end if vehicle == nil then @@ -287,7 +377,9 @@ local function storeLandtrainFrontLinkData(towingVehicle, towedVehicle, attachme towingModData[LANDTRAIN_REAR_SQL_ID_KEY] = nil end towingVehicle:transmitModData() - local resolvedA, resolvedB = chooseLandtrainTowbarPair(towingVehicle, towedVehicle, attachmentA, attachmentB) + local preferredA = attachmentA or towedModData[LANDTRAIN_FRONT_ATTACHMENT_A_KEY] + local preferredB = attachmentB or towedModData[LANDTRAIN_FRONT_ATTACHMENT_B_KEY] + local resolvedA, resolvedB = chooseLandtrainTowbarPair(towingVehicle, towedVehicle, preferredA, preferredB) towedModData[LANDTRAIN_FRONT_ATTACHMENT_A_KEY] = resolvedA towedModData[LANDTRAIN_FRONT_ATTACHMENT_B_KEY] = resolvedB towedVehicle:transmitModData() @@ -737,7 +829,7 @@ local function attachSavedLandtrainLink(playerObj, link, delayTicks) end local originalScriptName = rearVehicle:getScriptName() - TowBarMod.Utils.updateAttachmentsForRigidTow(frontVehicle, rearVehicle, link.attachmentA, link.attachmentB) + updateAttachmentsForRigidTowNoStack(frontVehicle, rearVehicle, link.attachmentA, link.attachmentB) rearVehicle:setScriptName("notTowingA_Trailer") local args = { vehicleA = frontVehicle:getId(), @@ -971,7 +1063,10 @@ local function captureTowbarFrontLink(towingVehicle) return nil end - local attachmentA, attachmentB = chooseLandtrainTowbarPair(frontVehicle, towingVehicle, nil, nil) + local towingModData = towingVehicle:getModData() + local savedAttachmentA = towingModData and towingModData[LANDTRAIN_FRONT_ATTACHMENT_A_KEY] or nil + local savedAttachmentB = towingModData and towingModData[LANDTRAIN_FRONT_ATTACHMENT_B_KEY] or nil + local attachmentA, attachmentB = chooseLandtrainTowbarPair(frontVehicle, towingVehicle, savedAttachmentA, savedAttachmentB) local link = { frontVehicle = frontVehicle, towingVehicle = towingVehicle, @@ -1005,7 +1100,7 @@ local function restoreTowbarFrontLink(playerObj, link) end ltLog("restoreTowbarFrontLink restoring front=" .. vehLabel(frontVehicle) .. " middle=" .. vehLabel(towingVehicle)) - TowBarMod.Utils.updateAttachmentsForRigidTow(frontVehicle, towingVehicle, link.attachmentA, link.attachmentB) + updateAttachmentsForRigidTowNoStack(frontVehicle, towingVehicle, link.attachmentA, link.attachmentB) towingVehicle:setScriptName("notTowingA_Trailer") local args = { @@ -1079,6 +1174,22 @@ local function ensureTowAttachmentsForTrailers() end end +local function captureTowbarAttachmentDefaults() + local scriptManager = getScriptManager() + if scriptManager == nil then return end + + local vehicleScripts = scriptManager:getAllVehicleScripts() + if vehicleScripts == nil then return end + + for i = 0, vehicleScripts:size() - 1 do + local script = vehicleScripts:get(i) + if script ~= nil then + cacheAttachmentBaseOffset(script, "trailer") + cacheAttachmentBaseOffset(script, "trailerfront") + end + end +end + local function menuHasTowbarAttachSlice(menu) if menu == nil or menu.slices == nil then return false end local attachAction = TowBarMod and TowBarMod.Hook and TowBarMod.Hook.attachByTowBarAction or nil @@ -1598,7 +1709,9 @@ local function startLandtrainInstallWatchdog() end Events.OnGameBoot.Add(ensureTowAttachmentsForTrailers) +Events.OnGameBoot.Add(captureTowbarAttachmentDefaults) Events.OnGameBoot.Add(startLandtrainInstallWatchdog) +Events.OnGameStart.Add(captureTowbarAttachmentDefaults) Events.OnGameStart.Add(startLandtrainInstallWatchdog) Events.OnGameStart.Add(scheduleLandtrainSingleLoadRestore) Events.OnTick.Add(onLandtrainSaveSnapshotTick)