Normalize line endings and enforce LF via .gitattributes

This commit is contained in:
2026-02-12 18:00:24 -05:00
parent 14f7b7bd4a
commit 633ebb4ac4
6 changed files with 493 additions and 127 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
*.lua text eol=lf
*.txt text eol=lf
*.md text eol=lf

View File

@@ -3,4 +3,5 @@ if not TowBarMod.Config then TowBarMod.Config = {} end
TowBarMod.Config.lowLevelAnimation = "RemoveGrass"
TowBarMod.Config.rigidTowbarDistance = 1.0
TowBarMod.Config.devMode = true
TowBarMod.Config.largeTowbarModelScaleThreshold = 1.2

View File

@@ -33,13 +33,66 @@ local function getTowBarItem(playerObj)
return inventory:getItemFromTypeRecurse("TowBar.TowBar")
end
local TowbarVariantSize = 24
local TowbarNormalStart = 0
local TowbarLargeStart = 24
local TowbarMaxIndex = TowbarVariantSize - 1
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()
if model then
return model:getScale()
end
return nil
end)
if ok and type(result) == "number" then
return result
end
return nil
end
local function shouldUseLargeTowbarModel(script)
local modelScale = getVehicleModelScale(script)
if modelScale == nil then
return false
end
local configuredThreshold = TowBarMod.Config and tonumber(TowBarMod.Config.largeTowbarModelScaleThreshold)
local threshold = configuredThreshold or 1.2
return modelScale < threshold
end
local function getTowbarModelSlot(script)
local chassisZ = script:getPhysicsChassisShape():z()
local index = 0
if chassisZ > 3.0 then
local halfZ = chassisZ / 2
index = math.floor((halfZ - 1.0) * 16 - 1)
end
index = math.max(0, math.min(TowbarMaxIndex, index))
local slotStart = shouldUseLargeTowbarModel(script) and TowbarLargeStart or TowbarNormalStart
return slotStart + index, index, slotStart
end
local function setTowBarModelVisible(vehicle, isVisible)
if not vehicle then return end
local part = vehicle:getPartById("towbar")
if part == nil then return end
for j = 0, 23 do
for j = 0, (TowbarVariantSize * 2) - 1 do
part:setModelVisible("towbar" .. j, false)
end
@@ -54,11 +107,8 @@ local function setTowBarModelVisible(vehicle, isVisible)
return
end
local scale = script:getModelScale()
if scale >= 1.5 and scale <= 2 then
local z = script:getPhysicsChassisShape():z()/2 - 0.1
part:setModelVisible("towbar" .. math.floor((z*2/3 - 1)*10), true)
end
local slot = getTowbarModelSlot(script)
part:setModelVisible("towbar" .. slot, true)
vehicle:doDamageOverlay()
end
@@ -238,6 +288,10 @@ function TowBarMod.Hook.setVehiclePostAttach(playerObj, towedVehicle, retriesLef
towedVehicle:setBrakingForce(0)
towedVehicle:constraintChanged()
towedVehicle:updateTotalMass()
-- Re-show the towbar model after the script name has been restored.
-- setScriptName() resets model visibility, so we must set it again here.
setTowBarModelVisible(towedVehicle, true)
end
function TowBarMod.Hook.performAttachTowBar(playerObj, towingVehicle, towedVehicle, attachmentA, attachmentB)
@@ -463,6 +517,65 @@ function TowBarMod.Hook.OnGameStart()
end
end
---------------------------------------------------------------------------
--- Dev / debug helpers
---------------------------------------------------------------------------
function TowBarMod.Hook.devShowAllTowbarModels(playerObj, vehicle)
if not vehicle then return end
local part = vehicle:getPartById("towbar")
if part == nil then
print("[TowBar DEV] No 'towbar' part found on vehicle " .. tostring(vehicle:getScriptName()))
return
end
local script = vehicle:getScript()
local chassisZ = script and script:getPhysicsChassisShape():z() or 0
local halfZ = chassisZ / 2
local modelScale = script and getVehicleModelScale(script) or nil
local slot, index, slotStart = 0, 0, TowbarNormalStart
if script then
slot, index, slotStart = getTowbarModelSlot(script)
end
print("[TowBar DEV] Vehicle: " .. tostring(vehicle:getScriptName()))
print("[TowBar DEV] chassisShape.z = " .. tostring(chassisZ) .. ", half = " .. tostring(halfZ))
print("[TowBar DEV] modelScale = " .. tostring(modelScale) .. ", largeModel = " .. tostring(slotStart == TowbarLargeStart))
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] Showing ALL 48 towbar models (towbar0..towbar47)")
for j = 0, (TowbarVariantSize * 2) - 1 do
part:setModelVisible("towbar" .. j, true)
end
vehicle:doDamageOverlay()
end
function TowBarMod.Hook.devHideAllTowbarModels(playerObj, vehicle)
if not vehicle then return end
local part = vehicle:getPartById("towbar")
if part == nil then
print("[TowBar DEV] No 'towbar' part found on vehicle " .. tostring(vehicle:getScriptName()))
return
end
print("[TowBar DEV] Hiding ALL towbar models on " .. tostring(vehicle:getScriptName()))
for j = 0, (TowbarVariantSize * 2) - 1 do
part:setModelVisible("towbar" .. j, false)
end
vehicle:doDamageOverlay()
end
function TowBarMod.Hook.devShowSingleTowbar(playerObj, vehicle, index)
if not vehicle then return end
local part = vehicle:getPartById("towbar")
if part == nil then
print("[TowBar DEV] No 'towbar' part found on vehicle " .. tostring(vehicle:getScriptName()))
return
end
for j = 0, (TowbarVariantSize * 2) - 1 do
part:setModelVisible("towbar" .. j, false)
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()
end
Events.OnSpawnVehicleEnd.Add(TowBarMod.Hook.OnSpawnVehicle)
if Events.OnGameStart then
Events.OnGameStart.Add(TowBarMod.Hook.OnGameStart)

View File

@@ -126,6 +126,71 @@ function TowBarMod.UI.addUnhookOptionToMenu(playerObj, vehicle)
)
end
---------------------------------------------------------------------------
--- Dev menu
---------------------------------------------------------------------------
function TowBarMod.UI.showDevSingleTowbarMenu(playerObj, vehicle)
local playerIndex = playerObj:getPlayerNum()
local menu = getPlayerRadialMenu(playerIndex)
menu:clear()
for j = 0, 47 do
local zIndex = j % 24
local modelType = (j >= 24) and "large" or "normal"
menu:addSlice(
"towbar" .. j .. " [" .. modelType .. "] (Z=" .. tostring(1.0 + zIndex * 0.1) .. ")",
getTexture("media/textures/tow_bar_icon.png"),
TowBarMod.Hook.devShowSingleTowbar,
playerObj,
vehicle,
j
)
end
menu:setX(getPlayerScreenLeft(playerIndex) + getPlayerScreenWidth(playerIndex) / 2 - menu:getWidth() / 2)
menu:setY(getPlayerScreenTop(playerIndex) + getPlayerScreenHeight(playerIndex) / 2 - menu:getHeight() / 2)
menu:addToUIManager()
if JoypadState.players[playerObj:getPlayerNum()+1] then
menu:setHideWhenButtonReleased(Joypad.DPadUp)
setJoypadFocus(playerObj:getPlayerNum(), menu)
playerObj:setJoypadIgnoreAimUntilCentered(true)
end
end
function TowBarMod.UI.addDevOptionsToMenu(playerObj, vehicle)
if not TowBarMod.Config.devMode then return end
if not vehicle then return end
local menu = getPlayerRadialMenu(playerObj:getPlayerNum())
if menu == nil then return end
menu:addSlice(
"[DEV] Show ALL Towbars",
getTexture("media/textures/tow_bar_icon.png"),
TowBarMod.Hook.devShowAllTowbarModels,
playerObj,
vehicle
)
menu:addSlice(
"[DEV] Hide ALL Towbars",
getTexture("media/textures/tow_bar_icon.png"),
TowBarMod.Hook.devHideAllTowbarModels,
playerObj,
vehicle
)
menu:addSlice(
"[DEV] Pick Single Towbar...",
getTexture("media/textures/tow_bar_icon.png"),
TowBarMod.UI.showDevSingleTowbarMenu,
playerObj,
vehicle
)
end
---------------------------------------------------------------------------
--- Mod compability
---------------------------------------------------------------------------
@@ -157,5 +222,7 @@ function ISVehicleMenu.showRadialMenu(playerObj)
elseif not vehicle:getVehicleTowing() and not vehicle:getVehicleTowedBy() then
TowBarMod.UI.addHookOptionToMenu(playerObj, vehicle)
end
TowBarMod.UI.addDevOptionsToMenu(playerObj, vehicle)
end

View File

@@ -2,21 +2,76 @@ BTtow = {}
BTtow.Create = {}
BTtow.Init = {}
local TowbarVariantSize = 24
local TowbarNormalStart = 0
local TowbarLargeStart = 24
local TowbarMaxIndex = TowbarVariantSize - 1
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()
if model then
return model:getScale()
end
return nil
end)
if ok and type(result) == "number" then
return result
end
return nil
end
local function shouldUseLargeTowbarModel(script)
local modelScale = getVehicleModelScale(script)
if modelScale == nil then
return false
end
local configuredThreshold = TowBarMod and TowBarMod.Config and tonumber(TowBarMod.Config.largeTowbarModelScaleThreshold)
local threshold = configuredThreshold or 1.2
return modelScale < threshold
end
local function getTowbarModelSlot(script)
local chassisZ = script:getPhysicsChassisShape():z()
local index = 0
if chassisZ > 3.0 then
local halfZ = chassisZ / 2
index = math.floor((halfZ - 1.0) * 16 - 1)
end
index = math.max(0, math.min(TowbarMaxIndex, index))
local slotStart = shouldUseLargeTowbarModel(script) and TowbarLargeStart or TowbarNormalStart
return slotStart + index
end
function BTtow.Create.towbar(vehicle, part)
if part == nil then return end
for j=0, 23 do
for j=0, (TowbarVariantSize * 2) - 1 do
part:setModelVisible("towbar" .. j, false)
end
end
function BTtow.Init.towbar(vehicle, part)
if part == nil then return end
for j=0, 23 do
part:setModelVisible("towbar" .. j, false)
for j=0, (TowbarVariantSize * 2) - 1 do
part:setModelVisible("towbar" .. j, false)
end
if vehicle:getScript():getModelScale() > 2 or vehicle:getScript():getModelScale() < 1.5 then return end
if vehicle:getModData()["isTowingByTowBar"] and vehicle:getModData()["towed"] then
local z = vehicle:getScript():getPhysicsChassisShape():z()/2 - 0.1
part:setModelVisible("towbar" .. math.floor((z*2/3-1)*10), true)
if vehicle:getModData()["isTowingByTowBar"] and vehicle:getModData()["towed"] then
local script = vehicle:getScript()
if script then
local slot = getTowbarModelSlot(script)
part:setModelVisible("towbar" .. slot, true)
end
end
end

View File

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