diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..55ec86e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +*.lua text eol=lf +*.txt text eol=lf +*.md text eol=lf \ No newline at end of file diff --git a/42.13/media/lua/client/TowBar/Config.lua b/42.13/media/lua/client/TowBar/Config.lua index 8a04c87..25e1dc0 100644 --- a/42.13/media/lua/client/TowBar/Config.lua +++ b/42.13/media/lua/client/TowBar/Config.lua @@ -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 \ No newline at end of file diff --git a/42.13/media/lua/client/TowBar/TowingHooking.lua b/42.13/media/lua/client/TowBar/TowingHooking.lua index aa5dede..7f01460 100644 --- a/42.13/media/lua/client/TowBar/TowingHooking.lua +++ b/42.13/media/lua/client/TowBar/TowingHooking.lua @@ -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) diff --git a/42.13/media/lua/client/TowBar/TowingUI.lua b/42.13/media/lua/client/TowBar/TowingUI.lua index 6631845..1e0c8bb 100644 --- a/42.13/media/lua/client/TowBar/TowingUI.lua +++ b/42.13/media/lua/client/TowBar/TowingUI.lua @@ -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 diff --git a/42.13/media/lua/server/BTTow.lua b/42.13/media/lua/server/BTTow.lua index d9da316..bc7ff50 100644 --- a/42.13/media/lua/server/BTTow.lua +++ b/42.13/media/lua/server/BTTow.lua @@ -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 diff --git a/42.13/media/scripts/vehicles/template_towbar.txt b/42.13/media/scripts/vehicles/template_towbar.txt index c7a48df..7368b72 100644 --- a/42.13/media/scripts/vehicles/template_towbar.txt +++ b/42.13/media/scripts/vehicles/template_towbar.txt @@ -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, + } + } } }