585 lines
21 KiB
Lua
585 lines
21 KiB
Lua
if not TowBarMod then TowBarMod = {} end
|
|
if not TowBarMod.Hook then TowBarMod.Hook = {} end
|
|
|
|
local TowBarTowMass = 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 function isTowBarTowPair(towingVehicle, towedVehicle)
|
|
if not towingVehicle or not towedVehicle then return false end
|
|
|
|
local towingModData = towingVehicle:getModData()
|
|
local towedModData = towedVehicle:getModData()
|
|
if not towingModData or not towedModData then return false end
|
|
|
|
if towingModData["isTowingByTowBar"] and towedModData["isTowingByTowBar"] and towedModData["towed"] then
|
|
return true
|
|
end
|
|
|
|
-- Rejoin fallback: original towbar state on the towed vehicle is enough to reapply rigid spacing.
|
|
if towedModData.towBarOriginalScriptName ~= nil then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
local function getTowBarItem(playerObj)
|
|
if not playerObj then return nil end
|
|
local inventory = playerObj:getInventory()
|
|
if not inventory then return nil end
|
|
return inventory:getItemFromTypeRecurse("TowBar.TowBar")
|
|
end
|
|
|
|
local TowbarVariantSize = 24
|
|
local TowbarNormalStart = 0
|
|
local TowbarLargeStart = 24
|
|
local TowbarMaxIndex = TowbarVariantSize - 1
|
|
|
|
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()
|
|
if model then
|
|
return model:getScale()
|
|
end
|
|
return nil
|
|
end)
|
|
if ok and type(result) == "number" then
|
|
return result
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
local function shouldUseLargeTowbarModel(script)
|
|
local modelScale = getVehicleModelScale(script)
|
|
if modelScale == nil then
|
|
return false
|
|
end
|
|
|
|
local configuredThreshold = TowBarMod.Config and tonumber(TowBarMod.Config.largeTowbarModelScaleThreshold)
|
|
local threshold = configuredThreshold or 1.2
|
|
return modelScale < threshold
|
|
end
|
|
|
|
local function getTowbarModelSlot(script)
|
|
local chassisZ = script:getPhysicsChassisShape():z()
|
|
local index = 0
|
|
if chassisZ > 3.0 then
|
|
local halfZ = chassisZ / 2
|
|
index = math.floor((halfZ - 1.0) * 16 - 1)
|
|
end
|
|
index = math.max(0, math.min(TowbarMaxIndex, index))
|
|
|
|
local slotStart = shouldUseLargeTowbarModel(script) and TowbarLargeStart or TowbarNormalStart
|
|
return slotStart + index, index, slotStart
|
|
end
|
|
|
|
local function setTowBarModelVisible(vehicle, isVisible)
|
|
if not vehicle then return end
|
|
|
|
local part = vehicle:getPartById("towbar")
|
|
if part == nil then return end
|
|
|
|
for j = 0, (TowbarVariantSize * 2) - 1 do
|
|
part:setModelVisible("towbar" .. j, false)
|
|
end
|
|
|
|
if not isVisible then
|
|
vehicle:doDamageOverlay()
|
|
return
|
|
end
|
|
|
|
local script = vehicle:getScript()
|
|
if not script then
|
|
vehicle:doDamageOverlay()
|
|
return
|
|
end
|
|
|
|
local slot = getTowbarModelSlot(script)
|
|
part:setModelVisible("towbar" .. slot, true)
|
|
|
|
vehicle:doDamageOverlay()
|
|
end
|
|
|
|
local function resolveTowAttachmentsForPair(towingVehicle, towedVehicle, towedModData)
|
|
if not towingVehicle or not towedVehicle then
|
|
return nil, nil
|
|
end
|
|
|
|
local attachmentA = towingVehicle:getTowAttachmentSelf() or "trailer"
|
|
local attachmentB = towingVehicle:getTowAttachmentOther()
|
|
or (towedModData and towedModData["towBarChangedAttachmentId"])
|
|
or "trailerfront"
|
|
|
|
if not towingVehicle:canAttachTrailer(towedVehicle, attachmentA, attachmentB) then
|
|
if towingVehicle:canAttachTrailer(towedVehicle, "trailer", "trailerfront") then
|
|
attachmentA = "trailer"
|
|
attachmentB = "trailerfront"
|
|
elseif towingVehicle:canAttachTrailer(towedVehicle, "trailerfront", "trailer") then
|
|
attachmentA = "trailerfront"
|
|
attachmentB = "trailer"
|
|
end
|
|
end
|
|
|
|
return attachmentA, attachmentB
|
|
end
|
|
|
|
local function hasTowBarTowState(modData)
|
|
if not modData then
|
|
return false
|
|
end
|
|
|
|
if modData["isTowingByTowBar"] and modData["towed"] then
|
|
return true
|
|
end
|
|
|
|
-- Rejoin fallback: legacy saves may only have the original-script marker.
|
|
if modData.towBarOriginalScriptName ~= nil then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
local function isActiveTowBarTowedVehicle(vehicle, modData)
|
|
if not vehicle or not modData then
|
|
return false
|
|
end
|
|
|
|
if modData["isTowingByTowBar"] and modData["towed"] then
|
|
return true
|
|
end
|
|
|
|
-- Rejoin fallback: if the tow link exists, original-script marker is enough.
|
|
if vehicle:getVehicleTowedBy() and modData.towBarOriginalScriptName ~= nil then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
local function reattachTowBarPair(playerObj, towingVehicle, towedVehicle, requireDriver)
|
|
if not playerObj or not towingVehicle or not towedVehicle then
|
|
return false
|
|
end
|
|
if requireDriver and not towingVehicle:isDriver(playerObj) then
|
|
return false
|
|
end
|
|
|
|
local towingModData = towingVehicle:getModData()
|
|
local towedModData = towedVehicle:getModData()
|
|
if not towingModData or not towedModData then
|
|
return false
|
|
end
|
|
if requireDriver then
|
|
if not isTowBarTowPair(towingVehicle, towedVehicle) then
|
|
return false
|
|
end
|
|
else
|
|
if not isActiveTowBarTowedVehicle(towedVehicle, towedModData) then
|
|
return false
|
|
end
|
|
end
|
|
|
|
local attachmentA, attachmentB = resolveTowAttachmentsForPair(towingVehicle, towedVehicle, towedModData)
|
|
if not attachmentA or not attachmentB then
|
|
return false
|
|
end
|
|
|
|
local towingScript = towingVehicle:getScript()
|
|
local towedScript = towedVehicle:getScript()
|
|
if not towingScript or not towedScript then
|
|
return false
|
|
end
|
|
if not towingScript:getAttachmentById(attachmentA) or not towedScript:getAttachmentById(attachmentB) then
|
|
return false
|
|
end
|
|
|
|
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
|
|
|
|
towingModData["isTowingByTowBar"] = true
|
|
towedModData["isTowingByTowBar"] = true
|
|
towedModData["towed"] = true
|
|
towingVehicle:transmitModData()
|
|
towedVehicle:transmitModData()
|
|
|
|
setTowBarModelVisible(towedVehicle, true)
|
|
towedVehicle:setScriptName("notTowingA_Trailer")
|
|
|
|
local args = {
|
|
vehicleA = towingVehicle:getId(),
|
|
vehicleB = towedVehicle:getId(),
|
|
attachmentA = attachmentA,
|
|
attachmentB = attachmentB
|
|
}
|
|
sendClientCommand(playerObj, "vehicle", "attachTrailer", args)
|
|
ISTimedActionQueue.add(TowBarScheduleAction:new(playerObj, 10, TowBarMod.Hook.setVehiclePostAttach, towedVehicle))
|
|
return true
|
|
end
|
|
|
|
local function recoverTowBarVehicleAfterLoad(playerObj, vehicle, retriesLeft)
|
|
if not vehicle then return end
|
|
|
|
local modData = vehicle:getModData()
|
|
if not hasTowBarTowState(modData) then
|
|
return
|
|
end
|
|
|
|
local retries = tonumber(retriesLeft) or 0
|
|
local localPlayer = playerObj or getPlayer()
|
|
local towingVehicle = vehicle:getVehicleTowedBy()
|
|
|
|
if localPlayer and towingVehicle then
|
|
if reattachTowBarPair(localPlayer, towingVehicle, vehicle, false) then
|
|
return
|
|
end
|
|
end
|
|
|
|
if localPlayer and retries > 0 then
|
|
-- During world load, tow links can become available a few ticks later.
|
|
ISTimedActionQueue.add(TowBarScheduleAction:new(localPlayer, 10, recoverTowBarVehicleAfterLoad, vehicle, retries - 1))
|
|
return
|
|
end
|
|
|
|
-- Fallback: keep original post-attach restoration behavior.
|
|
setTowBarModelVisible(vehicle, true)
|
|
TowBarMod.Hook.setVehiclePostAttach(nil, vehicle)
|
|
end
|
|
|
|
function TowBarMod.Hook.setVehiclePostAttach(playerObj, towedVehicle, retriesLeft)
|
|
if not towedVehicle then return end
|
|
|
|
local towedModData = towedVehicle:getModData()
|
|
if not isActiveTowBarTowedVehicle(towedVehicle, towedModData) then return end
|
|
|
|
if towedModData and towedModData.towBarOriginalScriptName then
|
|
towedVehicle:setScriptName(towedModData.towBarOriginalScriptName)
|
|
end
|
|
|
|
local towingVehicle = towedVehicle:getVehicleTowedBy()
|
|
if towingVehicle then
|
|
local attachmentA, attachmentB = resolveTowAttachmentsForPair(towingVehicle, towedVehicle, towedModData)
|
|
if attachmentA and attachmentB then
|
|
TowBarMod.Utils.updateAttachmentsForRigidTow(towingVehicle, towedVehicle, attachmentA, attachmentB)
|
|
end
|
|
end
|
|
|
|
towedVehicle:setMass(TowBarTowMass)
|
|
towedVehicle:setBrakingForce(0)
|
|
towedVehicle:constraintChanged()
|
|
towedVehicle:updateTotalMass()
|
|
|
|
-- Re-show the towbar model after the script name has been restored.
|
|
-- setScriptName() resets model visibility, so we must set it again here.
|
|
setTowBarModelVisible(towedVehicle, true)
|
|
end
|
|
|
|
function TowBarMod.Hook.performAttachTowBar(playerObj, towingVehicle, towedVehicle, attachmentA, attachmentB)
|
|
if playerObj == nil or towingVehicle == nil or towedVehicle == nil then return end
|
|
if #(TowBarMod.Utils.getHookTypeVariants(towingVehicle, towedVehicle, true)) == 0 then return end
|
|
|
|
local towBarItem = getTowBarItem(playerObj)
|
|
if towBarItem ~= nil then
|
|
sendClientCommand(playerObj, "towbar", "consumeTowBar", { itemId = towBarItem:getID() })
|
|
end
|
|
playerObj:setPrimaryHandItem(nil)
|
|
|
|
TowBarMod.Utils.updateAttachmentsForRigidTow(towingVehicle, towedVehicle, attachmentA, attachmentB)
|
|
|
|
local towingModData = towingVehicle:getModData()
|
|
local towedModData = towedVehicle:getModData()
|
|
|
|
towedModData.towBarOriginalScriptName = towedVehicle:getScriptName()
|
|
towedModData.towBarOriginalMass = towedVehicle:getMass()
|
|
towedModData.towBarOriginalBrakingForce = towedVehicle:getBrakingForce()
|
|
|
|
towingModData["isTowingByTowBar"] = true
|
|
towedModData["isTowingByTowBar"] = true
|
|
towedModData["towed"] = true
|
|
towingVehicle:transmitModData()
|
|
towedVehicle:transmitModData()
|
|
|
|
setTowBarModelVisible(towedVehicle, true)
|
|
|
|
-- Match the known-good rigid tow path: fake trailer + vanilla attach command.
|
|
towedVehicle:setScriptName("notTowingA_Trailer")
|
|
|
|
local args = {
|
|
vehicleA = towingVehicle:getId(),
|
|
vehicleB = towedVehicle:getId(),
|
|
attachmentA = attachmentA,
|
|
attachmentB = attachmentB
|
|
}
|
|
sendClientCommand(playerObj, "vehicle", "attachTrailer", args)
|
|
ISTimedActionQueue.add(TowBarScheduleAction:new(playerObj, 10, TowBarMod.Hook.setVehiclePostAttach, towedVehicle))
|
|
end
|
|
|
|
function TowBarMod.Hook.performDetachTowBar(playerObj, towingVehicle, towedVehicle)
|
|
if playerObj == nil or towingVehicle == nil or towedVehicle == nil then return end
|
|
|
|
TowBarMod.Utils.updateAttachmentsOnDefaultValues(towingVehicle, towedVehicle)
|
|
|
|
local args = { towingVehicle = towingVehicle:getId(), vehicle = towedVehicle:getId() }
|
|
sendClientCommand(playerObj, "towbar", "detachTowBar", args)
|
|
|
|
local towedModData = towedVehicle:getModData()
|
|
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()
|
|
|
|
sendClientCommand(playerObj, "towbar", "giveTowBar", { equipPrimary = true })
|
|
|
|
local towingModData = towingVehicle:getModData()
|
|
towingModData["isTowingByTowBar"] = false
|
|
towedModData["isTowingByTowBar"] = false
|
|
towedModData["towed"] = false
|
|
towedModData.towBarOriginalScriptName = nil
|
|
towedModData.towBarOriginalMass = nil
|
|
towedModData.towBarOriginalBrakingForce = nil
|
|
towingVehicle:transmitModData()
|
|
towedVehicle:transmitModData()
|
|
|
|
TowBarMod.Hook.lastAutoReattachAtByVehicle[towingVehicle:getId()] = nil
|
|
|
|
setTowBarModelVisible(towedVehicle, false)
|
|
end
|
|
|
|
function TowBarMod.Hook.reattachTowBarFromDriverSeat(playerObj, towingVehicle)
|
|
if not playerObj or not towingVehicle then return end
|
|
|
|
local towedVehicle = towingVehicle:getVehicleTowing()
|
|
if not towedVehicle then return end
|
|
|
|
reattachTowBarPair(playerObj, towingVehicle, towedVehicle, true)
|
|
end
|
|
|
|
local function tryAutoReattachFromCharacter(character)
|
|
if not character or not instanceof(character, "IsoPlayer") or not character:isLocalPlayer() then return end
|
|
|
|
local playerObj = character
|
|
local nowHours = getGameTime() and getGameTime():getWorldAgeHours() or 0
|
|
local playerNum = playerObj:getPlayerNum()
|
|
local lastPlayerHours = TowBarMod.Hook.lastAutoReattachAtByPlayer[playerNum]
|
|
if lastPlayerHours and (nowHours - lastPlayerHours) < AutoReattachPlayerCooldownHours then
|
|
return
|
|
end
|
|
|
|
local towingVehicle = playerObj:getVehicle()
|
|
if not towingVehicle then return end
|
|
if not towingVehicle:isDriver(playerObj) then return end
|
|
local towedVehicle = towingVehicle:getVehicleTowing()
|
|
if not towedVehicle then return end
|
|
if not isTowBarTowPair(towingVehicle, towedVehicle) then return end
|
|
|
|
local vehicleId = towingVehicle:getId()
|
|
local lastHours = TowBarMod.Hook.lastAutoReattachAtByVehicle[vehicleId]
|
|
if lastHours and (nowHours - lastHours) < AutoReattachCooldownHours then
|
|
return
|
|
end
|
|
|
|
TowBarMod.Hook.lastAutoReattachAtByPlayer[playerNum] = nowHours
|
|
TowBarMod.Hook.lastAutoReattachAtByVehicle[vehicleId] = nowHours
|
|
TowBarMod.Hook.reattachTowBarFromDriverSeat(playerObj, towingVehicle)
|
|
end
|
|
|
|
function TowBarMod.Hook.OnEnterVehicle(character)
|
|
tryAutoReattachFromCharacter(character)
|
|
end
|
|
|
|
function TowBarMod.Hook.OnSwitchVehicleSeat(character)
|
|
tryAutoReattachFromCharacter(character)
|
|
end
|
|
|
|
function TowBarMod.Hook.attachByTowBarAction(playerObj, towingVehicle, towedVehicle)
|
|
if playerObj == nil or towingVehicle == nil or towedVehicle == nil then return end
|
|
|
|
local item = getTowBarItem(playerObj)
|
|
if item == nil then return end
|
|
if #(TowBarMod.Utils.getHookTypeVariants(towingVehicle, towedVehicle, true)) == 0 then return end
|
|
|
|
local hookPoint = towedVehicle:getAttachmentWorldPos("trailerfront", TowBarMod.Utils.tempVector1)
|
|
if hookPoint == nil then return end
|
|
ISTimedActionQueue.add(TowBarCustomPathFind:pathToLocationF(playerObj, hookPoint:x(), hookPoint:y(), hookPoint:z()))
|
|
|
|
if not playerObj:getInventory():contains("TowBar.TowBar") then
|
|
ISTimedActionQueue.add(ISInventoryTransferAction:new(playerObj, item, item:getContainer(), playerObj:getInventory(), nil))
|
|
end
|
|
|
|
local storePrim = playerObj:getPrimaryHandItem()
|
|
if storePrim == nil or storePrim ~= item then
|
|
ISTimedActionQueue.add(ISEquipWeaponAction:new(playerObj, item, 12, true))
|
|
end
|
|
|
|
ISTimedActionQueue.add(TowBarHookVehicle:new(playerObj, 300, TowBarMod.Config.lowLevelAnimation))
|
|
|
|
hookPoint = towingVehicle:getAttachmentWorldPos("trailer", TowBarMod.Utils.tempVector1)
|
|
if hookPoint == nil then return end
|
|
ISTimedActionQueue.add(TowBarCustomPathFind:pathToLocationF(playerObj, hookPoint:x(), hookPoint:y(), hookPoint:z()))
|
|
|
|
ISTimedActionQueue.add(TowBarHookVehicle:new(
|
|
playerObj,
|
|
100,
|
|
TowBarMod.Config.lowLevelAnimation,
|
|
TowBarMod.Hook.performAttachTowBar,
|
|
towingVehicle,
|
|
towedVehicle,
|
|
"trailer",
|
|
"trailerfront"
|
|
))
|
|
end
|
|
|
|
function TowBarMod.Hook.deattachTowBarAction(playerObj, vehicle)
|
|
local towingVehicle = vehicle
|
|
local towedVehicle = vehicle and vehicle:getVehicleTowing() or nil
|
|
if vehicle and vehicle:getVehicleTowedBy() then
|
|
towingVehicle = vehicle:getVehicleTowedBy()
|
|
towedVehicle = vehicle
|
|
end
|
|
if towingVehicle == nil or towedVehicle == nil then return end
|
|
|
|
local localPoint = towingVehicle:getAttachmentLocalPos(towingVehicle:getTowAttachmentSelf(), TowBarMod.Utils.tempVector1)
|
|
local shift = 0
|
|
if towingVehicle:getModData()["isChangedTowedAttachment"] then
|
|
shift = localPoint:z() > 0 and -1 or 1
|
|
end
|
|
local hookPoint = towingVehicle:getWorldPos(localPoint:x(), localPoint:y(), localPoint:z() + shift, TowBarMod.Utils.tempVector2)
|
|
if hookPoint == nil then return end
|
|
ISTimedActionQueue.add(TowBarCustomPathFind:pathToLocationF(playerObj, hookPoint:x(), hookPoint:y(), hookPoint:z()))
|
|
|
|
local storePrim = playerObj:getPrimaryHandItem()
|
|
if storePrim ~= nil then
|
|
ISTimedActionQueue.add(ISUnequipAction:new(playerObj, storePrim, 12))
|
|
end
|
|
|
|
ISTimedActionQueue.add(TowBarHookVehicle:new(playerObj, 100, TowBarMod.Config.lowLevelAnimation))
|
|
|
|
localPoint = towedVehicle:getAttachmentLocalPos(towedVehicle:getTowAttachmentSelf(), TowBarMod.Utils.tempVector1)
|
|
shift = 0
|
|
if towedVehicle:getModData()["isChangedTowedAttachment"] then
|
|
shift = localPoint:z() > 0 and -1 or 1
|
|
end
|
|
hookPoint = towedVehicle:getWorldPos(localPoint:x(), localPoint:y(), localPoint:z() + shift, TowBarMod.Utils.tempVector2)
|
|
if hookPoint == nil then return end
|
|
ISTimedActionQueue.add(TowBarCustomPathFind:pathToLocationF(playerObj, hookPoint:x(), hookPoint:y(), hookPoint:z()))
|
|
|
|
ISTimedActionQueue.add(TowBarHookVehicle:new(
|
|
playerObj,
|
|
300,
|
|
TowBarMod.Config.lowLevelAnimation,
|
|
TowBarMod.Hook.performDetachTowBar,
|
|
towingVehicle,
|
|
towedVehicle
|
|
))
|
|
end
|
|
|
|
function TowBarMod.Hook.OnSpawnVehicle(vehicle)
|
|
recoverTowBarVehicleAfterLoad(nil, vehicle, 6)
|
|
end
|
|
|
|
function TowBarMod.Hook.OnGameStart()
|
|
local cell = getCell()
|
|
if not cell then return end
|
|
|
|
local vehicles = cell:getVehicles()
|
|
if not vehicles then return end
|
|
|
|
local playerObj = getPlayer()
|
|
for i = 0, vehicles:size() - 1 do
|
|
recoverTowBarVehicleAfterLoad(playerObj, vehicles:get(i), 6)
|
|
end
|
|
end
|
|
|
|
---------------------------------------------------------------------------
|
|
--- Dev / debug helpers
|
|
---------------------------------------------------------------------------
|
|
|
|
function TowBarMod.Hook.devShowAllTowbarModels(playerObj, vehicle)
|
|
if not vehicle then return end
|
|
local part = vehicle:getPartById("towbar")
|
|
if part == nil then
|
|
print("[TowBar DEV] No 'towbar' part found on vehicle " .. tostring(vehicle:getScriptName()))
|
|
return
|
|
end
|
|
local script = vehicle:getScript()
|
|
local chassisZ = script and script:getPhysicsChassisShape():z() or 0
|
|
local halfZ = chassisZ / 2
|
|
local modelScale = script and getVehicleModelScale(script) or nil
|
|
local slot, index, slotStart = 0, 0, TowbarNormalStart
|
|
if script then
|
|
slot, index, slotStart = getTowbarModelSlot(script)
|
|
end
|
|
print("[TowBar DEV] Vehicle: " .. tostring(vehicle:getScriptName()))
|
|
print("[TowBar DEV] chassisShape.z = " .. tostring(chassisZ) .. ", half = " .. tostring(halfZ))
|
|
print("[TowBar DEV] modelScale = " .. tostring(modelScale) .. ", largeModel = " .. tostring(slotStart == TowbarLargeStart))
|
|
print("[TowBar DEV] Formula picks index = " .. tostring(index) .. " (towbar" .. tostring(slot) .. " at Z offset " .. tostring(1.0 + index * 0.1) .. ") [chassisZ > 3.0: " .. tostring(chassisZ > 3.0) .. "]")
|
|
print("[TowBar DEV] Showing ALL 48 towbar models (towbar0..towbar47)")
|
|
for j = 0, (TowbarVariantSize * 2) - 1 do
|
|
part:setModelVisible("towbar" .. j, true)
|
|
end
|
|
vehicle:doDamageOverlay()
|
|
end
|
|
|
|
function TowBarMod.Hook.devHideAllTowbarModels(playerObj, vehicle)
|
|
if not vehicle then return end
|
|
local part = vehicle:getPartById("towbar")
|
|
if part == nil then
|
|
print("[TowBar DEV] No 'towbar' part found on vehicle " .. tostring(vehicle:getScriptName()))
|
|
return
|
|
end
|
|
print("[TowBar DEV] Hiding ALL towbar models on " .. tostring(vehicle:getScriptName()))
|
|
for j = 0, (TowbarVariantSize * 2) - 1 do
|
|
part:setModelVisible("towbar" .. j, false)
|
|
end
|
|
vehicle:doDamageOverlay()
|
|
end
|
|
|
|
function TowBarMod.Hook.devShowSingleTowbar(playerObj, vehicle, index)
|
|
if not vehicle then return end
|
|
local part = vehicle:getPartById("towbar")
|
|
if part == nil then
|
|
print("[TowBar DEV] No 'towbar' part found on vehicle " .. tostring(vehicle:getScriptName()))
|
|
return
|
|
end
|
|
for j = 0, (TowbarVariantSize * 2) - 1 do
|
|
part:setModelVisible("towbar" .. j, false)
|
|
end
|
|
print("[TowBar DEV] Showing only towbar" .. tostring(index) .. " (Z offset " .. tostring(1.0 + index * 0.1) .. ") on " .. tostring(vehicle:getScriptName()))
|
|
part:setModelVisible("towbar" .. index, true)
|
|
vehicle:doDamageOverlay()
|
|
end
|
|
|
|
Events.OnSpawnVehicleEnd.Add(TowBarMod.Hook.OnSpawnVehicle)
|
|
if Events.OnGameStart then
|
|
Events.OnGameStart.Add(TowBarMod.Hook.OnGameStart)
|
|
end
|
|
Events.OnEnterVehicle.Add(TowBarMod.Hook.OnEnterVehicle)
|
|
Events.OnSwitchVehicleSeat.Add(TowBarMod.Hook.OnSwitchVehicleSeat)
|