Finished
This commit is contained in:
@@ -63,14 +63,177 @@ local function setTowBarModelVisible(vehicle, isVisible)
|
||||
vehicle:doDamageOverlay()
|
||||
end
|
||||
|
||||
function TowBarMod.Hook.setVehiclePostAttach(playerObj, towedVehicle)
|
||||
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()
|
||||
@@ -157,55 +320,11 @@ end
|
||||
|
||||
function TowBarMod.Hook.reattachTowBarFromDriverSeat(playerObj, towingVehicle)
|
||||
if not playerObj or not towingVehicle then return end
|
||||
if not towingVehicle:isDriver(playerObj) then return end
|
||||
|
||||
local towedVehicle = towingVehicle:getVehicleTowing()
|
||||
if not towedVehicle then return end
|
||||
|
||||
local towingModData = towingVehicle:getModData()
|
||||
local towedModData = towedVehicle:getModData()
|
||||
if not towingModData or not towedModData then return end
|
||||
if not isTowBarTowPair(towingVehicle, towedVehicle) then return end
|
||||
|
||||
local attachmentA = towingVehicle:getTowAttachmentSelf() or "trailer"
|
||||
local attachmentB = towingVehicle:getTowAttachmentOther() 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
|
||||
|
||||
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))
|
||||
reattachTowBarPair(playerObj, towingVehicle, towedVehicle, true)
|
||||
end
|
||||
|
||||
local function tryAutoReattachFromCharacter(character)
|
||||
@@ -328,25 +447,25 @@ function TowBarMod.Hook.deattachTowBarAction(playerObj, vehicle)
|
||||
end
|
||||
|
||||
function TowBarMod.Hook.OnSpawnVehicle(vehicle)
|
||||
if not vehicle then return end
|
||||
recoverTowBarVehicleAfterLoad(nil, vehicle, 6)
|
||||
end
|
||||
|
||||
local modData = vehicle:getModData()
|
||||
if not (modData and modData["isTowingByTowBar"] and modData["towed"]) then
|
||||
return
|
||||
end
|
||||
function TowBarMod.Hook.OnGameStart()
|
||||
local cell = getCell()
|
||||
if not cell then return end
|
||||
|
||||
-- Keep behavior consistent after load/rejoin for active towbar tows.
|
||||
vehicle:setScriptName("notTowingA_Trailer")
|
||||
local vehicles = cell:getVehicles()
|
||||
if not vehicles then return end
|
||||
|
||||
local playerObj = getPlayer()
|
||||
if playerObj then
|
||||
ISTimedActionQueue.add(TowBarScheduleAction:new(playerObj, 10, TowBarMod.Hook.setVehiclePostAttach, vehicle))
|
||||
else
|
||||
TowBarMod.Hook.setVehiclePostAttach(nil, vehicle)
|
||||
for i = 0, vehicles:size() - 1 do
|
||||
recoverTowBarVehicleAfterLoad(playerObj, vehicles:get(i), 6)
|
||||
end
|
||||
setTowBarModelVisible(vehicle, true)
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user