42.19 Support

This commit is contained in:
2026-06-01 19:51:40 -04:00
parent 8c0afd857b
commit 40f0296080
3 changed files with 186 additions and 33 deletions
+2
View File
@@ -3,6 +3,8 @@ if not TowBarMod.Config then TowBarMod.Config = {} end
TowBarMod.Config.lowLevelAnimation = "RemoveGrass"
TowBarMod.Config.rigidTowbarDistance = 1.0
TowBarMod.Config.towAttachmentExteriorPadding = 0.25
TowBarMod.Config.towedVehicleRollingMass = 75
TowBarMod.Config.devMode = false
TowBarMod.Config.vanillaTowbarModelScaleMin = 1.5
TowBarMod.Config.vanillaTowbarModelScaleMax = 2.0
+107 -21
View File
@@ -1,11 +1,88 @@
if not TowBarMod then TowBarMod = {} end
if not TowBarMod.Hook then TowBarMod.Hook = {} end
local TowBarTowMass = 200
local DefaultTowBarTowMass = 200
local AutoReattachCooldownHours = 1 / 7200 -- 0.5 seconds
TowBarMod.Hook.lastAutoReattachAtByVehicle = TowBarMod.Hook.lastAutoReattachAtByVehicle or {}
local AutoReattachPlayerCooldownHours = 1 / 14400 -- 0.25 seconds
TowBarMod.Hook.lastAutoReattachAtByPlayer = TowBarMod.Hook.lastAutoReattachAtByPlayer or {}
local FreeRollTickInterval = 15
local freeRollTickCounter = 0
local function tryVehicleCall(vehicle, methodName, arg)
if not vehicle or not methodName then return false, nil end
local method = vehicle[methodName]
if method == nil then return false, nil end
return pcall(function()
if arg ~= nil then
return method(vehicle, arg)
end
return method(vehicle)
end)
end
local function storeOriginalVehicleCall(vehicle, modData, key, getterName)
if not vehicle or not modData or modData[key] ~= nil then return end
local ok, value = tryVehicleCall(vehicle, getterName)
if ok and value ~= nil then
modData[key] = value
end
end
local function applyFreeRollingTowState(vehicle)
if not vehicle then return end
local modData = vehicle:getModData()
if not modData then return end
if modData.towBarOriginalMass == nil then
modData.towBarOriginalMass = vehicle:getMass()
end
if modData.towBarOriginalBrakingForce == nil then
modData.towBarOriginalBrakingForce = vehicle:getBrakingForce()
end
storeOriginalVehicleCall(vehicle, modData, "towBarOriginalParkingBrakeOn", "isParkingBrakeOn")
storeOriginalVehicleCall(vehicle, modData, "towBarOriginalParkingBrake", "isParkingBrake")
storeOriginalVehicleCall(vehicle, modData, "towBarOriginalHandbrake", "isHandbrake")
local configuredTowMass = TowBarMod.Config and tonumber(TowBarMod.Config.towedVehicleRollingMass)
vehicle:setMass(configuredTowMass or DefaultTowBarTowMass)
vehicle:setBrakingForce(0)
tryVehicleCall(vehicle, "setParkingBrakeOn", false)
tryVehicleCall(vehicle, "setParkingBrake", false)
tryVehicleCall(vehicle, "setHandbrake", false)
tryVehicleCall(vehicle, "setBrake", false)
tryVehicleCall(vehicle, "setBraking", false)
vehicle:constraintChanged()
vehicle:updateTotalMass()
end
local function restoreFreeRollingTowState(vehicle, modData)
if not vehicle or not modData then return end
if modData.towBarOriginalMass ~= nil then
vehicle:setMass(modData.towBarOriginalMass)
end
if modData.towBarOriginalBrakingForce ~= nil then
vehicle:setBrakingForce(modData.towBarOriginalBrakingForce)
end
if modData.towBarOriginalParkingBrakeOn ~= nil then
tryVehicleCall(vehicle, "setParkingBrakeOn", modData.towBarOriginalParkingBrakeOn)
end
if modData.towBarOriginalParkingBrake ~= nil then
tryVehicleCall(vehicle, "setParkingBrake", modData.towBarOriginalParkingBrake)
end
if modData.towBarOriginalHandbrake ~= nil then
tryVehicleCall(vehicle, "setHandbrake", modData.towBarOriginalHandbrake)
end
vehicle:constraintChanged()
vehicle:updateTotalMass()
end
local function isTowBarTowPair(towingVehicle, towedVehicle)
if not towingVehicle or not towedVehicle then return false end
@@ -269,12 +346,7 @@ local function reattachTowBarPair(playerObj, towingVehicle, towedVehicle, requir
TowBarMod.Utils.updateAttachmentsForRigidTow(towingVehicle, towedVehicle, attachmentA, attachmentB)
towedModData.towBarOriginalScriptName = towedModData.towBarOriginalScriptName or towedVehicle:getScriptName()
if towedModData.towBarOriginalMass == nil then
towedModData.towBarOriginalMass = towedVehicle:getMass()
end
if towedModData.towBarOriginalBrakingForce == nil then
towedModData.towBarOriginalBrakingForce = towedVehicle:getBrakingForce()
end
applyFreeRollingTowState(towedVehicle)
towingModData["isTowingByTowBar"] = true
towedModData["isTowingByTowBar"] = true
@@ -376,10 +448,7 @@ function TowBarMod.Hook.setVehiclePostAttach(playerObj, towedVehicle, retriesLef
end
end
towedVehicle:setMass(TowBarTowMass)
towedVehicle:setBrakingForce(0)
towedVehicle:constraintChanged()
towedVehicle:updateTotalMass()
applyFreeRollingTowState(towedVehicle)
-- Re-show the towbar model after the script name has been restored.
-- setScriptName() resets model visibility, so we must set it again here.
@@ -402,8 +471,7 @@ function TowBarMod.Hook.performAttachTowBar(playerObj, towingVehicle, towedVehic
local towedModData = towedVehicle:getModData()
towedModData.towBarOriginalScriptName = towedVehicle:getScriptName()
towedModData.towBarOriginalMass = towedVehicle:getMass()
towedModData.towBarOriginalBrakingForce = towedVehicle:getBrakingForce()
applyFreeRollingTowState(towedVehicle)
towingModData["isTowingByTowBar"] = true
towedModData["isTowingByTowBar"] = true
@@ -438,14 +506,7 @@ function TowBarMod.Hook.performDetachTowBar(playerObj, towingVehicle, towedVehic
if towedModData.towBarOriginalScriptName then
towedVehicle:setScriptName(towedModData.towBarOriginalScriptName)
end
if towedModData.towBarOriginalMass ~= nil then
towedVehicle:setMass(towedModData.towBarOriginalMass)
end
if towedModData.towBarOriginalBrakingForce ~= nil then
towedVehicle:setBrakingForce(towedModData.towBarOriginalBrakingForce)
end
towedVehicle:constraintChanged()
towedVehicle:updateTotalMass()
restoreFreeRollingTowState(towedVehicle, towedModData)
sendClientCommand(playerObj, "towbar", "giveTowBar", { equipPrimary = true })
@@ -456,6 +517,9 @@ function TowBarMod.Hook.performDetachTowBar(playerObj, towingVehicle, towedVehic
towedModData.towBarOriginalScriptName = nil
towedModData.towBarOriginalMass = nil
towedModData.towBarOriginalBrakingForce = nil
towedModData.towBarOriginalParkingBrakeOn = nil
towedModData.towBarOriginalParkingBrake = nil
towedModData.towBarOriginalHandbrake = nil
towingVehicle:transmitModData()
towedVehicle:transmitModData()
@@ -526,6 +590,27 @@ local function forEachCollectionItem(collection, callback)
end
end
local function keepTowBarVehiclesFreeRolling()
freeRollTickCounter = freeRollTickCounter + 1
if freeRollTickCounter < FreeRollTickInterval then
return
end
freeRollTickCounter = 0
local cell = getCell()
if not cell then return end
local vehicles = cell:getVehicles()
if not vehicles then return end
forEachCollectionItem(vehicles, function(vehicle)
local modData = vehicle and vehicle:getModData() or nil
if isActiveTowBarTowedVehicle(vehicle, modData) then
applyFreeRollingTowState(vehicle)
end
end)
end
function TowBarMod.Hook.OnEnterVehicle(character)
tryAutoReattachFromCharacter(character)
end
@@ -716,3 +801,4 @@ if Events.OnGameStart then
end
Events.OnEnterVehicle.Add(TowBarMod.Hook.OnEnterVehicle)
Events.OnSwitchVehicleSeat.Add(TowBarMod.Hook.OnSwitchVehicleSeat)
Events.OnTick.Add(keepTowBarVehiclesFreeRolling)
+77 -12
View File
@@ -23,6 +23,53 @@ local function computeAttachmentHeight(vehicle)
return -0.5
end
local function getVehicleHalfLength(script)
if not script then return nil end
local ok, shape = pcall(function()
return script:getPhysicsChassisShape()
end)
if ok and shape then
local zOk, z = pcall(function()
return shape:z()
end)
z = zOk and tonumber(z) or nil
if z and z > 0 then return z / 2 end
end
ok, shape = pcall(function()
return script:getExtents()
end)
if ok and shape then
local zOk, z = pcall(function()
return shape:z()
end)
z = zOk and tonumber(z) or nil
if z and z > 0 then return z / 2 end
end
return nil
end
local function getExteriorAttachmentZ(script, attachmentId, originalZ, extraDistance)
local halfLength = getVehicleHalfLength(script)
if halfLength == nil then return originalZ end
local configuredPadding = TowBarMod.Config and tonumber(TowBarMod.Config.towAttachmentExteriorPadding)
local padding = configuredPadding or 0.25
local direction = originalZ >= 0 and 1 or -1
if originalZ == 0 and attachmentId == "trailer" then
direction = -1
end
local exteriorZ = direction * (halfLength + padding + (extraDistance or 0))
if math.abs(originalZ) > math.abs(exteriorZ) then
return originalZ
end
return exteriorZ
end
function TowBarMod.Utils.isTrailer(vehicle)
return string.match(string.lower(vehicle:getScript():getName()), "trailer")
end
@@ -93,8 +140,12 @@ function TowBarMod.Utils.getHookTypeVariants(vehicleA, vehicleB, hasTowBar)
end
function TowBarMod.Utils.updateAttachmentsForRigidTow(towingVehicle, towedVehicle, attachmentA, attachmentB)
local towingAttachment = towingVehicle:getScript():getAttachmentById(attachmentA)
local towedAttachment = towedVehicle:getScript():getAttachmentById(attachmentB)
local towingScript = towingVehicle:getScript()
local towedScript = towedVehicle:getScript()
if towingScript == nil or towedScript == nil then return end
local towingAttachment = towingScript:getAttachmentById(attachmentA)
local towedAttachment = towedScript:getAttachmentById(attachmentB)
if towingAttachment == nil or towedAttachment == nil then return end
towingAttachment:setUpdateConstraint(false)
@@ -109,12 +160,16 @@ function TowBarMod.Utils.updateAttachmentsForRigidTow(towingVehicle, towedVehicl
-- Store and update the towing vehicle's attachment Y.
local towingModData = towingVehicle:getModData()
if towingModData["towBarOriginalTowingOffsetY"] == nil then
towingModData["towBarOriginalTowingOffsetY"] = towingAttachment:getOffset():y()
local towingOffset = towingAttachment:getOffset()
if towingModData["towBarOriginalTowingAttachmentId"] ~= attachmentA
or towingModData["towBarOriginalTowingOffsetX"] == nil
or towingModData["towBarOriginalTowingOffsetY"] == nil
or towingModData["towBarOriginalTowingOffsetZ"] == nil then
towingModData["towBarOriginalTowingOffsetX"] = towingOffset:x()
towingModData["towBarOriginalTowingOffsetY"] = towingOffset:y()
towingModData["towBarOriginalTowingOffsetZ"] = towingOffset:z()
towingModData["towBarOriginalTowingAttachmentId"] = attachmentA
end
local towingOffset = towingAttachment:getOffset()
towingAttachment:getOffset():set(towingOffset:x(), towingHeight, towingOffset:z())
local towedModData = towedVehicle:getModData()
local spacingDistance = 1.0
@@ -122,6 +177,11 @@ function TowBarMod.Utils.updateAttachmentsForRigidTow(towingVehicle, towedVehicl
spacingDistance = tonumber(TowBarMod.Config.rigidTowbarDistance)
end
local towingBaseX = tonumber(towingModData["towBarOriginalTowingOffsetX"]) or towingOffset:x()
local towingBaseZ = tonumber(towingModData["towBarOriginalTowingOffsetZ"]) or towingOffset:z()
local towingExteriorZ = getExteriorAttachmentZ(towingScript, attachmentA, towingBaseZ, 0)
towingAttachment:getOffset():set(towingBaseX, towingHeight, towingExteriorZ)
local offset = towedAttachment:getOffset()
local storedBaseX = tonumber(towedModData["towBarBaseAttachmentOffsetX"])
local storedBaseY = tonumber(towedModData["towBarBaseAttachmentOffsetY"])
@@ -140,9 +200,9 @@ function TowBarMod.Utils.updateAttachmentsForRigidTow(towingVehicle, towedVehicl
towedModData["towBarBaseAttachmentOffsetZ"] = baseZ
end
local zDirection = baseZ >= 0 and 1 or -1
local zShift = zDirection * spacingDistance
towedAttachment:getOffset():set(baseX, towedHeight, baseZ + zShift)
local towedExteriorZ = getExteriorAttachmentZ(towedScript, attachmentB, baseZ, spacingDistance)
local zShift = towedExteriorZ - baseZ
towedAttachment:getOffset():set(baseX, towedHeight, towedExteriorZ)
towedModData["isChangedTowedAttachment"] = true
towedModData["towBarChangedAttachmentId"] = attachmentB
@@ -161,14 +221,20 @@ function TowBarMod.Utils.updateAttachmentsOnDefaultValues(towingVehicle, towedVe
local zOffset = (towingAttachmentId == "trailer") and -1 or 1
towingAttachment:setZOffset(zOffset)
-- Restore the original Y offset that was overridden by dynamic height.
-- Restore the original offset that was overridden for rigid tow spacing.
local originalX = tonumber(towingModData["towBarOriginalTowingOffsetX"])
local originalY = tonumber(towingModData["towBarOriginalTowingOffsetY"])
if originalY ~= nil then
local originalZ = tonumber(towingModData["towBarOriginalTowingOffsetZ"])
if originalX ~= nil and originalY ~= nil and originalZ ~= nil then
towingAttachment:getOffset():set(originalX, originalY, originalZ)
elseif originalY ~= nil then
local off = towingAttachment:getOffset()
towingAttachment:getOffset():set(off:x(), originalY, off:z())
end
end
towingModData["towBarOriginalTowingOffsetX"] = nil
towingModData["towBarOriginalTowingOffsetY"] = nil
towingModData["towBarOriginalTowingOffsetZ"] = nil
towingModData["towBarOriginalTowingAttachmentId"] = nil
towingVehicle:transmitModData()
@@ -249,4 +315,3 @@ local function fixTowAttachmentsForOtherVehicleMods()
end
Events.OnGameBoot.Add(fixTowAttachmentsForOtherVehicleMods)