Bump mod version to 1.0.5

This commit is contained in:
2026-08-12 13:36:01 -04:00
parent 40f0296080
commit a6ba4877d0
23 changed files with 6650 additions and 1 deletions
@@ -0,0 +1,173 @@
if isServer() then return end
if not TowBarMod then TowBarMod = {} end
TowBarMod.Sync = TowBarMod.Sync or {}
if TowBarMod.Sync._towSyncClientLoaded then return end
TowBarMod.Sync._towSyncClientLoaded = true
local function resolveVehicle(id)
if not id then return nil end
return getVehicleById(id)
end
local function ensureAttachment(vehicle, attachmentId)
if not vehicle or not attachmentId then return false end
local script = vehicle:getScript()
if not script then return false end
if script:getAttachmentById(attachmentId) ~= nil then return true end
local wheelCount = script:getWheelCount()
local yOffset = -0.5
if wheelCount > 0 then
local wheel = script:getWheel(0)
if wheel and wheel:getOffset() then
yOffset = wheel:getOffset():y() + 0.1
end
end
local chassis = script:getPhysicsChassisShape()
if not chassis then return false end
local attach = ModelAttachment.new(attachmentId)
if attachmentId == "trailer" then
attach:getOffset():set(0, yOffset, -chassis:z() / 2 - 0.1)
attach:setZOffset(-1)
else
attach:getOffset():set(0, yOffset, chassis:z() / 2 + 0.1)
attach:setZOffset(1)
end
script:addAttachment(attach)
return true
end
local function isLinked(vehicleA, vehicleB)
if not vehicleA or not vehicleB then return false end
return vehicleA:getVehicleTowing() == vehicleB and vehicleB:getVehicleTowedBy() == vehicleA
end
local function reconcilePairState(vehicleA, vehicleB, attachmentA, attachmentB)
if TowBarMod.Utils and TowBarMod.Utils.updateAttachmentsForRigidTow then
TowBarMod.Utils.updateAttachmentsForRigidTow(vehicleA, vehicleB, attachmentA, attachmentB)
end
local towingMd = vehicleA:getModData()
local towedMd = vehicleB:getModData()
local currentScript = vehicleB:getScriptName()
if towingMd then
towingMd["isTowingByTowBar"] = true
towingMd["towed"] = false
towingMd["towBarTowedVehicleId"] = vehicleB:getId()
towingMd["towBarTowingVehicleId"] = nil
towingMd["towBarExpectedAttachment"] = attachmentA
vehicleA:transmitModData()
end
if towedMd then
if towedMd.towBarOriginalScriptName == nil and currentScript ~= "notTowingA_Trailer" then
towedMd.towBarOriginalScriptName = currentScript
end
if towedMd.towBarOriginalMass == nil then
towedMd.towBarOriginalMass = vehicleB:getMass()
end
if towedMd.towBarOriginalBrakingForce == nil then
towedMd.towBarOriginalBrakingForce = vehicleB:getBrakingForce()
end
towedMd["isTowingByTowBar"] = true
towedMd["towed"] = true
towedMd["towBarTowedVehicleId"] = nil
towedMd["towBarTowingVehicleId"] = vehicleA:getId()
towedMd["towBarExpectedAttachment"] = attachmentB
vehicleB:transmitModData()
end
if TowBarMod.Hook and TowBarMod.Hook.setVehicleScriptWithTowBarHidden then
TowBarMod.Hook.setVehicleScriptWithTowBarHidden(vehicleB, "notTowingA_Trailer")
end
if TowBarMod.Hook and TowBarMod.Hook.setVehiclePostAttach then
pcall(TowBarMod.Hook.setVehiclePostAttach, nil, vehicleB)
end
end
local function applyAttachSync(args)
if not args then return end
local vehicleA = resolveVehicle(args.vehicleA)
local vehicleB = resolveVehicle(args.vehicleB)
if not vehicleA or not vehicleB then return end
local attachmentA = args.attachmentA or "trailer"
local attachmentB = args.attachmentB or "trailerfront"
if not ensureAttachment(vehicleA, attachmentA) or not ensureAttachment(vehicleB, attachmentB) then
return
end
if not isLinked(vehicleA, vehicleB) then
vehicleA:addPointConstraint(nil, vehicleB, attachmentA, attachmentB)
end
reconcilePairState(vehicleA, vehicleB, attachmentA, attachmentB)
end
local function hasConflictingTowLink(vehicle, expectedOther)
if not vehicle or not expectedOther then return false end
local towing = vehicle:getVehicleTowing()
local towedBy = vehicle:getVehicleTowedBy()
if (towing ~= nil and towing ~= expectedOther)
or (towedBy ~= nil and towedBy ~= expectedOther) then
return true
end
-- Also reject a delayed detach while a new tow is being established but
-- has not created its physical constraint yet.
local modData = vehicle:getModData()
if not modData then return false end
local expectedOtherId = expectedOther:getId()
return (modData["towBarTowedVehicleId"] ~= nil
and modData["towBarTowedVehicleId"] ~= expectedOtherId)
or (modData["towBarTowingVehicleId"] ~= nil
and modData["towBarTowingVehicleId"] ~= expectedOtherId)
end
local function breakTowBarPair(vehicleA, vehicleB)
if not vehicleA or not vehicleB then return end
local vehicleAReferencesB = vehicleA:getVehicleTowing() == vehicleB
or vehicleA:getVehicleTowedBy() == vehicleB
local vehicleBReferencesA = vehicleB:getVehicleTowing() == vehicleA
or vehicleB:getVehicleTowedBy() == vehicleA
if vehicleBReferencesA then
vehicleB:breakConstraint(true, true)
end
if vehicleAReferencesB then
vehicleA:breakConstraint(true, true)
end
end
local function applyDetachSync(args)
if not args then return end
local vehicleA = resolveVehicle(args.vehicleA)
local vehicleB = resolveVehicle(args.vehicleB)
if not vehicleA or not vehicleB then return end
if hasConflictingTowLink(vehicleA, vehicleB) or hasConflictingTowLink(vehicleB, vehicleA) then
return
end
breakTowBarPair(vehicleA, vehicleB)
if TowBarMod.Hook and TowBarMod.Hook.cleanupDetachedTowBar then
pcall(TowBarMod.Hook.cleanupDetachedTowBar, vehicleA, vehicleB)
end
end
local function onServerCommand(module, command, args)
if module ~= "towbar" then return end
if command == "forceAttachSync" then
applyAttachSync(args)
elseif command == "forceDetachSync" or command == "spontaneousDetachSync" then
applyDetachSync(args)
end
end
Events.OnServerCommand.Add(onServerCommand)