supinus

Utils

Jun 25th, 2025
19
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 18.02 KB | Gaming | 0 0
  1. ac.log('Script: Utils')
  2. local sim = ac.getSim()
  3. local car = ac.getCar(0) or error()
  4. if not car then return end
  5. local wheels = car.wheels or error()
  6. local uiState = ac.getUI()
  7.  
  8.  
  9. ui.setAsynchronousImagesLoading(true)
  10.  
  11. local localTesting = ac.dirname() == 'C:\\Program Files (x86)\\Steam\\steamapps\\common\\assettocorsa\\extension\\lua\\online'
  12. local initialisation = true
  13.  
  14. -- Constants --
  15. local STEAMID = const(ac.getUserSteamID())
  16. local CSP_VERSION = const(ac.getPatchVersionCode())
  17. local CSP_MIN_VERSION = const(3044)
  18. local CAR_ID = const(ac.getCarID(0))
  19. local CAR_NAME = const(ac.getCarName(0))
  20. local DRIVER_NAME = const(ac.getDriverName(0))
  21. local vUp = const(vec3(0, 1, 0))
  22. local vDown = const(vec3(0, -1, 0))
  23.  
  24. local SHARED_PLAYER_DATA = const('__ACP_SHARED_PLAYER_DATA')
  25. local SHARED_EVENT_KEY = const('__ACP_PLAYER_SHARED_UPDATE')
  26.  
  27. if CSP_VERSION < CSP_MIN_VERSION then return end
  28.  
  29. local WINDOW_WIDTH = const(sim.windowWidth / uiState.uiScale)
  30. local WINDOW_HEIGHT = const(sim.windowHeight / uiState.uiScale)
  31. local POLICE_CAR = { "ids_2022_ford_crown" }
  32.  
  33. local STATS_FONT_SIZE = const({
  34.     title = 40 / uiState.uiScale,
  35.     header = 30 / uiState.uiScale,
  36.     stats = 20 / uiState.uiScale,
  37. })
  38.  
  39. local WIDTH_DIV = const({
  40.     _2 = WINDOW_WIDTH / 2,
  41.     _3 = WINDOW_WIDTH / 3,
  42.     _4 = WINDOW_WIDTH / 4,
  43.     _5 = WINDOW_WIDTH / 5,
  44.     _6 = WINDOW_WIDTH / 6,
  45.     _8 = WINDOW_WIDTH / 8,
  46.     _9 = WINDOW_WIDTH / 9,
  47.     _10 = WINDOW_WIDTH / 10,
  48.     _12 = WINDOW_WIDTH / 12,
  49.     _15 = WINDOW_WIDTH / 15,
  50.     _20 = WINDOW_WIDTH / 20,
  51.     _25 = WINDOW_WIDTH / 25,
  52.     _30 = WINDOW_WIDTH / 30,
  53.     _32 = WINDOW_WIDTH / 32,
  54.     _40 = WINDOW_WIDTH / 40,
  55.     _50 = WINDOW_WIDTH / 50,
  56.     _100 = WINDOW_WIDTH / 100,
  57.     _320 = WINDOW_WIDTH / 320,
  58. })
  59.  
  60. local HEIGHT_DIV = const({
  61.     _2 = WINDOW_HEIGHT / 2,
  62.     _3 = WINDOW_HEIGHT / 3,
  63.     _4 = WINDOW_HEIGHT / 4,
  64.     _6 = WINDOW_HEIGHT / 6,
  65.     _8 = WINDOW_HEIGHT / 8,
  66.     _12 = WINDOW_HEIGHT / 12,
  67.     _14 = WINDOW_HEIGHT / 14,
  68.     _16 = WINDOW_HEIGHT / 16,
  69.     _20 = WINDOW_HEIGHT / 20,
  70.     _24 = WINDOW_HEIGHT / 24,
  71.     _25 = WINDOW_HEIGHT / 25,
  72.     _30 = WINDOW_HEIGHT / 30,
  73.     _40 = WINDOW_HEIGHT / 40,
  74.     _50 = WINDOW_HEIGHT / 50,
  75.     _60 = WINDOW_HEIGHT / 60,
  76.     _70 = WINDOW_HEIGHT / 70,
  77.     _80 = WINDOW_HEIGHT / 80,
  78.     _100 = WINDOW_HEIGHT / 100,
  79.     _320 = WINDOW_HEIGHT / 320,
  80. })
  81.  
  82.  
  83. local GAS_STATIONS = const({
  84.     {pos = vec3(762.713, 95.68, 2253.62), up = vec3(0.0211318, 0.998816, 0.0438106)},
  85.     {pos = vec3(-865, 144, 3496), up = vec3(0.0211318, 0.998816, 0.0438106)},
  86.     {pos = vec3(-4003, 58, 96), up = vec3(0.0211318, 0.998816, 0.0438106)},
  87. })
  88.  
  89. ---@param format string
  90. ---@param time number
  91. ---@return string
  92. local function formatTime(time, format)
  93.     if format == 'time played' then
  94.         local hours = math.floor(time / 3600)
  95.         local minutes = math.floor(time % 3600 / 60)
  96.         local seconds = math.floor(time % 60)
  97.         local formattedTime = ''
  98.         if hours > 0 then formattedTime = hours .. 'h ' end
  99.         if minutes > 0 then formattedTime = formattedTime .. minutes .. 'm ' end
  100.         formattedTime = formattedTime .. seconds .. 's'
  101.         return formattedTime
  102.     else
  103.         local minutes = math.floor(time / 60)
  104.         local seconds = math.floor(time % 60)
  105.         local milliseconds = math.floor((time % 1) * 1000)
  106.         return ('%02d:%02d.%03d'):format(minutes, seconds, milliseconds)
  107.     end
  108. end
  109.  
  110. ---@param number number
  111. ---@param decimal integer
  112. ---@return number
  113. local function truncate(number, decimal)
  114.     local power = 10 ^ decimal
  115.     return math.floor(number * power) / power
  116. end
  117.  
  118. ---@param carID string
  119. local function isPoliceCar(carID)
  120.     for _, carName in ipairs(POLICE_CAR) do
  121.         if carID == carName then
  122.             return true
  123.         end
  124.     end
  125.     return false
  126. end
  127.  
  128. local playerData = {
  129.     hudColorInverted = rgbm(0, 1, 1, 1),
  130.     hudColor = rgbm.colors.red,
  131.     name = '',
  132.     sectors = {},
  133.     arrests = '0',
  134.     getaways = '0',
  135.     thefts = '0',
  136.     heists = '0',
  137.     deliveries = '0',
  138.     overtake = '0',
  139.     wins = '0',
  140.     losses = '0',
  141.     elo = '0',
  142.     kms = '0',
  143.     time = '0',
  144. }
  145.  
  146. local sharedPlayerLayout = {
  147.     ac.StructItem.key(SHARED_PLAYER_DATA),
  148.     hudColor = ac.StructItem.rgbm(),
  149.     name = ac.StructItem.string(24),
  150.     sectorsFormated = ac.StructItem.array(ac.StructItem.struct({
  151.         name = ac.StructItem.string(16),
  152.         records = ac.StructItem.array(ac.StructItem.string(50), 20)
  153.     }), 5),
  154.     arrests = ac.StructItem.uint16(),
  155.     getaways = ac.StructItem.uint16(),
  156.     thefts = ac.StructItem.uint16(),
  157.     heists = ac.StructItem.uint16(),
  158.     deliveries = ac.StructItem.uint16(),
  159.     overtake = ac.StructItem.uint32(),
  160.     wins = ac.StructItem.uint16(),
  161.     losses = ac.StructItem.uint16(),
  162.     elo = ac.StructItem.uint16(),
  163.     kms = ac.StructItem.float(),
  164.     time = ac.StructItem.float(),
  165. }
  166.  
  167. local sharedPlayerData = ac.connect(sharedPlayerLayout, true, ac.SharedNamespace.ServerScript)
  168.  
  169. local playerStatsWindow = {
  170.     visible = true,
  171.     pos = vec2(WIDTH_DIV._2 - WIDTH_DIV._50, HEIGHT_DIV._25),
  172.     size = vec2(WIDTH_DIV._2, HEIGHT_DIV._2),
  173. }
  174.  
  175. local leftClickDown = false
  176.  
  177. local function moveMenu()
  178.     if ui.windowHovered(ui.HoveredFlags.ChildWindows) then
  179.         if not leftClickDown and ui.mouseDown() then leftClickDown = true end
  180.     end
  181.     if ui.mouseReleased() then leftClickDown = false end
  182.     if leftClickDown then
  183.         playerStatsWindow.pos = playerStatsWindow.pos + ui.mouseDelta()
  184.         playerStatsWindow.pos.x = math.clamp(playerStatsWindow.pos.x, 0, WINDOW_WIDTH - playerStatsWindow.size.x)
  185.         playerStatsWindow.pos.y = math.clamp(playerStatsWindow.pos.y, 0, WINDOW_HEIGHT - playerStatsWindow.size.y)
  186.     end
  187. end
  188.  
  189. local function playerScores()
  190.     ui.dwriteTextWrapped("Scores: ", STATS_FONT_SIZE.header, playerData.hudColor)
  191.     ui.newLine()
  192.     ui.sameLine(WIDTH_DIV._100)
  193.     ui.beginGroup()
  194.     ui.dwriteTextWrapped("Arrests: ", STATS_FONT_SIZE.stats, playerData.hudColorInverted)
  195.     ui.sameLine(WIDTH_DIV._10)
  196.     ui.dwriteTextWrapped(playerData.arrests, STATS_FONT_SIZE.stats, rgbm.colors.white)
  197.     ui.dwriteTextWrapped("Getaways: ", STATS_FONT_SIZE.stats, playerData.hudColorInverted)
  198.     ui.sameLine(WIDTH_DIV._10)
  199.     ui.dwriteTextWrapped(playerData.getaways, STATS_FONT_SIZE.stats, rgbm.colors.white)
  200.     ui.dwriteTextWrapped("Car Thefts: ", STATS_FONT_SIZE.stats, playerData.hudColorInverted)
  201.     ui.sameLine(WIDTH_DIV._10)
  202.     ui.dwriteTextWrapped(playerData.thefts, STATS_FONT_SIZE.stats, rgbm.colors.white)
  203.     ui.dwriteTextWrapped("Bank Heists: ", STATS_FONT_SIZE.stats, playerData.hudColorInverted)
  204.     ui.sameLine(WIDTH_DIV._10)
  205.     ui.dwriteTextWrapped(playerData.heists, STATS_FONT_SIZE.stats, rgbm.colors.white)
  206.     ui.dwriteTextWrapped("Drug Deliveries: ", STATS_FONT_SIZE.stats, playerData.hudColorInverted)
  207.     ui.sameLine(WIDTH_DIV._10)
  208.     ui.dwriteTextWrapped(playerData.deliveries, STATS_FONT_SIZE.stats, rgbm.colors.white)
  209.     ui.dwriteTextWrapped("Overtake: ", STATS_FONT_SIZE.stats, playerData.hudColorInverted)
  210.     ui.sameLine(WIDTH_DIV._10)
  211.     ui.dwriteTextWrapped(playerData.overtake, STATS_FONT_SIZE.stats, rgbm.colors.white)
  212.     ui.dwriteTextWrapped("Race Wins: ", STATS_FONT_SIZE.stats, playerData.hudColorInverted)
  213.     ui.sameLine(WIDTH_DIV._10)
  214.     ui.dwriteTextWrapped(playerData.wins, STATS_FONT_SIZE.stats, rgbm.colors.white)
  215.     ui.dwriteTextWrapped("Race Losses: ", STATS_FONT_SIZE.stats, playerData.hudColorInverted)
  216.     ui.sameLine(WIDTH_DIV._10)
  217.     ui.dwriteTextWrapped(playerData.losses, STATS_FONT_SIZE.stats, rgbm.colors.white)
  218.     ui.dwriteTextWrapped("Racing Elo: ", STATS_FONT_SIZE.stats, playerData.hudColorInverted)
  219.     ui.sameLine(WIDTH_DIV._10)
  220.     ui.dwriteTextWrapped(playerData.elo, STATS_FONT_SIZE.stats, rgbm.colors.white)
  221.     -- ui.dwriteTextWrapped("Distance Driven: ", STATS_FONT_SIZE.stats, playerData.hudColorInverted)
  222.     -- ui.sameLine(WIDTH_DIV._10)
  223.     -- ui.dwriteTextWrapped(playerData.kms, STATS_FONT_SIZE.stats, rgbm.colors.white)
  224.     -- ui.dwriteTextWrapped("Time Played: ", STATS_FONT_SIZE.stats, playerData.hudColorInverted)
  225.     -- ui.sameLine(WIDTH_DIV._10)
  226.     -- ui.dwriteTextWrapped(playerData.time, STATS_FONT_SIZE.stats, rgbm.colors.white)
  227.     ui.endGroup()
  228. end
  229.  
  230. local function playerTimes()
  231.     ui.dwriteTextWrapped("Sectors: ", STATS_FONT_SIZE.header, playerData.hudColor)
  232.     ui.newLine()
  233.     ui.sameLine(WIDTH_DIV._100)
  234.     ui.beginGroup()
  235.     for sectorName, record in pairs(playerData.sectors) do
  236.         ui.dwriteTextWrapped(sectorName .. ": ", STATS_FONT_SIZE.stats, playerData.hudColor)
  237.         ui.beginSubgroup(WIDTH_DIV._50)
  238.         for i = 1, #record do
  239.             ui.dwriteTextWrapped(record[i].car .. ": ", STATS_FONT_SIZE.stats, playerData.hudColorInverted)
  240.             ui.sameLine(WIDTH_DIV._8)
  241.             ui.dwriteTextWrapped(record[i].time, STATS_FONT_SIZE.stats, rgbm.colors.white)
  242.         end
  243.         ui.endSubgroup()
  244.         ui.newLine()
  245.     end
  246.     ui.dummy(vec2(WIDTH_DIV._50, HEIGHT_DIV._50))
  247.     ui.endGroup()
  248. end
  249.  
  250. local playerStatsSubWindow = const(vec2(WIDTH_DIV._4 - WIDTH_DIV._4 / 40, HEIGHT_DIV._2 - HEIGHT_DIV._20 - 10))
  251.  
  252. local function playerStats()
  253.     ui.pushDWriteFont("Orbitron;Weight=Black")
  254.     ui.dwriteTextWrapped("Player Stats", STATS_FONT_SIZE.title, rgbm.colors.white)
  255.     local playerStatSize = ui.measureDWriteText("Player Stats", 45)
  256.     ui.sameLine(WIDTH_DIV._2 - 64)
  257.     if ui.modernButton('', vec2(48, 40), ui.ButtonFlags.Error, playerStatsWindow.visible and 'HIDE' or 'EYE', 32, nil) then
  258.         playerStatsWindow.visible = not playerStatsWindow.visible
  259.         if playerStatsWindow.visible then
  260.             playerStatsWindow.size = vec2(WIDTH_DIV._2, HEIGHT_DIV._2)
  261.         else
  262.             playerStatsWindow.size = vec2(WIDTH_DIV._2, playerStatSize.y + 10)
  263.         end
  264.     end
  265.     if not playerStatsWindow.visible then return end
  266.     ui.separator()
  267.     local topPosY = ui.getCursorY() - 5
  268.     ui.childWindow('playerTimes', playerStatsSubWindow, false, ui.WindowFlags.ThinScrollbar, function()
  269.         playerTimes()
  270.     end)
  271.     ui.drawSimpleLine(vec2(playerStatsSubWindow.x + WIDTH_DIV._100, topPosY), vec2(playerStatsSubWindow.x + WIDTH_DIV._100, ui.getCursorY()), rgbm(0.1, 0.1, 0.1, 0.3), 2)
  272.     ui.sameLine(playerStatsSubWindow.x + WIDTH_DIV._50)
  273.     ui.childWindow('playerScores', playerStatsSubWindow, false, function()
  274.         playerScores()
  275.     end)
  276.     ui.popDWriteFont()
  277. end
  278.  
  279. ui.onExclusiveHUD(function(mode)
  280.     if mode == 'menu' then
  281.         ui.toolWindow('PlayerStats', playerStatsWindow.pos, playerStatsWindow.size, false, true, function()
  282.             moveMenu()
  283.             playerStats()
  284.         end)
  285.     end
  286. end)
  287.  
  288. local function resetPlayerData()
  289.     playerData = {
  290.         hudColor = rgbm.colors.red,
  291.         hudColorInverted = rgbm(0, 1, 1, 1),
  292.         name = '',
  293.         sectors = {},
  294.         arrests = '0',
  295.         getaways = '0',
  296.         thefts = '0',
  297.         overtake = '0',
  298.         wins = '0',
  299.         losses = '0',
  300.         elo = '0',
  301.     }
  302. end
  303.  
  304. local function updatedSharedData()
  305.     resetPlayerData()
  306.     if sharedPlayerData.name ~= '' then
  307.         playerData.hudColor = sharedPlayerData.hudColor
  308.         playerData.hudColorInverted = rgbm(1 - sharedPlayerData.hudColor.r, 1 - sharedPlayerData.hudColor.g, 1 - sharedPlayerData.hudColor.b, 1)
  309.         playerData.name = sharedPlayerData.name
  310.         playerData.arrests = tostring(sharedPlayerData.arrests)
  311.         playerData.getaways = tostring(sharedPlayerData.getaways)
  312.         playerData.thefts = tostring(sharedPlayerData.thefts)
  313.         playerData.heists = tostring(sharedPlayerData.heists)
  314.         playerData.deliveries = tostring(sharedPlayerData.deliveries)
  315.         playerData.overtake = tostring(sharedPlayerData.overtake)
  316.         playerData.wins = tostring(sharedPlayerData.wins)
  317.         playerData.losses = tostring(sharedPlayerData.losses)
  318.         playerData.elo = tostring(sharedPlayerData.elo) .. ' pts'
  319.         playerData.kms = tostring(truncate(sharedPlayerData.kms, 3)) .. ' km'
  320.         playerData.time = formatTime(sharedPlayerData.time, 'time played')
  321.         for i = 1, 5 do
  322.             local sectorName = ffi.string(sharedPlayerData.sectorsFormated[i].name)
  323.             if sectorName ~= '' then
  324.                 for j = 1, 11 do
  325.                     local record = ffi.string(sharedPlayerData.sectorsFormated[i].records[j])
  326.                     if record ~= '' then
  327.                         local recordInfo = string.split(record, ' - ')
  328.                         if #recordInfo == 2 then
  329.                             if not playerData.sectors[sectorName] then playerData.sectors[sectorName] = {} end
  330.                             table.insert(playerData.sectors[sectorName], {car = recordInfo[1], time = recordInfo[2]})
  331.                         end
  332.                     end
  333.                 end
  334.             end
  335.         end
  336.     end
  337. end
  338.  
  339. ac.onSharedEvent(SHARED_EVENT_KEY, function(data)
  340.     if data == 'update' then
  341.         updatedSharedData()
  342.     end
  343. end, true)
  344.  
  345. local fuelWindow = {
  346.     visible = true,
  347.     pos = vec2(WIDTH_DIV._2 - WIDTH_DIV._4/2, HEIGHT_DIV._8),
  348.     size = vec2(WIDTH_DIV._4, HEIGHT_DIV._4),
  349.     up = {
  350.         p1 = vec2(WIDTH_DIV._4 / 2 - 96, HEIGHT_DIV._8 / 2 - 24),
  351.         p2 = vec2(WIDTH_DIV._4 / 2 - 48, HEIGHT_DIV._8 / 2 + 24),
  352.         color = rgbm.colors.white,
  353.     },
  354.     fuel = {
  355.         p1 = vec2(WIDTH_DIV._4 / 2 - 32, HEIGHT_DIV._8 / 2 - 32),
  356.         p2 = vec2(WIDTH_DIV._4 / 2 + 32, HEIGHT_DIV._8 / 2 + 32),
  357.         color = rgbm.colors.white,
  358.     },
  359.     down = {
  360.         p1 = vec2(WIDTH_DIV._4 / 2 + 48, HEIGHT_DIV._8 / 2 - 24),
  361.         p2 = vec2(WIDTH_DIV._4 / 2 + 96, HEIGHT_DIV._8 / 2 + 24),
  362.         color = rgbm.colors.white,
  363.     },
  364. }
  365.  
  366. local function isAtGasStation()
  367.     for _, gasStation in ipairs(GAS_STATIONS) do
  368.         if car.position:distanceSquared(gasStation.pos) < 500 then
  369.             return true
  370.         end
  371.     end
  372.     return false
  373. end
  374.  
  375. local carFuel = car.fuel
  376.  
  377. local function textWithBackground(text, sizeMult, yOffset, textColor)
  378.     ui.pushDWriteFont("Orbitron;Weight=Black")
  379.     local textSize = ui.measureDWriteText(text, STATS_FONT_SIZE.stats * sizeMult)
  380.     local rectPos1 = vec2(WINDOW_WIDTH / 2, HEIGHT_DIV._100 + yOffset) - vec2(textSize.x / 2, 0)
  381.     local rectPos2 = textSize + rectPos1
  382.     local rectOffset = vec2(WIDTH_DIV._320, HEIGHT_DIV._320)
  383.     if ui.time() % 1 < 0.5 then
  384.         ui.drawRectFilled(rectPos1 - rectOffset, rectPos2 + rectOffset, rgbm(0, 0, 0, 0.1), 10)
  385.     else
  386.         ui.drawRectFilled(rectPos1 - rectOffset, rectPos2 + rectOffset, rgbm(0, 0, 0, 0.6), 10)
  387.     end
  388.     ui.dwriteDrawText(text, STATS_FONT_SIZE.stats * sizeMult, rectPos1, textColor)
  389.     ui.popDWriteFont()
  390. end
  391.  
  392. local function fillCarWithFuel()
  393.     if car.speedKmh > 1 then
  394.         textWithBackground('Stop the car to refuel!', 1, HEIGHT_DIV._50, rgbm.colors.red)
  395.     end
  396.     ui.transparentWindow('FuelWindow', fuelWindow.pos, fuelWindow.size, true, function()
  397.         -- if ui.rectHovered(fuelWindow.down.p1, fuelWindow.down.p2) then fuelWindow.down.color = rgbm.colors.red else fuelWindow.down.color = rgbm.colors.white end
  398.         if ui.rectHovered(fuelWindow.up.p1, fuelWindow.up.p2) then fuelWindow.up.color = rgbm.colors.green else fuelWindow.up.color = rgbm.colors.white end
  399.         -- if ui.rectHovered(fuelWindow.fuel.p1, fuelWindow.fuel.p2) then fuelWindow.fuel.color = rgbm.colors.blue else fuelWindow.fuel.color = rgbm.colors.white end
  400.         -- ui.drawIcon(ui.Icons.ArrowDown, fuelWindow.down.p1, fuelWindow.down.p2, fuelWindow.down.color)
  401.         if ui.time() % 1 < 0.5 then
  402.             ui.drawIcon(ui.Icons.Fuel, fuelWindow.fuel.p1, fuelWindow.fuel.p2, rgbm.colors.white)
  403.         else
  404.             ui.drawIcon(ui.Icons.Fuel, fuelWindow.fuel.p1, fuelWindow.fuel.p2, car.speedKmh > 1 and rgbm.colors.white or rgbm.colors.green)
  405.         end
  406.         -- ui.drawIcon(ui.Icons.ArrowUp, fuelWindow.up.p1, fuelWindow.up.p2, fuelWindow.up.color)
  407.         ui.newLine(HEIGHT_DIV._16)
  408.         ui.pushDWriteFont("Orbitron;Weight=Black")
  409.         ui.dwriteTextWrapped('Fuel: ' .. math.floor(car.fuel) .. ' / ' .. car.maxFuel, STATS_FONT_SIZE.stats, rgbm.colors.white)
  410.         ui.popDWriteFont()
  411.         ui.progressBar(car.fuel / car.maxFuel, vec2(WIDTH_DIV._4, HEIGHT_DIV._40))
  412.     end)
  413.     if car.fuel >= car.maxFuel or car.speedKmh > 1 then return end
  414.     physics.setCarFuel(0, math.min(car.maxFuel, car.fuel + uiState.dt * 2))
  415.     carFuel = car.fuel
  416. end
  417.  
  418. local function fuelWarning()
  419.     if car.fuel < 5 then
  420.         textWithBackground('Fuel Low! Stop at a gas station to refuel.', 1, 0, rgbm.colors.red)
  421.     end
  422. end
  423.  
  424. local CREW_PREFIX = const({
  425.     ['emile lesage *SR*'] = 'sr',
  426.     ['_RR_'] = 'rr',
  427.     ['[MM]'] = 'mm',
  428. })
  429.  
  430. local CAR_NAMES = const({
  431.     { "300zx_acp24" },
  432.     { "ae86_acp24" },
  433.     { "celicast205_acp24" },
  434.     { "civiceg6_acp24" },
  435.     { "evo3_acp24" },
  436.     { "gt86_acp24" },
  437.     { "gto_acp24" },
  438.     { "impreza22b_acp24" },
  439.     { "nsx_acp24" },
  440.     { "r32_acp24" },
  441.     { "r34gtr_acp24" },
  442.     { "rx7_acp24" },
  443.     { "rx7gtx_acp24" },
  444.     { "sil180_acp24" },
  445.     { "supra_acp24" },
  446.     { "supramk3_acp24" },
  447. })
  448.  
  449. ---@param url string
  450. ---@param carId integer
  451. local function applySkinToCar(carId, url)
  452.     web.loadRemoteAssets(url, function(error, data)
  453.         local carNode = ac.findNodes('carRoot:' .. carId)
  454.         carNode:resetSkin()
  455.         carNode:applySkin({
  456.             ['ext_body_Mixed_AO.dds'] = data .. "/ext_body_Mixed_AO.dds"
  457.         })
  458.         ac.refreshCarColor(carId)
  459.     end)
  460. end
  461.  
  462. local skinWindowParams = {
  463.     visible = true,
  464.     pos = vec2(WIDTH_DIV._2, HEIGHT_DIV._12),
  465.     size = vec2(WIDTH_DIV._4, HEIGHT_DIV._4),
  466. }
  467.  
  468. local function getSkinPrefix(carIndex)
  469.     local playerName = ac.getDriverName(carIndex)
  470.     if playerName then
  471.         for crew, prefix in pairs(CREW_PREFIX) do
  472.             if string.find(playerName, crew) or playerName == crew then
  473.                 return prefix
  474.             end
  475.         end
  476.     end
  477.     return 'base'
  478. end
  479.  
  480. -- local function applySkinToConnectedCar(carIndex, default)
  481. --  local carId = ac.getCarID(carIndex)
  482. --  local skin = "/" .. getSkinPrefix(carIndex) .. "_ext_body_Mixed_AO.zip"
  483. --  if default then skin = "/base_ext_body_Mixed_AO.zip" end
  484. --  local url = "https://github.com/ele-sage/ACP-apps/raw/refs/heads/master/skins/" .. carId .. skin
  485.  
  486. --  applySkinToCar(carIndex, url)
  487. -- end
  488.  
  489. -- local function applySkinToAllConnectedCars()
  490. --  for i, c in ac.iterateCars.serverSlots() do
  491. --      if not c.isHidingLabels and not isPoliceCar(c:id()) then
  492. --          applySkinToConnectedCar(c.index)
  493. --      end
  494. --  end
  495. -- end
  496.  
  497. -- applySkinToAllConnectedCars()
  498.  
  499. -- ac.onClientConnected(function (carIndex, connectedSessionID)
  500. --  applySkinToConnectedCar(carIndex)
  501. -- end)
  502.  
  503. -- ac.onClientDisconnected(function (carIndex, connectedSessionID)
  504. --  local carId = ac.getCarID(carIndex)
  505. --  local url = "https://github.com/ele-sage/ACP-apps/raw/refs/heads/master/skins/" .. carId .. "/base_ext_body_Mixed_AO.zip"
  506. --  applySkinToCar(carIndex, url)
  507. -- end)
  508.  
  509. -- local function skinWindow()
  510. --  ui.toolWindow('skinWindow', skinWindowParams.pos, skinWindowParams.size, false, true, function()
  511. --      if ui.button('Default Skin') then
  512. --          applySkinToConnectedCar(0, true)
  513. --      end
  514. --      ui.sameLine()
  515. --      if ui.button('Crew Skin') then
  516. --          applySkinToConnectedCar(0, false)
  517. --      end
  518. --  end)
  519. -- end
  520.  
  521.  
  522. function script.drawUI()
  523.     fuelWarning()
  524.     if isAtGasStation() then
  525.         fillCarWithFuel()
  526.     end
  527. end
  528.  
  529. ac.onCarJumped(0, function()
  530.     physics.setCarFuel(0, math.max(1, carFuel))
  531. end)
  532.  
Add Comment
Please, Sign In to add comment