BTtow = {} BTtow.Create = {} BTtow.Init = {} require("TowBar/VehicleCompatibility") local TowbarVariantSize = 24 local TowbarMaxIndex = TowbarVariantSize - 1 local TowbarFirstZ = 1.0 local TowbarSlotStep = 0.1 -- Measured from Towbar.fbx at its original 0.01 script scale. Keep this in -- lockstep with the client selector so the enlarged mesh's inner end remains -- on the same vehicle-hitbox contact point. local TowbarVisualScale = 2.5 local TowbarModelLength = 0.9714089036 local TowbarScaledModelLength = TowbarModelLength * TowbarVisualScale local TowbarModelHalfLength = TowbarScaledModelLength / 2 local VanillaScaleMin = 1.5 local VanillaScaleMax = 2.0 local function getTowbarFrontEdgeZ(script) if not script then return nil end local ok, shape = pcall(function() return script:getPhysicsChassisShape() end) if not ok or not shape then return nil end local shapeOk, shapeZ = pcall(function() return shape:z() end) if not shapeOk or type(shapeZ) ~= "number" or shapeZ <= 0 then return nil end local centerOk, center = pcall(function() return script:getCenterOfMassOffset() end) if not centerOk or not center then return nil end local zOk, centerZ = pcall(function() return center:z() end) if not zOk or type(centerZ) ~= "number" then return nil end return centerZ + shapeZ / 2 end local function getTowbarModelSlot(script) local frontEdgeZ = getTowbarFrontEdgeZ(script) if frontEdgeZ == nil then return nil end -- Model offsets position the mesh origin. Move its center outward so the -- towbar's inner end, rather than its center, meets the vehicle hitbox. local modelCenterZ = frontEdgeZ + TowbarModelHalfLength local index = math.floor(((modelCenterZ - TowbarFirstZ) / TowbarSlotStep) + 0.5) return math.max(0, math.min(TowbarMaxIndex, index)) end local function getVehicleModelScale(script) if not script then return nil end local ok, result = pcall(function() return script:getModelScale() end) if ok and type(result) == "number" then return result end ok, result = pcall(function() local model = script:getModel() return model and model:getScale() or nil end) return ok and type(result) == "number" and result or nil end local function isVanillaScale(script) local modelScale = getVehicleModelScale(script) if modelScale == nil then return true end local configuredMin = TowBarMod.Config and tonumber(TowBarMod.Config.vanillaTowbarModelScaleMin) local configuredMax = TowBarMod.Config and tonumber(TowBarMod.Config.vanillaTowbarModelScaleMax) return modelScale >= (configuredMin or VanillaScaleMin) and modelScale <= (configuredMax or VanillaScaleMax) end local function getTowbarIndexVanilla(script) if not script then return nil end local ok, shape = pcall(function() return script:getPhysicsChassisShape() end) if not ok or not shape then return nil end local zOk, shapeZ = pcall(function() return shape:z() end) if not zOk or type(shapeZ) ~= "number" then return nil end local z = shapeZ / 2 - 0.1 local index = math.floor((z * 2 / 3 - 1) * 10) return math.max(0, math.min(TowbarMaxIndex, index)) end local function getTowbarIndexSmallScale(script) if not script then return nil end local maxAbsTowZ = nil local trailer = script:getAttachmentById("trailer") if trailer then maxAbsTowZ = math.abs(trailer:getOffset():z()) end local trailerFront = script:getAttachmentById("trailerfront") if trailerFront then local frontAbsZ = math.abs(trailerFront:getOffset():z()) if not maxAbsTowZ or frontAbsZ > maxAbsTowZ then maxAbsTowZ = frontAbsZ end end if maxAbsTowZ == nil then return nil end local index = math.floor((maxAbsTowZ + 0.1 - 1.0) * 10) return math.max(0, math.min(TowbarMaxIndex, index)) end local function getLegacyTowbarModelSlot(script) local useNormalPart = isVanillaScale(script) local index = getTowbarIndexVanilla(script) if not useNormalPart then index = getTowbarIndexSmallScale(script) or index end return index, useNormalPart end function BTtow.Create.towbar(vehicle, part) if part == nil then return end for j=0, TowbarVariantSize - 1 do part:setModelVisible("towbar" .. j, false) end end function BTtow.Init.towbar(vehicle, part) if part == nil then return end for j=0, TowbarVariantSize - 1 do part:setModelVisible("towbar" .. j, false) end local modData = vehicle:getModData() if modData and modData.towBarModelSwapInProgress then return end if modData and modData["isTowingByTowBar"] and modData["towed"] then local script = vehicle:getScript() if script then local isKi5 = TowBarMod.Compatibility.isKi5Vehicle(vehicle) local index, useNormalPart if isKi5 then index = getTowbarModelSlot(script) else index, useNormalPart = getLegacyTowbarModelSlot(script) end local partId = part:getId() local shouldShowOnThisPart = (isKi5 and partId == "towbarKI5") or (not isKi5 and useNormalPart and partId == "towbar") or (not isKi5 and not useNormalPart and partId == "towbarLarge") if shouldShowOnThisPart and index ~= nil then part:setModelVisible("towbar" .. index, true) end end end end