if not TowBarMod then TowBarMod = {} end if not TowBarMod.Utils then TowBarMod.Utils = {} end TowBarMod.Utils.tempVector1 = Vector3f.new() TowBarMod.Utils.tempVector2 = Vector3f.new() --------------------------------------------------------------------------- --- Util functions --------------------------------------------------------------------------- function TowBarMod.Utils.isTrailer(vehicle) return string.match(string.lower(vehicle:getScript():getName()), "trailer") end --- Return vehicles from sector that player can tow by tow bar. function TowBarMod.Utils.getAviableVehicles(mainVehicle, hasTowBar) local vehicles = {} if not hasTowBar then return vehicles end local square = mainVehicle:getSquare() if square == nil then return vehicles end -- Match vanilla towing search radius. for y=square:getY() - 6, square:getY() + 6 do for x=square:getX() - 6, square:getX() + 6 do local square2 = getCell():getGridSquare(x, y, square:getZ()) if square2 then for i=1, square2:getMovingObjects():size() do local obj = square2:getMovingObjects():get(i-1) if obj ~= nil and instanceof(obj, "BaseVehicle") and obj ~= mainVehicle and #(TowBarMod.Utils.getHookTypeVariants(mainVehicle, obj, hasTowBar)) ~= 0 then table.insert(vehicles, obj) end end end end end return vehicles end --- Return a table with towbar-only hook options for vehicles. function TowBarMod.Utils.getHookTypeVariants(vehicleA, vehicleB, hasTowBar) local hookTypeVariants = {} if not hasTowBar then return hookTypeVariants end if vehicleA:getVehicleTowing() or vehicleA:getVehicleTowedBy() or vehicleB:getVehicleTowing() or vehicleB:getVehicleTowedBy() then return hookTypeVariants end -- Keep tow bars for vehicle-to-vehicle towing only. if TowBarMod.Utils.isTrailer(vehicleA) or TowBarMod.Utils.isTrailer(vehicleB) then return hookTypeVariants end if vehicleA:canAttachTrailer(vehicleB, "trailerfront", "trailer") then local hookType = {} hookType.name = getText("UI_Text_Towing_attach") .. "\n" .. ISVehicleMenu.getVehicleDisplayName(vehicleB) .. "\n" .. getText("UI_Text_Towing_byTowBar") hookType.func = TowBarMod.Hook.attachByTowBarAction hookType.towingVehicle = vehicleB hookType.towedVehicle = vehicleA hookType.textureName = "tow_bar_icon" table.insert(hookTypeVariants, hookType) elseif vehicleA:canAttachTrailer(vehicleB, "trailer", "trailerfront") then local hookType = {} hookType.name = getText("UI_Text_Towing_attach") .. "\n" .. ISVehicleMenu.getVehicleDisplayName(vehicleB) .. "\n" .. getText("UI_Text_Towing_byTowBar") hookType.func = TowBarMod.Hook.attachByTowBarAction hookType.towingVehicle = vehicleA hookType.towedVehicle = vehicleB hookType.textureName = "tow_bar_icon" table.insert(hookTypeVariants, hookType) end return hookTypeVariants end function TowBarMod.Utils.updateAttachmentsForRigidTow(towingVehicle, towedVehicle, attachmentA, attachmentB) local towingAttachment = towingVehicle:getScript():getAttachmentById(attachmentA) local towedAttachment = towedVehicle:getScript():getAttachmentById(attachmentB) if towingAttachment == nil or towedAttachment == nil then return end towingAttachment:setUpdateConstraint(false) towingAttachment:setZOffset(0) towedAttachment:setUpdateConstraint(false) towedAttachment:setZOffset(0) local towedModData = towedVehicle:getModData() local spacingDistance = 1.0 if TowBarMod.Config and tonumber(TowBarMod.Config.rigidTowbarDistance) ~= nil then spacingDistance = tonumber(TowBarMod.Config.rigidTowbarDistance) end local offset = towedAttachment:getOffset() local storedBaseX = tonumber(towedModData["towBarBaseAttachmentOffsetX"]) local storedBaseY = tonumber(towedModData["towBarBaseAttachmentOffsetY"]) local storedBaseZ = tonumber(towedModData["towBarBaseAttachmentOffsetZ"]) local hasStoredBase = towedModData["towBarBaseAttachmentId"] == attachmentB and storedBaseX ~= nil and storedBaseY ~= nil and storedBaseZ ~= nil local baseX = hasStoredBase and storedBaseX or offset:x() local baseY = hasStoredBase and storedBaseY or offset:y() local baseZ = hasStoredBase and storedBaseZ or offset:z() if not hasStoredBase then towedModData["towBarBaseAttachmentId"] = attachmentB towedModData["towBarBaseAttachmentOffsetX"] = baseX towedModData["towBarBaseAttachmentOffsetY"] = baseY towedModData["towBarBaseAttachmentOffsetZ"] = baseZ end local zDirection = baseZ >= 0 and 1 or -1 local zShift = zDirection * spacingDistance towedAttachment:getOffset():set(baseX, baseY, baseZ + zShift) towedModData["isChangedTowedAttachment"] = true towedModData["towBarChangedAttachmentId"] = attachmentB towedModData["towBarChangedOffsetZShift"] = zShift towedVehicle:transmitModData() end function TowBarMod.Utils.updateAttachmentsOnDefaultValues(towingVehicle, towedVehicle) local towingAttachmentId = towingVehicle:getTowAttachmentSelf() local towingAttachment = towingVehicle:getScript():getAttachmentById(towingAttachmentId) if towingAttachment ~= nil then towingAttachment:setUpdateConstraint(true) local zOffset = (towingAttachmentId == "trailer") and -1 or 1 towingAttachment:setZOffset(zOffset) end local towedModData = towedVehicle:getModData() local changedAttachmentId = towedModData["towBarChangedAttachmentId"] or towedVehicle:getTowAttachmentSelf() local towedAttachment = towedVehicle:getScript():getAttachmentById(changedAttachmentId) if towedAttachment ~= nil then towedAttachment:setUpdateConstraint(true) local zOffset = (changedAttachmentId == "trailer") and -1 or 1 towedAttachment:setZOffset(zOffset) if towedModData["isChangedTowedAttachment"] then local storedBaseX = tonumber(towedModData["towBarBaseAttachmentOffsetX"]) local storedBaseY = tonumber(towedModData["towBarBaseAttachmentOffsetY"]) local storedBaseZ = tonumber(towedModData["towBarBaseAttachmentOffsetZ"]) local hasStoredBase = towedModData["towBarBaseAttachmentId"] == changedAttachmentId and storedBaseX ~= nil and storedBaseY ~= nil and storedBaseZ ~= nil if hasStoredBase then towedAttachment:getOffset():set(storedBaseX, storedBaseY, storedBaseZ) else local offset = towedAttachment:getOffset() local storedShift = tonumber(towedModData["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 towedModData["isChangedTowedAttachment"] = false towedModData["towBarChangedAttachmentId"] = nil towedModData["towBarChangedOffsetZShift"] = nil towedModData["towBarBaseAttachmentId"] = nil towedModData["towBarBaseAttachmentOffsetX"] = nil towedModData["towBarBaseAttachmentOffsetY"] = nil towedModData["towBarBaseAttachmentOffsetZ"] = nil towedVehicle:transmitModData() end ----------------------------------------------------------- --- Fix mods that add vehicles without tow attachments local function fixTowAttachmentsForOtherVehicleMods() local scriptManager = getScriptManager() local vehicleScripts = scriptManager:getAllVehicleScripts() for i = 0, vehicleScripts:size()-1 do local script = vehicleScripts:get(i) local wheelCount = script:getWheelCount() local attachHeigtOffset = -0.5 if wheelCount > 0 then attachHeigtOffset = script:getWheel(0):getOffset():y() + 0.1 end if not string.match(string.lower(script:getName()), "trailer") then local trailerAttachment = script:getAttachmentById("trailer") if trailerAttachment == nil then local attach = ModelAttachment.new("trailer") attach:getOffset():set(0, attachHeigtOffset, -script:getPhysicsChassisShape():z()/2 - 0.1) attach:setZOffset(-1) script:addAttachment(attach) end local trailerFrontAttachment = script:getAttachmentById("trailerfront") if trailerFrontAttachment == nil then local attach = ModelAttachment.new("trailerfront") attach:getOffset():set(0, attachHeigtOffset, script:getPhysicsChassisShape():z()/2 + 0.1) attach:setZOffset(1) script:addAttachment(attach) end end end end Events.OnGameBoot.Add(fixTowAttachmentsForOtherVehicleMods)