Add KI5 Compat

This commit is contained in:
2026-02-12 19:01:14 -05:00
parent 633ebb4ac4
commit e694f746f8
5 changed files with 443 additions and 193 deletions

View File

@@ -4,4 +4,6 @@ if not TowBarMod.Config then TowBarMod.Config = {} end
TowBarMod.Config.lowLevelAnimation = "RemoveGrass" TowBarMod.Config.lowLevelAnimation = "RemoveGrass"
TowBarMod.Config.rigidTowbarDistance = 1.0 TowBarMod.Config.rigidTowbarDistance = 1.0
TowBarMod.Config.devMode = true TowBarMod.Config.devMode = true
TowBarMod.Config.largeTowbarModelScaleThreshold = 1.2 TowBarMod.Config.vanillaTowbarModelScaleMin = 1.5
TowBarMod.Config.vanillaTowbarModelScaleMax = 2.0
TowBarMod.Config.smallScaleTowbarIndexOffset = 2

View File

@@ -37,6 +37,8 @@ local TowbarVariantSize = 24
local TowbarNormalStart = 0 local TowbarNormalStart = 0
local TowbarLargeStart = 24 local TowbarLargeStart = 24
local TowbarMaxIndex = TowbarVariantSize - 1 local TowbarMaxIndex = TowbarVariantSize - 1
local VanillaScaleMin = 1.5
local VanillaScaleMax = 2.0
local function getVehicleModelScale(script) local function getVehicleModelScale(script)
if not script then return nil end if not script then return nil end
@@ -62,38 +64,76 @@ local function getVehicleModelScale(script)
return nil return nil
end end
local function shouldUseLargeTowbarModel(script) local function isVanillaScale(script)
local modelScale = getVehicleModelScale(script) local modelScale = getVehicleModelScale(script)
if modelScale == nil then if modelScale == nil then
return false return true
end end
local configuredThreshold = TowBarMod.Config and tonumber(TowBarMod.Config.largeTowbarModelScaleThreshold) local configuredMin = TowBarMod.Config and tonumber(TowBarMod.Config.vanillaTowbarModelScaleMin)
local threshold = configuredThreshold or 1.2 local configuredMax = TowBarMod.Config and tonumber(TowBarMod.Config.vanillaTowbarModelScaleMax)
return modelScale < threshold local minScale = configuredMin or VanillaScaleMin
local maxScale = configuredMax or VanillaScaleMax
return modelScale >= minScale and modelScale <= maxScale
end
local function getTowbarIndexVanilla(script)
local z = script:getPhysicsChassisShape():z() / 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
-- Match KI5-size vehicles by anchoring to tow attachment depth.
-- +0.1 keeps the bar slightly beyond the attachment point.
local index = math.floor((maxAbsTowZ + 0.1 - 1.0) * 10)
return math.max(0, math.min(TowbarMaxIndex, index))
end
return nil
end end
local function getTowbarModelSlot(script) local function getTowbarModelSlot(script)
local chassisZ = script:getPhysicsChassisShape():z() local isVanilla = isVanillaScale(script)
local index = 0 local index = getTowbarIndexVanilla(script)
if chassisZ > 3.0 then if not isVanilla then
local halfZ = chassisZ / 2 local attachmentIndex = getTowbarIndexSmallScale(script)
index = math.floor((halfZ - 1.0) * 16 - 1) if attachmentIndex ~= nil then
index = attachmentIndex
else
local offset = TowBarMod.Config and tonumber(TowBarMod.Config.smallScaleTowbarIndexOffset) or 2
index = math.max(0, math.min(TowbarMaxIndex, index + offset))
end end
index = math.max(0, math.min(TowbarMaxIndex, index)) end
return index, isVanilla
local slotStart = shouldUseLargeTowbarModel(script) and TowbarLargeStart or TowbarNormalStart
return slotStart + index, index, slotStart
end end
local function setTowBarModelVisible(vehicle, isVisible) local function setTowBarModelVisible(vehicle, isVisible)
if not vehicle then return end if not vehicle then return end
local part = vehicle:getPartById("towbar") local normalPart = vehicle:getPartById("towbar")
if part == nil then return end local largePart = vehicle:getPartById("towbarLarge")
if normalPart == nil and largePart == nil then return end
for j = 0, (TowbarVariantSize * 2) - 1 do for j = 0, TowbarVariantSize - 1 do
part:setModelVisible("towbar" .. j, false) if normalPart then normalPart:setModelVisible("towbar" .. j, false) end
if largePart then largePart:setModelVisible("towbar" .. j, false) end
end end
if not isVisible then if not isVisible then
@@ -107,8 +147,14 @@ local function setTowBarModelVisible(vehicle, isVisible)
return return
end end
local slot = getTowbarModelSlot(script) local index, isVanilla = getTowbarModelSlot(script)
part:setModelVisible("towbar" .. slot, true) local part = isVanilla and normalPart or largePart
if part == nil then
part = normalPart or largePart
end
if part then
part:setModelVisible("towbar" .. index, true)
end
vehicle:doDamageOverlay() vehicle:doDamageOverlay()
end end
@@ -523,56 +569,74 @@ end
function TowBarMod.Hook.devShowAllTowbarModels(playerObj, vehicle) function TowBarMod.Hook.devShowAllTowbarModels(playerObj, vehicle)
if not vehicle then return end if not vehicle then return end
local part = vehicle:getPartById("towbar") local normalPart = vehicle:getPartById("towbar")
if part == nil then local largePart = vehicle:getPartById("towbarLarge")
print("[TowBar DEV] No 'towbar' part found on vehicle " .. tostring(vehicle:getScriptName())) if normalPart == nil and largePart == nil then
print("[TowBar DEV] No 'towbar' or 'towbarLarge' part found on vehicle " .. tostring(vehicle:getScriptName()))
return return
end end
local script = vehicle:getScript() local script = vehicle:getScript()
local chassisZ = script and script:getPhysicsChassisShape():z() or 0 local chassisZ = script and script:getPhysicsChassisShape():z() or 0
local halfZ = chassisZ / 2 local halfZ = chassisZ / 2
local modelScale = script and getVehicleModelScale(script) or nil local modelScale = script and getVehicleModelScale(script) or nil
local slot, index, slotStart = 0, 0, TowbarNormalStart local index, isVanilla = 0, true
if script then if script then
slot, index, slotStart = getTowbarModelSlot(script) index, isVanilla = getTowbarModelSlot(script)
end end
local selectedPart = isVanilla and "towbar" or "towbarLarge"
print("[TowBar DEV] Vehicle: " .. tostring(vehicle:getScriptName())) print("[TowBar DEV] Vehicle: " .. tostring(vehicle:getScriptName()))
print("[TowBar DEV] chassisShape.z = " .. tostring(chassisZ) .. ", half = " .. tostring(halfZ)) print("[TowBar DEV] chassisShape.z = " .. tostring(chassisZ) .. ", half = " .. tostring(halfZ))
print("[TowBar DEV] modelScale = " .. tostring(modelScale) .. ", largeModel = " .. tostring(slotStart == TowbarLargeStart)) print("[TowBar DEV] modelScale = " .. tostring(modelScale) .. ", part = " .. selectedPart)
print("[TowBar DEV] Formula picks index = " .. tostring(index) .. " (towbar" .. tostring(slot) .. " at Z offset " .. tostring(1.0 + index * 0.1) .. ") [chassisZ > 3.0: " .. tostring(chassisZ > 3.0) .. "]") print("[TowBar DEV] Formula picks index = " .. tostring(index) .. " (towbar" .. tostring(index) .. " at Z offset " .. tostring(1.0 + index * 0.1) .. ")")
print("[TowBar DEV] Showing ALL 48 towbar models (towbar0..towbar47)") print("[TowBar DEV] Showing towbar0..towbar23 on both parts")
for j = 0, (TowbarVariantSize * 2) - 1 do for j = 0, TowbarVariantSize - 1 do
part:setModelVisible("towbar" .. j, true) if normalPart then normalPart:setModelVisible("towbar" .. j, true) end
if largePart then largePart:setModelVisible("towbar" .. j, true) end
end end
vehicle:doDamageOverlay() vehicle:doDamageOverlay()
end end
function TowBarMod.Hook.devHideAllTowbarModels(playerObj, vehicle) function TowBarMod.Hook.devHideAllTowbarModels(playerObj, vehicle)
if not vehicle then return end if not vehicle then return end
local part = vehicle:getPartById("towbar") local normalPart = vehicle:getPartById("towbar")
if part == nil then local largePart = vehicle:getPartById("towbarLarge")
print("[TowBar DEV] No 'towbar' part found on vehicle " .. tostring(vehicle:getScriptName())) if normalPart == nil and largePart == nil then
print("[TowBar DEV] No 'towbar' or 'towbarLarge' part found on vehicle " .. tostring(vehicle:getScriptName()))
return return
end end
print("[TowBar DEV] Hiding ALL towbar models on " .. tostring(vehicle:getScriptName())) print("[TowBar DEV] Hiding ALL towbar models on " .. tostring(vehicle:getScriptName()))
for j = 0, (TowbarVariantSize * 2) - 1 do for j = 0, TowbarVariantSize - 1 do
part:setModelVisible("towbar" .. j, false) if normalPart then normalPart:setModelVisible("towbar" .. j, false) end
if largePart then largePart:setModelVisible("towbar" .. j, false) end
end end
vehicle:doDamageOverlay() vehicle:doDamageOverlay()
end end
function TowBarMod.Hook.devShowSingleTowbar(playerObj, vehicle, index) function TowBarMod.Hook.devShowSingleTowbar(playerObj, vehicle, index)
if not vehicle then return end if not vehicle then return end
local part = vehicle:getPartById("towbar") local normalPart = vehicle:getPartById("towbar")
if part == nil then local largePart = vehicle:getPartById("towbarLarge")
print("[TowBar DEV] No 'towbar' part found on vehicle " .. tostring(vehicle:getScriptName())) if normalPart == nil and largePart == nil then
print("[TowBar DEV] No 'towbar' or 'towbarLarge' part found on vehicle " .. tostring(vehicle:getScriptName()))
return return
end end
for j = 0, (TowbarVariantSize * 2) - 1 do
part:setModelVisible("towbar" .. j, false) local localIndex = math.max(0, math.min(TowbarMaxIndex, index % TowbarVariantSize))
local useLargePart = index >= TowbarVariantSize
for j = 0, TowbarVariantSize - 1 do
if normalPart then normalPart:setModelVisible("towbar" .. j, false) end
if largePart then largePart:setModelVisible("towbar" .. j, false) end
end
local part = useLargePart and largePart or normalPart
if part == nil then
part = normalPart or largePart
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()))
if part then
part:setModelVisible("towbar" .. localIndex, true)
end end
print("[TowBar DEV] Showing only towbar" .. tostring(index) .. " (Z offset " .. tostring(1.0 + index * 0.1) .. ") on " .. tostring(vehicle:getScriptName()))
part:setModelVisible("towbar" .. index, true)
vehicle:doDamageOverlay() vehicle:doDamageOverlay()
end end

View File

@@ -6,6 +6,8 @@ local TowbarVariantSize = 24
local TowbarNormalStart = 0 local TowbarNormalStart = 0
local TowbarLargeStart = 24 local TowbarLargeStart = 24
local TowbarMaxIndex = TowbarVariantSize - 1 local TowbarMaxIndex = TowbarVariantSize - 1
local VanillaScaleMin = 1.5
local VanillaScaleMax = 2.0
local function getVehicleModelScale(script) local function getVehicleModelScale(script)
if not script then return nil end if not script then return nil end
@@ -31,47 +33,85 @@ local function getVehicleModelScale(script)
return nil return nil
end end
local function shouldUseLargeTowbarModel(script) local function isVanillaScale(script)
local modelScale = getVehicleModelScale(script) local modelScale = getVehicleModelScale(script)
if modelScale == nil then if modelScale == nil then
return false return true
end end
local configuredThreshold = TowBarMod and TowBarMod.Config and tonumber(TowBarMod.Config.largeTowbarModelScaleThreshold) local configuredMin = TowBarMod and TowBarMod.Config and tonumber(TowBarMod.Config.vanillaTowbarModelScaleMin)
local threshold = configuredThreshold or 1.2 local configuredMax = TowBarMod and TowBarMod.Config and tonumber(TowBarMod.Config.vanillaTowbarModelScaleMax)
return modelScale < threshold local minScale = configuredMin or VanillaScaleMin
local maxScale = configuredMax or VanillaScaleMax
return modelScale >= minScale and modelScale <= maxScale
end
local function getTowbarIndexVanilla(script)
local z = script:getPhysicsChassisShape():z() / 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
local index = math.floor((maxAbsTowZ + 0.1 - 1.0) * 10)
return math.max(0, math.min(TowbarMaxIndex, index))
end
return nil
end end
local function getTowbarModelSlot(script) local function getTowbarModelSlot(script)
local chassisZ = script:getPhysicsChassisShape():z() local isVanilla = isVanillaScale(script)
local index = 0 local index = getTowbarIndexVanilla(script)
if chassisZ > 3.0 then if not isVanilla then
local halfZ = chassisZ / 2 local attachmentIndex = getTowbarIndexSmallScale(script)
index = math.floor((halfZ - 1.0) * 16 - 1) if attachmentIndex ~= nil then
index = attachmentIndex
else
local offset = TowBarMod and TowBarMod.Config and tonumber(TowBarMod.Config.smallScaleTowbarIndexOffset) or 2
index = math.max(0, math.min(TowbarMaxIndex, index + offset))
end end
index = math.max(0, math.min(TowbarMaxIndex, index)) end
return index, isVanilla
local slotStart = shouldUseLargeTowbarModel(script) and TowbarLargeStart or TowbarNormalStart
return slotStart + index
end end
function BTtow.Create.towbar(vehicle, part) function BTtow.Create.towbar(vehicle, part)
if part == nil then return end if part == nil then return end
for j=0, (TowbarVariantSize * 2) - 1 do for j=0, TowbarVariantSize - 1 do
part:setModelVisible("towbar" .. j, false) part:setModelVisible("towbar" .. j, false)
end end
end end
function BTtow.Init.towbar(vehicle, part) function BTtow.Init.towbar(vehicle, part)
if part == nil then return end if part == nil then return end
for j=0, (TowbarVariantSize * 2) - 1 do for j=0, TowbarVariantSize - 1 do
part:setModelVisible("towbar" .. j, false) part:setModelVisible("towbar" .. j, false)
end end
if vehicle:getModData()["isTowingByTowBar"] and vehicle:getModData()["towed"] then if vehicle:getModData()["isTowingByTowBar"] and vehicle:getModData()["towed"] then
local script = vehicle:getScript() local script = vehicle:getScript()
if script then if script then
local slot = getTowbarModelSlot(script) local index, isVanilla = getTowbarModelSlot(script)
part:setModelVisible("towbar" .. slot, true) local partId = part:getId()
local shouldShowOnThisPart = (isVanilla and partId == "towbar") or ((not isVanilla) and partId == "towbarLarge")
if shouldShowOnThisPart then
part:setModelVisible("towbar" .. index, true)
end
end end
end end
end end

View File

@@ -125,6 +125,137 @@ module Base
offset = 0 -0.3 3.3, offset = 0 -0.3 3.3,
} }
area = Engine,
mechanicRequireKey = false,
lua
{
create = BTtow.Create.towbar,
init = BTtow.Init.towbar,
}
}
part towbarLarge
{
model towbar0
{
file = towbarModelLarge,
offset = 0 -0.3 1.0,
}
model towbar1
{
file = towbarModelLarge,
offset = 0 -0.3 1.1,
}
model towbar2
{
file = towbarModelLarge,
offset = 0 -0.3 1.2,
}
model towbar3
{
file = towbarModelLarge,
offset = 0 -0.3 1.3,
}
model towbar4
{
file = towbarModelLarge,
offset = 0 -0.3 1.4,
}
model towbar5
{
file = towbarModelLarge,
offset = 0 -0.3 1.5,
}
model towbar6
{
file = towbarModelLarge,
offset = 0 -0.3 1.6,
}
model towbar7
{
file = towbarModelLarge,
offset = 0 -0.3 1.7,
}
model towbar8
{
file = towbarModelLarge,
offset = 0 -0.3 1.8,
}
model towbar9
{
file = towbarModelLarge,
offset = 0 -0.3 1.9,
}
model towbar10
{
file = towbarModelLarge,
offset = 0 -0.3 2.0,
}
model towbar11
{
file = towbarModelLarge,
offset = 0 -0.3 2.1,
}
model towbar12
{
file = towbarModelLarge,
offset = 0 -0.3 2.2,
}
model towbar13
{
file = towbarModelLarge,
offset = 0 -0.3 2.3,
}
model towbar14
{
file = towbarModelLarge,
offset = 0 -0.3 2.4,
}
model towbar15
{
file = towbarModelLarge,
offset = 0 -0.3 2.5,
}
model towbar16
{
file = towbarModelLarge,
offset = 0 -0.3 2.6,
}
model towbar17
{
file = towbarModelLarge,
offset = 0 -0.3 2.7,
}
model towbar18
{
file = towbarModelLarge,
offset = 0 -0.3 2.8,
}
model towbar19
{
file = towbarModelLarge,
offset = 0 -0.3 2.9,
}
model towbar20
{
file = towbarModelLarge,
offset = 0 -0.3 3.0,
}
model towbar21
{
file = towbarModelLarge,
offset = 0 -0.3 3.1,
}
model towbar22
{
file = towbarModelLarge,
offset = 0 -0.3 3.2,
}
model towbar23
{
file = towbarModelLarge,
offset = 0 -0.3 3.3,
}
area = Engine, area = Engine,
mechanicRequireKey = false, mechanicRequireKey = false,
lua lua

View File

@@ -11,7 +11,7 @@ module Base
{ {
mesh = vehicles/Towbar, mesh = vehicles/Towbar,
texture = Vehicles/Towbar_Texture, texture = Vehicles/Towbar_Texture,
scale = 0.02, scale = 0.02022,
} }
template vehicle Towbar template vehicle Towbar
@@ -138,122 +138,134 @@ module Base
file = towbarModel, file = towbarModel,
offset = 0 -0.3 3.3, offset = 0 -0.3 3.3,
} }
model towbar24
area = Engine,
mechanicRequireKey = false,
lua
{
create = BTtow.Create.towbar,
init = BTtow.Init.towbar,
}
}
part towbarLarge
{
model towbar0
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 1.0, offset = 0 -0.3 1.0,
} }
model towbar25 model towbar1
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 1.1, offset = 0 -0.3 1.1,
} }
model towbar26 model towbar2
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 1.2, offset = 0 -0.3 1.2,
} }
model towbar27 model towbar3
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 1.3, offset = 0 -0.3 1.3,
} }
model towbar28 model towbar4
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 1.4, offset = 0 -0.3 1.4,
} }
model towbar29 model towbar5
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 1.5, offset = 0 -0.3 1.5,
} }
model towbar30 model towbar6
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 1.6, offset = 0 -0.3 1.6,
} }
model towbar31 model towbar7
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 1.7, offset = 0 -0.3 1.7,
} }
model towbar32 model towbar8
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 1.8, offset = 0 -0.3 1.8,
} }
model towbar33 model towbar9
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 1.9, offset = 0 -0.3 1.9,
} }
model towbar34 model towbar10
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 2.0, offset = 0 -0.3 2.0,
} }
model towbar35 model towbar11
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 2.1, offset = 0 -0.3 2.1,
} }
model towbar36 model towbar12
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 2.2, offset = 0 -0.3 2.2,
} }
model towbar37 model towbar13
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 2.3, offset = 0 -0.3 2.3,
} }
model towbar38 model towbar14
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 2.4, offset = 0 -0.3 2.4,
} }
model towbar39 model towbar15
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 2.5, offset = 0 -0.3 2.5,
} }
model towbar40 model towbar16
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 2.6, offset = 0 -0.3 2.6,
} }
model towbar41 model towbar17
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 2.7, offset = 0 -0.3 2.7,
} }
model towbar42 model towbar18
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 2.8, offset = 0 -0.3 2.8,
} }
model towbar43 model towbar19
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 2.9, offset = 0 -0.3 2.9,
} }
model towbar44 model towbar20
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 3.0, offset = 0 -0.3 3.0,
} }
model towbar45 model towbar21
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 3.1, offset = 0 -0.3 3.1,
} }
model towbar46 model towbar22
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 3.2, offset = 0 -0.3 3.2,
} }
model towbar47 model towbar23
{ {
file = towbarModelLarge, file = towbarModelLarge,
offset = 0 -0.3 3.3, offset = 0 -0.3 3.3,
@@ -267,5 +279,6 @@ module Base
init = BTtow.Init.towbar, init = BTtow.Init.towbar,
} }
} }
} }
} }