Working 42.20 MP
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
if not TowBarMod then TowBarMod = {} end
|
||||
if not TowBarMod.Hook then TowBarMod.Hook = {} end
|
||||
|
||||
require("TowBar/VehicleCompatibility")
|
||||
|
||||
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
|
||||
|
||||
@@ -44,7 +42,7 @@ local function applyFreeRollingTowState(vehicle)
|
||||
modData.towBarOriginalBrakingForce = vehicle:getBrakingForce()
|
||||
end
|
||||
storeOriginalVehicleCall(vehicle, modData, "towBarOriginalParkingBrakeOn", "isParkingBrakeOn")
|
||||
storeOriginalVehicleCall(vehicle, modData, "towBarOriginalParkingBrake", "isParkingBrake")
|
||||
storeOriginalVehicleCall(vehicle, modData, "towBarOriginalParkingBrake", "getParkingBrake")
|
||||
storeOriginalVehicleCall(vehicle, modData, "towBarOriginalHandbrake", "isHandbrake")
|
||||
|
||||
local configuredTowMass = TowBarMod.Config and tonumber(TowBarMod.Config.towedVehicleRollingMass)
|
||||
@@ -61,10 +59,13 @@ local function applyFreeRollingTowState(vehicle)
|
||||
tryVehicleCall(vehicle, "setHandbrake", false)
|
||||
end
|
||||
|
||||
vehicle:constraintChanged()
|
||||
vehicle:updateTotalMass()
|
||||
-- Match the working wrecker path. Recalculating total mass here would
|
||||
-- immediately replace the temporary towing mass in multiplayer; creating
|
||||
-- the rigid constraint below notifies Bullet of the changed vehicle state.
|
||||
end
|
||||
|
||||
TowBarMod.Hook.applyFreeRollingTowState = applyFreeRollingTowState
|
||||
|
||||
local function restoreFreeRollingTowState(vehicle, modData)
|
||||
if not vehicle or not modData then return end
|
||||
|
||||
@@ -88,25 +89,6 @@ local function restoreFreeRollingTowState(vehicle, modData)
|
||||
vehicle:updateTotalMass()
|
||||
end
|
||||
|
||||
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()
|
||||
@@ -116,15 +98,8 @@ end
|
||||
|
||||
local function sendTowAttachCommand(playerObj, args)
|
||||
if not playerObj or not args then return end
|
||||
|
||||
-- MP-safe/server-authoritative attach path (Landtrain style).
|
||||
if isClient() and isMultiplayer() then
|
||||
sendClientCommand(playerObj, "towbar", "attachTowBar", args)
|
||||
return
|
||||
end
|
||||
|
||||
-- Keep vanilla attach path for SP/local behavior.
|
||||
sendClientCommand(playerObj, "vehicle", "attachTrailer", args)
|
||||
-- SP and MP now share the same authoritative attach lifecycle.
|
||||
sendClientCommand(playerObj, "towbar", "attachTowBar", args)
|
||||
end
|
||||
|
||||
local TowbarVariantSize = 24
|
||||
@@ -138,6 +113,8 @@ 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
|
||||
@@ -178,16 +155,87 @@ local function getTowbarModelSlot(script)
|
||||
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)
|
||||
if ok and type(result) == "number" then return result end
|
||||
|
||||
return 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
|
||||
if index == nil then
|
||||
local offset = TowBarMod.Config and tonumber(TowBarMod.Config.smallScaleTowbarIndexOffset) or 2
|
||||
index = math.max(0, math.min(TowbarMaxIndex, offset))
|
||||
end
|
||||
end
|
||||
return index, useNormalPart
|
||||
end
|
||||
|
||||
local function setTowBarModelVisible(vehicle, isVisible)
|
||||
if not vehicle then return end
|
||||
|
||||
local normalPart = vehicle:getPartById("towbar")
|
||||
local largePart = vehicle:getPartById("towbarLarge")
|
||||
if normalPart == nil and largePart == nil then return end
|
||||
local ki5Part = vehicle:getPartById("towbarKI5")
|
||||
if normalPart == nil and largePart == nil and ki5Part == nil then return end
|
||||
|
||||
for j = 0, TowbarVariantSize - 1 do
|
||||
if normalPart then normalPart:setModelVisible("towbar" .. j, false) end
|
||||
if largePart then largePart:setModelVisible("towbar" .. j, false) end
|
||||
if ki5Part then ki5Part:setModelVisible("towbar" .. j, false) end
|
||||
end
|
||||
|
||||
if not isVisible then
|
||||
@@ -201,8 +249,16 @@ local function setTowBarModelVisible(vehicle, isVisible)
|
||||
return
|
||||
end
|
||||
|
||||
local index = getTowbarModelSlot(script)
|
||||
local part = normalPart
|
||||
local isKi5 = TowBarMod.Compatibility.isKi5Vehicle(vehicle)
|
||||
local index, useNormalPart
|
||||
if isKi5 then
|
||||
index = getTowbarModelSlot(script)
|
||||
else
|
||||
index, useNormalPart = getLegacyTowbarModelSlot(script)
|
||||
end
|
||||
|
||||
local part = isKi5 and ki5Part or (useNormalPart and normalPart or largePart)
|
||||
if part == nil and not isKi5 then part = normalPart or largePart end
|
||||
if part and index ~= nil then
|
||||
part:setModelVisible("towbar" .. index, true)
|
||||
end
|
||||
@@ -259,23 +315,6 @@ local function resolveTowAttachmentsForPair(towingVehicle, towedVehicle, towedMo
|
||||
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
|
||||
@@ -293,142 +332,22 @@ local function isActiveTowBarTowedVehicle(vehicle, modData)
|
||||
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()
|
||||
applyFreeRollingTowState(towedVehicle)
|
||||
|
||||
towingModData["isTowingByTowBar"] = true
|
||||
towingModData["towed"] = false
|
||||
towingModData["towBarTowedVehicleId"] = towedVehicle:getId()
|
||||
towingModData["towBarTowingVehicleId"] = nil
|
||||
towingModData["towBarExpectedAttachment"] = attachmentA
|
||||
towedModData["isTowingByTowBar"] = true
|
||||
towedModData["towed"] = true
|
||||
towedModData["towBarTowedVehicleId"] = nil
|
||||
towedModData["towBarTowingVehicleId"] = towingVehicle:getId()
|
||||
towedModData["towBarExpectedAttachment"] = attachmentB
|
||||
towingVehicle:transmitModData()
|
||||
towedVehicle:transmitModData()
|
||||
|
||||
TowBarMod.Hook.setVehicleScriptWithTowBarHidden(towedVehicle, "notTowingA_Trailer")
|
||||
|
||||
local args = {
|
||||
vehicleA = towingVehicle:getId(),
|
||||
vehicleB = towedVehicle:getId(),
|
||||
attachmentA = attachmentA,
|
||||
attachmentB = attachmentB
|
||||
}
|
||||
sendTowAttachCommand(playerObj, args)
|
||||
ISTimedActionQueue.add(TowBarScheduleAction:new(playerObj, 10, TowBarMod.Hook.setVehiclePostAttach, towedVehicle))
|
||||
return true
|
||||
end
|
||||
|
||||
local function reattachTowBarPairAfterCleanDetach(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 detachArgs = {
|
||||
towingVehicle = towingVehicle:getId(),
|
||||
vehicle = towedVehicle:getId()
|
||||
}
|
||||
sendClientCommand(playerObj, "towbar", "detachTowBar", detachArgs)
|
||||
|
||||
-- World load/spawn can restore constraints in a bad state. Reattach one
|
||||
-- short tick later so the detach is fully applied first.
|
||||
ISTimedActionQueue.add(TowBarScheduleAction:new(
|
||||
playerObj,
|
||||
1,
|
||||
reattachTowBarPair,
|
||||
towingVehicle,
|
||||
towedVehicle,
|
||||
requireDriver
|
||||
))
|
||||
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 towingVehicle then
|
||||
-- Apply rigid spacing as soon as the tow link exists to avoid a visible
|
||||
-- bumper-to-bumper snap. Server/SP reconciliation owns recovery and
|
||||
-- never refunds or consumes an item while loading a save.
|
||||
TowBarMod.Hook.setVehiclePostAttach(nil, vehicle)
|
||||
return
|
||||
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
|
||||
|
||||
-- Leave reciprocal saved state untouched. The authoritative audit will
|
||||
-- reconnect it when both vehicle chunks are loaded.
|
||||
end
|
||||
|
||||
function TowBarMod.Hook.setVehiclePostAttach(playerObj, towedVehicle, retriesLeft)
|
||||
function TowBarMod.Hook.setVehiclePostAttach(playerObj, towedVehicle, knownTowingVehicle)
|
||||
if not towedVehicle then return end
|
||||
|
||||
local towedModData = towedVehicle:getModData()
|
||||
if not isActiveTowBarTowedVehicle(towedVehicle, towedModData) then return end
|
||||
|
||||
if towedModData and towedModData.towBarOriginalScriptName then
|
||||
local towingVehicle = knownTowingVehicle or towedVehicle:getVehicleTowedBy()
|
||||
if not towingVehicle then return end
|
||||
|
||||
-- The rigid primitive passes the authoritative towing vehicle because MP
|
||||
-- reciprocal getters can lag behind accepted local constraint submission.
|
||||
if towedModData and towedModData.towBarOriginalScriptName
|
||||
and towedVehicle:getScriptName() ~= towedModData.towBarOriginalScriptName then
|
||||
TowBarMod.Hook.setVehicleScriptWithTowBarHidden(towedVehicle, towedModData.towBarOriginalScriptName)
|
||||
end
|
||||
|
||||
local towingVehicle = towedVehicle:getVehicleTowedBy()
|
||||
if towingVehicle then
|
||||
local attachmentA, attachmentB = resolveTowAttachmentsForPair(towingVehicle, towedVehicle, towedModData)
|
||||
if attachmentA and attachmentB then
|
||||
@@ -443,7 +362,6 @@ function TowBarMod.Hook.setVehiclePostAttach(playerObj, towedVehicle, retriesLef
|
||||
towingVehicle:transmitModData()
|
||||
towedVehicle:transmitModData()
|
||||
end
|
||||
TowBarMod.Utils.updateAttachmentsForRigidTow(towingVehicle, towedVehicle, attachmentA, attachmentB)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -459,35 +377,9 @@ function TowBarMod.Hook.performAttachTowBar(playerObj, towingVehicle, towedVehic
|
||||
if #(TowBarMod.Utils.getHookTypeVariants(towingVehicle, towedVehicle, true)) == 0 then return end
|
||||
|
||||
local towBarItem = getTowBarItem(playerObj)
|
||||
if towBarItem ~= nil and not (isClient() and isMultiplayer()) then
|
||||
sendClientCommand(playerObj, "towbar", "consumeTowBar", { itemId = towBarItem:getID() })
|
||||
end
|
||||
if towBarItem == nil then return end
|
||||
playerObj:setPrimaryHandItem(nil)
|
||||
|
||||
TowBarMod.Utils.updateAttachmentsForRigidTow(towingVehicle, towedVehicle, attachmentA, attachmentB)
|
||||
|
||||
local towingModData = towingVehicle:getModData()
|
||||
local towedModData = towedVehicle:getModData()
|
||||
|
||||
towedModData.towBarOriginalScriptName = towedVehicle:getScriptName()
|
||||
applyFreeRollingTowState(towedVehicle)
|
||||
|
||||
towingModData["isTowingByTowBar"] = true
|
||||
towingModData["towed"] = false
|
||||
towingModData["towBarTowedVehicleId"] = towedVehicle:getId()
|
||||
towingModData["towBarTowingVehicleId"] = nil
|
||||
towingModData["towBarExpectedAttachment"] = attachmentA
|
||||
towedModData["isTowingByTowBar"] = true
|
||||
towedModData["towed"] = true
|
||||
towedModData["towBarTowedVehicleId"] = nil
|
||||
towedModData["towBarTowingVehicleId"] = towingVehicle:getId()
|
||||
towedModData["towBarExpectedAttachment"] = attachmentB
|
||||
towingVehicle:transmitModData()
|
||||
towedVehicle:transmitModData()
|
||||
|
||||
-- Match the known-good rigid tow path: fake trailer + vanilla attach command.
|
||||
TowBarMod.Hook.setVehicleScriptWithTowBarHidden(towedVehicle, "notTowingA_Trailer")
|
||||
|
||||
local args = {
|
||||
vehicleA = towingVehicle:getId(),
|
||||
vehicleB = towedVehicle:getId(),
|
||||
@@ -496,7 +388,6 @@ function TowBarMod.Hook.performAttachTowBar(playerObj, towingVehicle, towedVehic
|
||||
itemId = towBarItem and towBarItem:getID() or nil
|
||||
}
|
||||
sendTowAttachCommand(playerObj, args)
|
||||
ISTimedActionQueue.add(TowBarScheduleAction:new(playerObj, 10, TowBarMod.Hook.setVehiclePostAttach, towedVehicle))
|
||||
end
|
||||
|
||||
function TowBarMod.Hook.cleanupDetachedTowBar(towingVehicle, towedVehicle)
|
||||
@@ -538,7 +429,6 @@ function TowBarMod.Hook.cleanupDetachedTowBar(towingVehicle, towedVehicle)
|
||||
towingVehicle:transmitModData()
|
||||
towedVehicle:transmitModData()
|
||||
|
||||
TowBarMod.Hook.lastAutoReattachAtByVehicle[towingVehicle:getId()] = nil
|
||||
setTowBarModelVisible(towedVehicle, false)
|
||||
end
|
||||
|
||||
@@ -547,46 +437,6 @@ function TowBarMod.Hook.performDetachTowBar(playerObj, towingVehicle, towedVehic
|
||||
|
||||
local args = { towingVehicle = towingVehicle:getId(), vehicle = towedVehicle:getId() }
|
||||
sendClientCommand(playerObj, "towbar", "detachTowBar", args)
|
||||
|
||||
TowBarMod.Hook.cleanupDetachedTowBar(towingVehicle, towedVehicle)
|
||||
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
|
||||
|
||||
local function forEachCollectionItem(collection, callback)
|
||||
@@ -634,14 +484,6 @@ local function keepTowBarVehiclesFreeRolling()
|
||||
end)
|
||||
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
|
||||
|
||||
@@ -725,20 +567,11 @@ function TowBarMod.Hook.deattachTowBarAction(playerObj, vehicle)
|
||||
end
|
||||
|
||||
function TowBarMod.Hook.OnSpawnVehicle(vehicle)
|
||||
recoverTowBarVehicleAfterLoad(nil, vehicle, 6)
|
||||
-- Server persistence snapshots own attach recovery after vehicle streaming.
|
||||
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()
|
||||
forEachCollectionItem(vehicles, function(vehicle)
|
||||
recoverTowBarVehicleAfterLoad(playerObj, vehicle, 6)
|
||||
end)
|
||||
-- Server persistence broadcasts the same attach snapshot used by a fresh pair.
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
@@ -749,8 +582,9 @@ function TowBarMod.Hook.devShowAllTowbarModels(playerObj, vehicle)
|
||||
if not vehicle then return end
|
||||
local normalPart = vehicle:getPartById("towbar")
|
||||
local largePart = vehicle:getPartById("towbarLarge")
|
||||
if normalPart == nil and largePart == nil then
|
||||
print("[TowBar DEV] No 'towbar' or 'towbarLarge' part found on vehicle " .. tostring(vehicle:getScriptName()))
|
||||
local ki5Part = vehicle:getPartById("towbarKI5")
|
||||
if normalPart == nil and largePart == nil and ki5Part == nil then
|
||||
print("[TowBar DEV] No towbar model part found on vehicle " .. tostring(vehicle:getScriptName()))
|
||||
return
|
||||
end
|
||||
local script = vehicle:getScript()
|
||||
@@ -765,10 +599,11 @@ function TowBarMod.Hook.devShowAllTowbarModels(playerObj, vehicle)
|
||||
print("[TowBar DEV] chassisShape.z = " .. tostring(chassisZ) .. ", half = " .. tostring(halfZ))
|
||||
print("[TowBar DEV] frontEdgeZ = " .. tostring(script and getTowbarFrontEdgeZ(script) or nil) .. ", part = " .. selectedPart)
|
||||
print("[TowBar DEV] Formula picks index = " .. tostring(index) .. " (towbar" .. tostring(index) .. " at Z offset " .. tostring(1.0 + index * 0.1) .. ")")
|
||||
print("[TowBar DEV] Showing towbar0..towbar23 on both parts")
|
||||
print("[TowBar DEV] Showing towbar0..towbar23 on all parts")
|
||||
for j = 0, TowbarVariantSize - 1 do
|
||||
if normalPart then normalPart:setModelVisible("towbar" .. j, true) end
|
||||
if largePart then largePart:setModelVisible("towbar" .. j, true) end
|
||||
if ki5Part then ki5Part:setModelVisible("towbar" .. j, true) end
|
||||
end
|
||||
vehicle:doDamageOverlay()
|
||||
end
|
||||
@@ -777,14 +612,16 @@ function TowBarMod.Hook.devHideAllTowbarModels(playerObj, vehicle)
|
||||
if not vehicle then return end
|
||||
local normalPart = vehicle:getPartById("towbar")
|
||||
local largePart = vehicle:getPartById("towbarLarge")
|
||||
if normalPart == nil and largePart == nil then
|
||||
print("[TowBar DEV] No 'towbar' or 'towbarLarge' part found on vehicle " .. tostring(vehicle:getScriptName()))
|
||||
local ki5Part = vehicle:getPartById("towbarKI5")
|
||||
if normalPart == nil and largePart == nil and ki5Part == nil then
|
||||
print("[TowBar DEV] No towbar model part found on vehicle " .. tostring(vehicle:getScriptName()))
|
||||
return
|
||||
end
|
||||
print("[TowBar DEV] Hiding ALL towbar models on " .. tostring(vehicle:getScriptName()))
|
||||
for j = 0, TowbarVariantSize - 1 do
|
||||
if normalPart then normalPart:setModelVisible("towbar" .. j, false) end
|
||||
if largePart then largePart:setModelVisible("towbar" .. j, false) end
|
||||
if ki5Part then ki5Part:setModelVisible("towbar" .. j, false) end
|
||||
end
|
||||
vehicle:doDamageOverlay()
|
||||
end
|
||||
@@ -793,24 +630,32 @@ function TowBarMod.Hook.devShowSingleTowbar(playerObj, vehicle, index)
|
||||
if not vehicle then return end
|
||||
local normalPart = vehicle:getPartById("towbar")
|
||||
local largePart = vehicle:getPartById("towbarLarge")
|
||||
if normalPart == nil and largePart == nil then
|
||||
print("[TowBar DEV] No 'towbar' or 'towbarLarge' part found on vehicle " .. tostring(vehicle:getScriptName()))
|
||||
local ki5Part = vehicle:getPartById("towbarKI5")
|
||||
if normalPart == nil and largePart == nil and ki5Part == nil then
|
||||
print("[TowBar DEV] No towbar model part found on vehicle " .. tostring(vehicle:getScriptName()))
|
||||
return
|
||||
end
|
||||
|
||||
local localIndex = math.max(0, math.min(TowbarMaxIndex, index % TowbarVariantSize))
|
||||
local useLargePart = index >= TowbarVariantSize
|
||||
local selectedPartId = "towbar"
|
||||
if index >= TowbarVariantSize * 2 then
|
||||
selectedPartId = "towbarKI5"
|
||||
elseif index >= TowbarVariantSize then
|
||||
selectedPartId = "towbarLarge"
|
||||
end
|
||||
|
||||
for j = 0, TowbarVariantSize - 1 do
|
||||
if normalPart then normalPart:setModelVisible("towbar" .. j, false) end
|
||||
if largePart then largePart:setModelVisible("towbar" .. j, false) end
|
||||
if ki5Part then ki5Part:setModelVisible("towbar" .. j, false) end
|
||||
end
|
||||
|
||||
local part = useLargePart and largePart or normalPart
|
||||
local part = selectedPartId == "towbarKI5" and ki5Part
|
||||
or (selectedPartId == "towbarLarge" and largePart or normalPart)
|
||||
if part == nil then
|
||||
part = normalPart or largePart
|
||||
part = normalPart or largePart or ki5Part
|
||||
end
|
||||
print("[TowBar DEV] Showing only towbar" .. tostring(localIndex) .. " on part " .. tostring(useLargePart and "towbarLarge" or "towbar") .. " (Z offset " .. tostring(1.0 + localIndex * 0.1) .. ") on " .. tostring(vehicle:getScriptName()))
|
||||
print("[TowBar DEV] Showing only towbar" .. tostring(localIndex) .. " on part " .. selectedPartId .. " (Z offset " .. tostring(1.0 + localIndex * 0.1) .. ") on " .. tostring(vehicle:getScriptName()))
|
||||
if part then
|
||||
part:setModelVisible("towbar" .. localIndex, true)
|
||||
end
|
||||
@@ -821,6 +666,4 @@ 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)
|
||||
Events.OnTick.Add(keepTowBarVehiclesFreeRolling)
|
||||
|
||||
Reference in New Issue
Block a user