42.20 and Towtrucks
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
if not TowBarMod then TowBarMod = {} end
|
||||
TowBarMod.Wrecker = TowBarMod.Wrecker or {}
|
||||
|
||||
local Wrecker = TowBarMod.Wrecker
|
||||
|
||||
Wrecker.MaxAttachDistance = 0.6
|
||||
Wrecker.HeightStep = 0.30
|
||||
Wrecker.MinHeightLevel = 0
|
||||
Wrecker.MaxHeightLevel = 2
|
||||
Wrecker.ScanRadius = 8
|
||||
|
||||
local SupportedWreckers = {
|
||||
-- KI5 '76 Chevrolet K Series (Workshop 3161951724)
|
||||
["Base.76chevyC30CCwrecker"] = true,
|
||||
["Base.76chevyC30SCwrecker"] = true,
|
||||
["Base.76chevyK30CCwrecker"] = true,
|
||||
["Base.76chevyK30SCwrecker"] = true,
|
||||
|
||||
-- KI5 '93 Chevrolet Suburban / Silverado (Workshop 3152529790).
|
||||
-- The mechanic variant inherits the K3500 wrecker template.
|
||||
["Base.93chevySilveradoK3500wrecker"] = true,
|
||||
["Base.93chevySilveradoK3500mechanic"] = true,
|
||||
|
||||
-- KI5 '78 AM General M35 Series Trucks (Workshop 2799152995).
|
||||
["Base.78amgeneralM62"] = true
|
||||
}
|
||||
|
||||
local HeightAttachmentIds = {
|
||||
[0] = "towbarWreckerHookLow",
|
||||
[1] = "towbarWreckerHookMid",
|
||||
[2] = "towbarWreckerHookHigh"
|
||||
}
|
||||
|
||||
local function getScriptFullName(script)
|
||||
if not script then return nil end
|
||||
local ok, fullName = pcall(function() return script:getFullName() end)
|
||||
if ok and fullName then return tostring(fullName) end
|
||||
local name = script:getName()
|
||||
return name and ("Base." .. tostring(name)) or nil
|
||||
end
|
||||
|
||||
function Wrecker.isSupportedWrecker(vehicle)
|
||||
if not vehicle then return false end
|
||||
local script = vehicle:getScript()
|
||||
local fullName = getScriptFullName(script)
|
||||
return SupportedWreckers[fullName] == true
|
||||
and vehicle:attachmentExist("hook")
|
||||
end
|
||||
|
||||
function Wrecker.getHeightAttachmentId(level)
|
||||
return HeightAttachmentIds[Wrecker.normalizeHeightLevel(level)]
|
||||
end
|
||||
|
||||
function Wrecker.normalizeHeightLevel(level)
|
||||
local numeric = tonumber(level) or Wrecker.MinHeightLevel
|
||||
numeric = math.floor(numeric + 0.5)
|
||||
return math.max(Wrecker.MinHeightLevel, math.min(Wrecker.MaxHeightLevel, numeric))
|
||||
end
|
||||
|
||||
function Wrecker.nextHeightLevel(currentLevel, direction)
|
||||
local current = Wrecker.normalizeHeightLevel(currentLevel)
|
||||
local delta = tonumber(direction)
|
||||
if delta ~= -1 and delta ~= 1 then return nil end
|
||||
return math.max(Wrecker.MinHeightLevel, math.min(Wrecker.MaxHeightLevel, current + delta))
|
||||
end
|
||||
|
||||
local function distanceSquared(pointA, pointB)
|
||||
if not pointA or not pointB then return nil end
|
||||
local dx = pointA:x() - pointB:x()
|
||||
local dy = pointA:y() - pointB:y()
|
||||
return dx * dx + dy * dy
|
||||
end
|
||||
|
||||
local function forEachVehicle(collection, callback)
|
||||
if not collection then return end
|
||||
if type(collection) == "table" then
|
||||
for _, vehicle in ipairs(collection) do callback(vehicle) end
|
||||
return
|
||||
end
|
||||
|
||||
local ok, iterator = pcall(function() return collection:iterator() end)
|
||||
if ok and iterator then
|
||||
while iterator:hasNext() do callback(iterator:next()) end
|
||||
return
|
||||
end
|
||||
|
||||
local sizeOk, size = pcall(function() return collection:size() end)
|
||||
if not sizeOk then return end
|
||||
for index = 0, size - 1 do callback(collection:get(index)) end
|
||||
end
|
||||
|
||||
local function isTargetEligible(wrecker, vehicle)
|
||||
if not vehicle or vehicle == wrecker then return false end
|
||||
if vehicle:getVehicleTowing() or vehicle:getVehicleTowedBy() then return false end
|
||||
if wrecker:getVehicleTowing() or wrecker:getVehicleTowedBy() then return false end
|
||||
local script = vehicle:getScript()
|
||||
if not script then return false end
|
||||
local name = string.lower(tostring(script:getName() or ""))
|
||||
return not string.find(name, "trailer", 1, true)
|
||||
end
|
||||
|
||||
function Wrecker.resolveNearestTarget(wrecker, candidates)
|
||||
if not Wrecker.isSupportedWrecker(wrecker) then return nil end
|
||||
local hookPoint = wrecker:getAttachmentWorldPos("hook", Vector3f.new())
|
||||
if not hookPoint then return nil end
|
||||
|
||||
local best
|
||||
local maxDistanceSquared = Wrecker.MaxAttachDistance * Wrecker.MaxAttachDistance
|
||||
forEachVehicle(candidates, function(vehicle)
|
||||
if not isTargetEligible(wrecker, vehicle) then return end
|
||||
for _, attachmentId in ipairs({ "trailerfront", "trailer" }) do
|
||||
if vehicle:attachmentExist(attachmentId) then
|
||||
local targetPoint = vehicle:getAttachmentWorldPos(attachmentId, Vector3f.new())
|
||||
local score = distanceSquared(hookPoint, targetPoint)
|
||||
if score and score <= maxDistanceSquared then
|
||||
local vehicleId = vehicle:getId()
|
||||
if not best or score < best.distanceSquared
|
||||
or (score == best.distanceSquared and vehicleId < best.vehicle:getId()) then
|
||||
best = {
|
||||
vehicle = vehicle,
|
||||
attachment = attachmentId,
|
||||
distanceSquared = score
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
return best
|
||||
end
|
||||
|
||||
function Wrecker.getWorldVehicles()
|
||||
local cell = getCell()
|
||||
return cell and cell:getVehicles() or nil
|
||||
end
|
||||
|
||||
local function registerHookAttachments()
|
||||
local manager = getScriptManager()
|
||||
if not manager then return end
|
||||
for fullName in pairs(SupportedWreckers) do
|
||||
local script = manager:getVehicle(fullName)
|
||||
local baseAttachment = script and script:getAttachmentById("hook") or nil
|
||||
local baseOffset = baseAttachment and baseAttachment:getOffset() or nil
|
||||
if baseOffset then
|
||||
for level = Wrecker.MinHeightLevel, Wrecker.MaxHeightLevel do
|
||||
local attachmentId = HeightAttachmentIds[level]
|
||||
if script:getAttachmentById(attachmentId) == nil then
|
||||
local attachment = ModelAttachment.new(attachmentId)
|
||||
attachment:getOffset():set(
|
||||
baseOffset:x(),
|
||||
baseOffset:y() + level * Wrecker.HeightStep,
|
||||
baseOffset:z()
|
||||
)
|
||||
attachment:setUpdateConstraint(false)
|
||||
script:addAttachment(attachment)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Wrecker.registerHookAttachments = registerHookAttachments
|
||||
Events.OnGameBoot.Add(registerHookAttachments)
|
||||
Reference in New Issue
Block a user