42.20 and Towtrucks
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
if isServer() then return end
|
||||
if not TowBarMod then TowBarMod = {} end
|
||||
TowBarMod.WreckerSync = TowBarMod.WreckerSync or {}
|
||||
|
||||
local Sync = TowBarMod.WreckerSync
|
||||
Sync.appliedLevels = Sync.appliedLevels or {}
|
||||
|
||||
local function pairKeyContainsVehicle(key, vehicleId)
|
||||
local id = tostring(vehicleId)
|
||||
return string.sub(key, 1, #id + 1) == id .. ":"
|
||||
or string.sub(key, -(#id + 1)) == ":" .. id
|
||||
end
|
||||
|
||||
local function clearAppliedLevelForVehicle(vehicle)
|
||||
if not vehicle then return end
|
||||
local vehicleId = vehicle:getId()
|
||||
for key in pairs(Sync.appliedLevels) do
|
||||
if pairKeyContainsVehicle(key, vehicleId) then
|
||||
Sync.appliedLevels[key] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function pairKey(wrecker, target)
|
||||
return tostring(wrecker:getId()) .. ":" .. tostring(target:getId())
|
||||
end
|
||||
|
||||
local function callVehicle(vehicle, methodName, value)
|
||||
local method = vehicle and vehicle[methodName] or nil
|
||||
if not method then return false, nil end
|
||||
return pcall(function()
|
||||
if value ~= nil then return method(vehicle, value) end
|
||||
return method(vehicle)
|
||||
end)
|
||||
end
|
||||
|
||||
local function applyFreeRollingState(vehicle)
|
||||
local md = vehicle and vehicle:getModData() or nil
|
||||
if not md then return end
|
||||
local capturedOriginal = false
|
||||
if md.wreckerOriginalMass == nil then
|
||||
md.wreckerOriginalMass = vehicle:getMass()
|
||||
capturedOriginal = true
|
||||
end
|
||||
if md.wreckerOriginalBrakingForce == nil then
|
||||
md.wreckerOriginalBrakingForce = vehicle:getBrakingForce()
|
||||
capturedOriginal = true
|
||||
end
|
||||
if md.wreckerOriginalParkingBrakeOn == nil then
|
||||
local ok, value = callVehicle(vehicle, "isParkingBrakeOn")
|
||||
if ok then
|
||||
md.wreckerOriginalParkingBrakeOn = value
|
||||
capturedOriginal = true
|
||||
end
|
||||
end
|
||||
if md.wreckerOriginalParkingBrake == nil then
|
||||
local ok, value = callVehicle(vehicle, "getParkingBrake")
|
||||
if ok then
|
||||
md.wreckerOriginalParkingBrake = value
|
||||
capturedOriginal = true
|
||||
end
|
||||
end
|
||||
if md.wreckerOriginalHandbrake == nil then
|
||||
local ok, value = callVehicle(vehicle, "isHandbrake")
|
||||
if ok then
|
||||
md.wreckerOriginalHandbrake = value
|
||||
capturedOriginal = true
|
||||
end
|
||||
end
|
||||
vehicle:setMass(200)
|
||||
vehicle:setBrakingForce(0)
|
||||
if md.wreckerOriginalParkingBrakeOn ~= nil then callVehicle(vehicle, "setParkingBrakeOn", false) end
|
||||
if md.wreckerOriginalParkingBrake ~= nil then callVehicle(vehicle, "setParkingBrake", false) end
|
||||
if md.wreckerOriginalHandbrake ~= nil then callVehicle(vehicle, "setHandbrake", false) end
|
||||
if capturedOriginal then vehicle:transmitModData() end
|
||||
end
|
||||
|
||||
local function restoreFreeRollingState(vehicle)
|
||||
local md = vehicle and vehicle:getModData() or nil
|
||||
if not md then return end
|
||||
if md.wreckerOriginalMass ~= nil then vehicle:setMass(md.wreckerOriginalMass) end
|
||||
if md.wreckerOriginalBrakingForce ~= nil then vehicle:setBrakingForce(md.wreckerOriginalBrakingForce) end
|
||||
if md.wreckerOriginalParkingBrakeOn ~= nil then
|
||||
callVehicle(vehicle, "setParkingBrakeOn", md.wreckerOriginalParkingBrakeOn)
|
||||
end
|
||||
if md.wreckerOriginalParkingBrake ~= nil then
|
||||
callVehicle(vehicle, "setParkingBrake", md.wreckerOriginalParkingBrake)
|
||||
end
|
||||
if md.wreckerOriginalHandbrake ~= nil then
|
||||
callVehicle(vehicle, "setHandbrake", md.wreckerOriginalHandbrake)
|
||||
end
|
||||
md.wreckerOriginalMass = nil
|
||||
md.wreckerOriginalBrakingForce = nil
|
||||
md.wreckerOriginalParkingBrakeOn = nil
|
||||
md.wreckerOriginalParkingBrake = nil
|
||||
md.wreckerOriginalHandbrake = nil
|
||||
vehicle:transmitModData()
|
||||
end
|
||||
|
||||
local function isPairLinked(wrecker, target)
|
||||
return wrecker and target
|
||||
and wrecker:getVehicleTowing() == target
|
||||
and target:getVehicleTowedBy() == wrecker
|
||||
end
|
||||
|
||||
local function hasConflictingLink(vehicle, other)
|
||||
return vehicle and ((vehicle:getVehicleTowing() and vehicle:getVehicleTowing() ~= other)
|
||||
or (vehicle:getVehicleTowedBy() and vehicle:getVehicleTowedBy() ~= other))
|
||||
end
|
||||
|
||||
local function breakWreckerPair(wrecker, target)
|
||||
if not wrecker or not target then return end
|
||||
if isPairLinked(wrecker, target) then
|
||||
wrecker:breakConstraint(true, true)
|
||||
end
|
||||
end
|
||||
|
||||
local function setScriptSafely(vehicle, scriptName)
|
||||
if TowBarMod.Hook and TowBarMod.Hook.setVehicleScriptWithTowBarHidden then
|
||||
return TowBarMod.Hook.setVehicleScriptWithTowBarHidden(vehicle, scriptName)
|
||||
end
|
||||
vehicle:setScriptName(scriptName)
|
||||
return true
|
||||
end
|
||||
|
||||
local function applyAttachSync(args)
|
||||
if not args then return end
|
||||
local wrecker = getVehicleById(args.wrecker)
|
||||
local target = getVehicleById(args.target)
|
||||
if not wrecker or not target then return end
|
||||
if hasConflictingLink(wrecker, target) or hasConflictingLink(target, wrecker) then return end
|
||||
|
||||
local hookAttachment = TowBarMod.Wrecker.getHeightAttachmentId(args.heightLevel)
|
||||
local targetAttachment = args.targetAttachment
|
||||
if not hookAttachment or (targetAttachment ~= "trailer" and targetAttachment ~= "trailerfront") then return end
|
||||
if not wrecker:attachmentExist(hookAttachment) or not target:attachmentExist(targetAttachment) then return end
|
||||
|
||||
local key = pairKey(wrecker, target)
|
||||
local canonicalLevel = tonumber(args.heightLevel) or 0
|
||||
local appliedLevel = Sync.appliedLevels[key]
|
||||
if isPairLinked(wrecker, target) and appliedLevel == canonicalLevel then
|
||||
Sync.appliedLevels[key] = canonicalLevel
|
||||
applyFreeRollingState(target)
|
||||
return
|
||||
end
|
||||
|
||||
breakWreckerPair(wrecker, target)
|
||||
local targetMd = target:getModData()
|
||||
local originalScript = targetMd.wreckerOriginalScriptName or target:getScriptName()
|
||||
targetMd.wreckerOriginalScriptName = originalScript
|
||||
applyFreeRollingState(target)
|
||||
|
||||
setScriptSafely(target, "notTowingA_Trailer")
|
||||
wrecker:addPointConstraint(nil, target, hookAttachment, targetAttachment, true)
|
||||
setScriptSafely(target, originalScript)
|
||||
|
||||
local wreckerMd = wrecker:getModData()
|
||||
wreckerMd.wreckerTowActive = true
|
||||
wreckerMd.wreckerTowedVehicleId = target:getId()
|
||||
wreckerMd.wreckerTowedVehicleSqlId = tonumber(args.targetSqlId)
|
||||
wreckerMd.wreckerTargetAttachment = targetAttachment
|
||||
wreckerMd.wreckerHeightLevel = canonicalLevel
|
||||
targetMd.wreckerTowingVehicleId = wrecker:getId()
|
||||
targetMd.wreckerTowingVehicleSqlId = tonumber(args.wreckerSqlId)
|
||||
Sync.appliedLevels[key] = canonicalLevel
|
||||
end
|
||||
|
||||
local function applyDetachSync(args)
|
||||
if not args then return end
|
||||
local wrecker = getVehicleById(args.wrecker)
|
||||
local target = getVehicleById(args.target)
|
||||
if not target then return end
|
||||
local targetMd = target:getModData()
|
||||
local expectedWreckerId = tonumber(targetMd.wreckerTowingVehicleId)
|
||||
local expectedWreckerSqlId = tonumber(targetMd.wreckerTowingVehicleSqlId)
|
||||
local commandWreckerSqlId = tonumber(args.wreckerSqlId)
|
||||
if expectedWreckerSqlId and commandWreckerSqlId then
|
||||
if expectedWreckerSqlId ~= commandWreckerSqlId then return end
|
||||
elseif expectedWreckerId and expectedWreckerId ~= tonumber(args.wrecker) then
|
||||
return
|
||||
end
|
||||
if wrecker and (hasConflictingLink(wrecker, target) or hasConflictingLink(target, wrecker)) then return end
|
||||
if wrecker then breakWreckerPair(wrecker, target) end
|
||||
if targetMd.wreckerOriginalScriptName and target:getScriptName() ~= targetMd.wreckerOriginalScriptName then
|
||||
setScriptSafely(target, targetMd.wreckerOriginalScriptName)
|
||||
end
|
||||
restoreFreeRollingState(target)
|
||||
if wrecker then Sync.appliedLevels[pairKey(wrecker, target)] = nil end
|
||||
|
||||
if wrecker then
|
||||
local wreckerMd = wrecker:getModData()
|
||||
wreckerMd.wreckerTowActive = nil
|
||||
wreckerMd.wreckerTowedVehicleId = nil
|
||||
wreckerMd.wreckerTowedVehicleSqlId = nil
|
||||
wreckerMd.wreckerTargetAttachment = nil
|
||||
wreckerMd.wreckerHeightLevel = nil
|
||||
end
|
||||
targetMd.wreckerTowingVehicleId = nil
|
||||
targetMd.wreckerTowingVehicleSqlId = nil
|
||||
targetMd.wreckerOriginalScriptName = nil
|
||||
end
|
||||
|
||||
Sync.applyAttachSync = applyAttachSync
|
||||
Sync.applyDetachSync = applyDetachSync
|
||||
|
||||
Events.OnServerCommand.Add(function(module, command, args)
|
||||
if module ~= "towbar" then return end
|
||||
if command == "wreckerAttachSync" then
|
||||
applyAttachSync(args)
|
||||
elseif command == "wreckerDetachSync" then
|
||||
applyDetachSync(args)
|
||||
end
|
||||
end)
|
||||
if Events.OnSpawnVehicleEnd then
|
||||
Events.OnSpawnVehicleEnd.Add(clearAppliedLevelForVehicle)
|
||||
end
|
||||
Reference in New Issue
Block a user