149 lines
5.4 KiB
Lua
149 lines
5.4 KiB
Lua
BTtow = {}
|
|
BTtow.Create = {}
|
|
BTtow.Init = {}
|
|
|
|
require("TowBar/VehicleCompatibility")
|
|
|
|
local TowbarVariantSize = 24
|
|
local TowbarMaxIndex = TowbarVariantSize - 1
|
|
local TowbarFirstZ = 1.0
|
|
local TowbarSlotStep = 0.1
|
|
-- Measured from Towbar.fbx at its original 0.01 script scale. Keep this in
|
|
-- lockstep with the client selector so the enlarged mesh's inner end remains
|
|
-- on the same vehicle-hitbox contact point.
|
|
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
|
|
|
|
local ok, shape = pcall(function()
|
|
return script:getPhysicsChassisShape()
|
|
end)
|
|
if not ok or not shape then return nil end
|
|
|
|
local shapeOk, shapeZ = pcall(function()
|
|
return shape:z()
|
|
end)
|
|
if not shapeOk or type(shapeZ) ~= "number" or shapeZ <= 0 then
|
|
return nil
|
|
end
|
|
|
|
local centerOk, center = pcall(function()
|
|
return script:getCenterOfMassOffset()
|
|
end)
|
|
if not centerOk or not center then return nil end
|
|
|
|
local zOk, centerZ = pcall(function()
|
|
return center:z()
|
|
end)
|
|
if not zOk or type(centerZ) ~= "number" then return nil end
|
|
|
|
return centerZ + shapeZ / 2
|
|
end
|
|
|
|
local function getTowbarModelSlot(script)
|
|
local frontEdgeZ = getTowbarFrontEdgeZ(script)
|
|
if frontEdgeZ == nil then return nil end
|
|
|
|
-- Model offsets position the mesh origin. Move its center outward so the
|
|
-- towbar's inner end, rather than its center, meets the vehicle hitbox.
|
|
local modelCenterZ = frontEdgeZ + TowbarModelHalfLength
|
|
local index = math.floor(((modelCenterZ - TowbarFirstZ) / TowbarSlotStep) + 0.5)
|
|
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)
|
|
return ok and type(result) == "number" and result or 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 end
|
|
return index, useNormalPart
|
|
end
|
|
|
|
function BTtow.Create.towbar(vehicle, part)
|
|
if part == nil then return end
|
|
for j=0, TowbarVariantSize - 1 do
|
|
part:setModelVisible("towbar" .. j, false)
|
|
end
|
|
end
|
|
|
|
function BTtow.Init.towbar(vehicle, part)
|
|
if part == nil then return end
|
|
for j=0, TowbarVariantSize - 1 do
|
|
part:setModelVisible("towbar" .. j, false)
|
|
end
|
|
local modData = vehicle:getModData()
|
|
if modData and modData.towBarModelSwapInProgress then
|
|
return
|
|
end
|
|
if modData and modData["isTowingByTowBar"] and modData["towed"] then
|
|
local script = vehicle:getScript()
|
|
if script then
|
|
local isKi5 = TowBarMod.Compatibility.isKi5Vehicle(vehicle)
|
|
local index, useNormalPart
|
|
if isKi5 then
|
|
index = getTowbarModelSlot(script)
|
|
else
|
|
index, useNormalPart = getLegacyTowbarModelSlot(script)
|
|
end
|
|
local partId = part:getId()
|
|
local shouldShowOnThisPart = (isKi5 and partId == "towbarKI5")
|
|
or (not isKi5 and useNormalPart and partId == "towbar")
|
|
or (not isKi5 and not useNormalPart and partId == "towbarLarge")
|
|
if shouldShowOnThisPart and index ~= nil then
|
|
part:setModelVisible("towbar" .. index, true)
|
|
end
|
|
end
|
|
end
|
|
end
|