Landtrain!!!!
This commit is contained in:
483
42.13/media/lua/client/Landtrain/UnlimitedTowbarChains.lua
Normal file
483
42.13/media/lua/client/Landtrain/UnlimitedTowbarChains.lua
Normal file
@@ -0,0 +1,483 @@
|
||||
if not TowBarMod then TowBarMod = {} end
|
||||
if not TowBarMod.Utils then TowBarMod.Utils = {} end
|
||||
if not TowBarMod.UI then TowBarMod.UI = {} end
|
||||
|
||||
local LANDTRAIN_DEBUG = false
|
||||
local LANDTRAIN_ATTACH_LABEL = "Landtrain attach"
|
||||
local LANDTRAIN_FALLBACK_MAX_DIST_SQ = 3.25 -- ~1.8 tiles
|
||||
local LANDTRAIN_FALLBACK_MAX_DZ = 0.9
|
||||
|
||||
local function ltLog(msg)
|
||||
if not LANDTRAIN_DEBUG then return end
|
||||
print("[Landtrain] " .. tostring(msg))
|
||||
end
|
||||
|
||||
local function vehLabel(vehicle)
|
||||
if vehicle == nil then return "nil" end
|
||||
local name = vehicle:getScriptName() or "unknown"
|
||||
return tostring(vehicle:getId()) .. ":" .. tostring(name)
|
||||
end
|
||||
|
||||
local function dumpTowState(prefix, vehicle)
|
||||
if not LANDTRAIN_DEBUG then return end
|
||||
if vehicle == nil then
|
||||
ltLog(prefix .. " vehicle=nil")
|
||||
return
|
||||
end
|
||||
local modData = vehicle:getModData() or {}
|
||||
local front = vehicle:getVehicleTowedBy()
|
||||
local rear = vehicle:getVehicleTowing()
|
||||
ltLog(prefix
|
||||
.. " v=" .. vehLabel(vehicle)
|
||||
.. " front=" .. vehLabel(front)
|
||||
.. " rear=" .. vehLabel(rear)
|
||||
.. " md.isTowingByTowBar=" .. tostring(modData["isTowingByTowBar"])
|
||||
.. " md.towed=" .. tostring(modData["towed"]))
|
||||
end
|
||||
|
||||
local function refreshTowBarState(vehicle)
|
||||
if vehicle == nil then return end
|
||||
|
||||
local modData = vehicle:getModData()
|
||||
if modData == nil then return end
|
||||
|
||||
local frontVehicle = vehicle:getVehicleTowedBy()
|
||||
local rearVehicle = vehicle:getVehicleTowing()
|
||||
|
||||
local isTowedByTowBar = false
|
||||
if frontVehicle ~= nil then
|
||||
local frontModData = frontVehicle:getModData()
|
||||
if modData["towed"] == true or (frontModData and frontModData["isTowingByTowBar"] == true) then
|
||||
isTowedByTowBar = true
|
||||
end
|
||||
end
|
||||
|
||||
local isTowingByTowBar = false
|
||||
if rearVehicle ~= nil then
|
||||
local rearModData = rearVehicle:getModData()
|
||||
if rearModData and rearModData["towed"] == true and rearModData["isTowingByTowBar"] == true then
|
||||
isTowingByTowBar = true
|
||||
end
|
||||
end
|
||||
|
||||
modData["towed"] = isTowedByTowBar
|
||||
modData["isTowingByTowBar"] = (isTowedByTowBar or isTowingByTowBar)
|
||||
vehicle:transmitModData()
|
||||
end
|
||||
|
||||
local function setTowBarModelVisibleForVehicle(vehicle, visible)
|
||||
if vehicle == nil then return end
|
||||
local part = vehicle:getPartById("towbar")
|
||||
if part == nil then return end
|
||||
|
||||
for j = 0, 23 do
|
||||
part:setModelVisible("towbar" .. j, false)
|
||||
end
|
||||
|
||||
if visible then
|
||||
local z = vehicle:getScript():getPhysicsChassisShape():z() / 2 - 0.1
|
||||
local index = math.floor((z * 2 / 3 - 1) * 10)
|
||||
if index < 0 then index = 0 end
|
||||
if index > 23 then index = 23 end
|
||||
part:setModelVisible("towbar" .. index, true)
|
||||
end
|
||||
|
||||
vehicle:doDamageOverlay()
|
||||
end
|
||||
|
||||
local _landtrainHookPosA = Vector3f.new()
|
||||
local _landtrainHookPosB = Vector3f.new()
|
||||
|
||||
local function hasTowAttachment(vehicle, attachmentId)
|
||||
if vehicle == nil or attachmentId == nil then return false end
|
||||
local script = vehicle:getScript()
|
||||
if script == nil then return false end
|
||||
return script:getAttachmentById(attachmentId) ~= nil
|
||||
end
|
||||
|
||||
local function isAttachmentSideFree(vehicle, attachmentId)
|
||||
if vehicle == nil then return false end
|
||||
if attachmentId == "trailer" then
|
||||
return vehicle:getVehicleTowing() == nil
|
||||
end
|
||||
if attachmentId == "trailerfront" then
|
||||
return vehicle:getVehicleTowedBy() == nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function canTowByLandtrain(vehicleA, vehicleB, attachmentA, attachmentB)
|
||||
if vehicleA == nil or vehicleB == nil then return false end
|
||||
if vehicleA == vehicleB then return false end
|
||||
if not hasTowAttachment(vehicleA, attachmentA) then return false end
|
||||
if not hasTowAttachment(vehicleB, attachmentB) then return false end
|
||||
if not isAttachmentSideFree(vehicleA, attachmentA) then return false end
|
||||
if not isAttachmentSideFree(vehicleB, attachmentB) then return false end
|
||||
if vehicleA:getVehicleTowing() == vehicleB or vehicleA:getVehicleTowedBy() == vehicleB then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Keep vanilla behavior when possible.
|
||||
if vehicleA:canAttachTrailer(vehicleB, attachmentA, attachmentB) then
|
||||
ltLog("canTowByLandtrain vanilla=true A=" .. vehLabel(vehicleA) .. " B=" .. vehLabel(vehicleB) .. " attA=" .. tostring(attachmentA) .. " attB=" .. tostring(attachmentB))
|
||||
return true
|
||||
end
|
||||
|
||||
-- Vanilla blocks chained towing here; allow only near-identical close-range hookups.
|
||||
local posA = vehicleA:getAttachmentWorldPos(attachmentA, _landtrainHookPosA)
|
||||
local posB = vehicleB:getAttachmentWorldPos(attachmentB, _landtrainHookPosB)
|
||||
if posA == nil or posB == nil then return false end
|
||||
|
||||
local dx = posA:x() - posB:x()
|
||||
local dy = posA:y() - posB:y()
|
||||
local dz = posA:z() - posB:z()
|
||||
local distSq = dx * dx + dy * dy + dz * dz
|
||||
local allow = distSq <= LANDTRAIN_FALLBACK_MAX_DIST_SQ and math.abs(dz) <= LANDTRAIN_FALLBACK_MAX_DZ
|
||||
ltLog("canTowByLandtrain fallback=" .. tostring(allow)
|
||||
.. " A=" .. vehLabel(vehicleA)
|
||||
.. " B=" .. vehLabel(vehicleB)
|
||||
.. " attA=" .. tostring(attachmentA)
|
||||
.. " attB=" .. tostring(attachmentB)
|
||||
.. " distSq=" .. string.format("%.3f", distSq)
|
||||
.. " dz=" .. string.format("%.3f", dz))
|
||||
return allow
|
||||
end
|
||||
|
||||
local function getAttachLabel(vehicleA, vehicleB)
|
||||
local isLandtrainLink = (vehicleA and (vehicleA:getVehicleTowing() or vehicleA:getVehicleTowedBy()))
|
||||
or (vehicleB and (vehicleB:getVehicleTowing() or vehicleB:getVehicleTowedBy()))
|
||||
if isLandtrainLink then
|
||||
return LANDTRAIN_ATTACH_LABEL
|
||||
end
|
||||
return getText("UI_Text_Towing_byTowBar")
|
||||
end
|
||||
|
||||
local function captureTowbarFrontLink(towingVehicle)
|
||||
if towingVehicle == nil then
|
||||
ltLog("captureTowbarFrontLink towingVehicle=nil")
|
||||
return nil
|
||||
end
|
||||
|
||||
local frontVehicle = towingVehicle:getVehicleTowedBy()
|
||||
if frontVehicle == nil then
|
||||
ltLog("captureTowbarFrontLink no front link for " .. vehLabel(towingVehicle))
|
||||
return nil
|
||||
end
|
||||
|
||||
local link = {
|
||||
frontVehicle = frontVehicle,
|
||||
towingVehicle = towingVehicle,
|
||||
attachmentA = frontVehicle:getTowAttachmentSelf() or "trailer",
|
||||
attachmentB = towingVehicle:getTowAttachmentSelf() or "trailerfront"
|
||||
}
|
||||
ltLog("captureTowbarFrontLink captured front=" .. vehLabel(frontVehicle) .. " middle=" .. vehLabel(towingVehicle)
|
||||
.. " attA=" .. tostring(link.attachmentA) .. " attB=" .. tostring(link.attachmentB))
|
||||
return link
|
||||
end
|
||||
|
||||
local function restoreTowbarFrontLink(playerObj, link)
|
||||
if link == nil then
|
||||
ltLog("restoreTowbarFrontLink skipped (no captured link)")
|
||||
return
|
||||
end
|
||||
local frontVehicle = link.frontVehicle
|
||||
local towingVehicle = link.towingVehicle
|
||||
if frontVehicle == nil or towingVehicle == nil then
|
||||
ltLog("restoreTowbarFrontLink invalid captured refs")
|
||||
return
|
||||
end
|
||||
if towingVehicle:getVehicleTowedBy() ~= nil then
|
||||
ltLog("restoreTowbarFrontLink not needed; front still connected for middle=" .. vehLabel(towingVehicle))
|
||||
return
|
||||
end
|
||||
|
||||
ltLog("restoreTowbarFrontLink restoring front=" .. vehLabel(frontVehicle) .. " middle=" .. vehLabel(towingVehicle))
|
||||
TowBarMod.Utils.updateAttachmentsForRigidTow(frontVehicle, towingVehicle, link.attachmentA, link.attachmentB)
|
||||
towingVehicle:setScriptName("notTowingA_Trailer")
|
||||
|
||||
local args = {
|
||||
vehicleA = frontVehicle:getId(),
|
||||
vehicleB = towingVehicle:getId(),
|
||||
attachmentA = link.attachmentA,
|
||||
attachmentB = link.attachmentB
|
||||
}
|
||||
sendClientCommand(playerObj, "vehicle", "attachTrailer", args)
|
||||
ISTimedActionQueue.add(TowBarScheduleAction:new(playerObj, 10, TowBarMod.Hook.setVehiclePostAttach, towingVehicle))
|
||||
|
||||
local frontModData = frontVehicle:getModData()
|
||||
local towingModData = towingVehicle:getModData()
|
||||
frontModData["isTowingByTowBar"] = true
|
||||
towingModData["isTowingByTowBar"] = true
|
||||
towingModData["towed"] = true
|
||||
frontVehicle:transmitModData()
|
||||
towingVehicle:transmitModData()
|
||||
dumpTowState("restoreTowbarFrontLink after front", frontVehicle)
|
||||
dumpTowState("restoreTowbarFrontLink after middle", towingVehicle)
|
||||
end
|
||||
|
||||
local function queueTowbarFrontLinkRestore(playerObj, link, delayTicks)
|
||||
if link == nil or playerObj == nil then return end
|
||||
local delay = delayTicks or 15
|
||||
ISTimedActionQueue.add(TowBarScheduleAction:new(playerObj, delay, function(character, linkArg)
|
||||
ltLog("queueTowbarFrontLinkRestore fire delay=" .. tostring(delay) .. " middle=" .. vehLabel(linkArg and linkArg.towingVehicle or nil))
|
||||
restoreTowbarFrontLink(character, linkArg)
|
||||
end, link))
|
||||
end
|
||||
|
||||
local function ensureTowAttachmentsForTrailers()
|
||||
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)
|
||||
local scriptName = script and script:getName() or nil
|
||||
if script and scriptName and string.match(string.lower(scriptName), "trailer") then
|
||||
local wheelCount = script:getWheelCount()
|
||||
local attachHeightOffset = -0.5
|
||||
if wheelCount > 0 then
|
||||
attachHeightOffset = script:getWheel(0):getOffset():y() + 0.1
|
||||
end
|
||||
|
||||
local rearTow = script:getAttachmentById("trailer")
|
||||
if rearTow == nil then
|
||||
local attach = ModelAttachment.new("trailer")
|
||||
attach:getOffset():set(0, attachHeightOffset, -script:getPhysicsChassisShape():z() / 2 - 0.1)
|
||||
attach:setZOffset(-1)
|
||||
script:addAttachment(attach)
|
||||
end
|
||||
|
||||
local frontTow = script:getAttachmentById("trailerfront")
|
||||
if frontTow == nil then
|
||||
local attach = ModelAttachment.new("trailerfront")
|
||||
attach:getOffset():set(0, attachHeightOffset, script:getPhysicsChassisShape():z() / 2 + 0.1)
|
||||
attach:setZOffset(1)
|
||||
script:addAttachment(attach)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function menuHasTowbarAttachSlice(menu)
|
||||
if menu == nil or menu.slices == nil then return false end
|
||||
for _, slice in ipairs(menu.slices) do
|
||||
local command = slice.command and slice.command[1]
|
||||
if command == TowBarMod.Hook.attachByTowBarAction or command == TowBarMod.UI.showChooseVehicleMenu then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function getLandtrainHookTypeVariants(vehicleA, vehicleB)
|
||||
local hookTypeVariants = {}
|
||||
if vehicleA == nil or vehicleB == nil or vehicleA == vehicleB then
|
||||
return hookTypeVariants
|
||||
end
|
||||
|
||||
if canTowByLandtrain(vehicleA, vehicleB, "trailerfront", "trailer") then
|
||||
local hookType = {}
|
||||
hookType.name = getText("UI_Text_Towing_attach") .. "\n" .. ISVehicleMenu.getVehicleDisplayName(vehicleB) .. "\n" .. LANDTRAIN_ATTACH_LABEL
|
||||
hookType.func = TowBarMod.Hook.attachByTowBarAction
|
||||
hookType.towingVehicle = vehicleB
|
||||
hookType.towedVehicle = vehicleA
|
||||
hookType.textureName = "tow_bar_icon"
|
||||
table.insert(hookTypeVariants, hookType)
|
||||
elseif canTowByLandtrain(vehicleA, vehicleB, "trailer", "trailerfront") then
|
||||
local hookType = {}
|
||||
hookType.name = getText("UI_Text_Towing_attach") .. "\n" .. ISVehicleMenu.getVehicleDisplayName(vehicleB) .. "\n" .. LANDTRAIN_ATTACH_LABEL
|
||||
hookType.func = TowBarMod.Hook.attachByTowBarAction
|
||||
hookType.towingVehicle = vehicleA
|
||||
hookType.towedVehicle = vehicleB
|
||||
hookType.textureName = "tow_bar_icon"
|
||||
table.insert(hookTypeVariants, hookType)
|
||||
end
|
||||
|
||||
return hookTypeVariants
|
||||
end
|
||||
|
||||
local function getNearbyLandtrainTargets(mainVehicle)
|
||||
local vehicles = {}
|
||||
local square = mainVehicle and mainVehicle:getSquare() or nil
|
||||
if square == nil then return vehicles end
|
||||
|
||||
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 then
|
||||
local variants = getLandtrainHookTypeVariants(mainVehicle, obj)
|
||||
if #variants > 0 then
|
||||
table.insert(vehicles, { vehicle = obj, variants = variants })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return vehicles
|
||||
end
|
||||
|
||||
local function addLandtrainHookOptionToMenu(playerObj, vehicle)
|
||||
if playerObj == nil or vehicle == nil then return end
|
||||
if not playerObj:getInventory():getItemFromTypeRecurse("TowBar.TowBar") then return end
|
||||
|
||||
local menu = getPlayerRadialMenu(playerObj:getPlayerNum())
|
||||
if menu == nil then return end
|
||||
|
||||
local targets = getNearbyLandtrainTargets(vehicle)
|
||||
if #targets == 0 then
|
||||
ltLog("addLandtrainHookOptionToMenu no nearby valid targets for " .. vehLabel(vehicle))
|
||||
return
|
||||
end
|
||||
|
||||
if #targets == 1 then
|
||||
local hookType = targets[1].variants[1]
|
||||
menu:addSlice(
|
||||
hookType.name,
|
||||
getTexture("media/textures/tow_bar_attach.png"),
|
||||
hookType.func,
|
||||
playerObj,
|
||||
hookType.towingVehicle,
|
||||
hookType.towedVehicle,
|
||||
hookType.towingPoint,
|
||||
hookType.towedPoint
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
-- Reuse Towbar's chooser UI by passing only the candidate vehicles.
|
||||
local vehicleList = {}
|
||||
for _, entry in ipairs(targets) do
|
||||
table.insert(vehicleList, entry.vehicle)
|
||||
end
|
||||
menu:addSlice(
|
||||
LANDTRAIN_ATTACH_LABEL .. "...",
|
||||
getTexture("media/textures/tow_bar_attach.png"),
|
||||
TowBarMod.UI.showChooseVehicleMenu,
|
||||
playerObj,
|
||||
vehicle,
|
||||
vehicleList,
|
||||
true
|
||||
)
|
||||
end
|
||||
|
||||
local function installLandtrainTowbarPatch()
|
||||
if not TowBarMod or not TowBarMod.Utils then
|
||||
return
|
||||
end
|
||||
|
||||
if TowBarMod.Utils._landtrainUnlimitedChainsInstalled then
|
||||
return
|
||||
end
|
||||
|
||||
-- Override Towbar's single-link limitation so a vehicle can be part of a chain.
|
||||
function TowBarMod.Utils.getHookTypeVariants(vehicleA, vehicleB, hasTowBar)
|
||||
local hookTypeVariants = {}
|
||||
if not hasTowBar then return hookTypeVariants end
|
||||
if vehicleA == nil or vehicleB == nil then return hookTypeVariants end
|
||||
if vehicleA == vehicleB then return hookTypeVariants end
|
||||
|
||||
-- Allow trailer <-> vehicle and trailer <-> trailer links for landtrains.
|
||||
if canTowByLandtrain(vehicleA, vehicleB, "trailerfront", "trailer") then
|
||||
local hookType = {}
|
||||
hookType.name = getText("UI_Text_Towing_attach") .. "\n" .. ISVehicleMenu.getVehicleDisplayName(vehicleB) .. "\n" .. getAttachLabel(vehicleA, vehicleB)
|
||||
hookType.func = TowBarMod.Hook.attachByTowBarAction
|
||||
hookType.towingVehicle = vehicleB
|
||||
hookType.towedVehicle = vehicleA
|
||||
hookType.textureName = "tow_bar_icon"
|
||||
table.insert(hookTypeVariants, hookType)
|
||||
elseif canTowByLandtrain(vehicleA, vehicleB, "trailer", "trailerfront") then
|
||||
local hookType = {}
|
||||
hookType.name = getText("UI_Text_Towing_attach") .. "\n" .. ISVehicleMenu.getVehicleDisplayName(vehicleB) .. "\n" .. getAttachLabel(vehicleA, vehicleB)
|
||||
hookType.func = TowBarMod.Hook.attachByTowBarAction
|
||||
hookType.towingVehicle = vehicleA
|
||||
hookType.towedVehicle = vehicleB
|
||||
hookType.textureName = "tow_bar_icon"
|
||||
table.insert(hookTypeVariants, hookType)
|
||||
end
|
||||
|
||||
return hookTypeVariants
|
||||
end
|
||||
|
||||
-- Keep towbar state valid for middle links in a chain after detach/attach.
|
||||
local originalPerformAttach = TowBarMod.Hook and TowBarMod.Hook.performAttachTowBar
|
||||
if originalPerformAttach then
|
||||
TowBarMod.Hook.performAttachTowBar = function(playerObj, towingVehicle, towedVehicle, attachmentA, attachmentB)
|
||||
ltLog("performAttachTowBar begin towing=" .. vehLabel(towingVehicle) .. " towed=" .. vehLabel(towedVehicle)
|
||||
.. " attA=" .. tostring(attachmentA) .. " attB=" .. tostring(attachmentB))
|
||||
dumpTowState("performAttachTowBar pre towing", towingVehicle)
|
||||
dumpTowState("performAttachTowBar pre towed", towedVehicle)
|
||||
local frontLink = captureTowbarFrontLink(towingVehicle)
|
||||
originalPerformAttach(playerObj, towingVehicle, towedVehicle, attachmentA, attachmentB)
|
||||
dumpTowState("performAttachTowBar post-original towing", towingVehicle)
|
||||
dumpTowState("performAttachTowBar post-original towed", towedVehicle)
|
||||
restoreTowbarFrontLink(playerObj, frontLink)
|
||||
queueTowbarFrontLinkRestore(playerObj, frontLink, 12)
|
||||
queueTowbarFrontLinkRestore(playerObj, frontLink, 30)
|
||||
dumpTowState("performAttachTowBar post-restore towing", towingVehicle)
|
||||
dumpTowState("performAttachTowBar post-restore towed", towedVehicle)
|
||||
|
||||
setTowBarModelVisibleForVehicle(towedVehicle, true)
|
||||
refreshTowBarState(towingVehicle)
|
||||
refreshTowBarState(towedVehicle)
|
||||
if towingVehicle then
|
||||
refreshTowBarState(towingVehicle:getVehicleTowedBy())
|
||||
end
|
||||
if towedVehicle then
|
||||
refreshTowBarState(towedVehicle:getVehicleTowing())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local originalPerformDetach = TowBarMod.Hook and TowBarMod.Hook.performDeattachTowBar
|
||||
if originalPerformDetach then
|
||||
TowBarMod.Hook.performDeattachTowBar = function(playerObj, towingVehicle, towedVehicle)
|
||||
originalPerformDetach(playerObj, towingVehicle, towedVehicle)
|
||||
|
||||
setTowBarModelVisibleForVehicle(towedVehicle, false)
|
||||
refreshTowBarState(towingVehicle)
|
||||
refreshTowBarState(towedVehicle)
|
||||
if towingVehicle then
|
||||
refreshTowBarState(towingVehicle:getVehicleTowedBy())
|
||||
refreshTowBarState(towingVehicle:getVehicleTowing())
|
||||
end
|
||||
if towedVehicle then
|
||||
refreshTowBarState(towedVehicle:getVehicleTowedBy())
|
||||
refreshTowBarState(towedVehicle:getVehicleTowing())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Towbar UI only adds attach when fully unlinked; add attach option for linked vehicles too.
|
||||
if ISVehicleMenu and ISVehicleMenu.showRadialMenu and not TowBarMod.UI._landtrainShowRadialPatched then
|
||||
local originalShowRadialMenu = ISVehicleMenu.showRadialMenu
|
||||
ISVehicleMenu.showRadialMenu = function(playerObj)
|
||||
originalShowRadialMenu(playerObj)
|
||||
|
||||
if playerObj == nil or playerObj:getVehicle() then return end
|
||||
local vehicle = ISVehicleMenu.getVehicleToInteractWith(playerObj)
|
||||
if vehicle == nil then return end
|
||||
|
||||
local menu = getPlayerRadialMenu(playerObj:getPlayerNum())
|
||||
if menuHasTowbarAttachSlice(menu) then
|
||||
return
|
||||
end
|
||||
|
||||
if (vehicle:getVehicleTowing() or vehicle:getVehicleTowedBy()) then
|
||||
addLandtrainHookOptionToMenu(playerObj, vehicle)
|
||||
end
|
||||
end
|
||||
TowBarMod.UI._landtrainShowRadialPatched = true
|
||||
end
|
||||
|
||||
TowBarMod.Utils._landtrainUnlimitedChainsInstalled = true
|
||||
end
|
||||
|
||||
Events.OnGameBoot.Add(ensureTowAttachmentsForTrailers)
|
||||
Events.OnGameBoot.Add(installLandtrainTowbarPatch)
|
||||
Events.OnGameStart.Add(installLandtrainTowbarPatch)
|
||||
9
42.13/mod.info
Normal file
9
42.13/mod.info
Normal file
@@ -0,0 +1,9 @@
|
||||
name=Landtrain
|
||||
id=hrsys_landtrain
|
||||
require=\hrsys_towbars
|
||||
description=Extends Towbars to allow chained vehicle towing.
|
||||
author=Riggs0
|
||||
category=vehicle
|
||||
versionMin=42.13.0
|
||||
url=https://hudsonriggs.systems
|
||||
modversion=1.0.0
|
||||
Reference in New Issue
Block a user