supinus

Untitled

Nov 12th, 2025
114
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 12.27 KB | Gaming | 0 0
  1. -- Custom Rectangular Nameplate for Assetto Corsa
  2. -- Shows driver name, country flag, ping, and SERVER-BASED input mode
  3.  
  4. local driverData = {}
  5. local serverInputModes = {} -- Store server input mode data
  6. local steamAvatarUrls = {} -- Store Steam avatar URLs
  7. local lastBroadcastReceived = 0
  8.  
  9. -- Online event to receive input mode broadcasts from server
  10. -- CRITICAL: Must specify the exact key that matches C# packet
  11. local inputModeBroadcastEvent = ac.OnlineEvent({
  12.     ac.StructItem.key("input_modes_broadcast"), -- MUST match C# packet key exactly
  13.     data = ac.StructItem.string(512)
  14. }, function(sender, message)
  15.     if message and message.data and message.data ~= "" then
  16.         ac.log("Nameplate: Received server input data: " .. message.data)
  17.         lastBroadcastReceived = os.clock()
  18.        
  19.         -- Clear and parse new data
  20.         serverInputModes = {}
  21.        
  22.         -- Parse format: "sessionId:inputMode;sessionId:inputMode;..."
  23.         for sessionData in string.gmatch(message.data, "([^;]+)") do
  24.             local sessionId, inputMode = string.match(sessionData, "(%d+):(%w+)")
  25.             if sessionId and inputMode then
  26.                 local sessionIdNum = tonumber(sessionId)
  27.                 serverInputModes[sessionIdNum] = inputMode
  28.                 ac.log("Nameplate: Server data - Session " .. sessionId .. " = " .. inputMode)
  29.             end
  30.         end
  31.     end
  32. end)
  33.  
  34. -- Online event to receive Steam avatar URLs from server
  35. local steamAvatarBroadcastEvent = ac.OnlineEvent({
  36.     ac.StructItem.key("steam_avatars_broadcast"), -- MUST match C# packet key exactly
  37.     data = ac.StructItem.string(1024) -- Increased size for URLs
  38. }, function(sender, message)
  39.     if message and message.data and message.data ~= "" then
  40.         ac.log("Nameplate: Received Steam avatar URLs: " .. message.data)
  41.        
  42.         -- Clear and parse new data
  43.         steamAvatarUrls = {}
  44.        
  45.         -- Parse format: "sessionId:avatarUrl;sessionId:avatarUrl;..."
  46.         for sessionData in string.gmatch(message.data, "([^;]+)") do
  47.             local sessionId, avatarUrl = string.match(sessionData, "(%d+):(.+)")
  48.             if sessionId and avatarUrl then
  49.                 local sessionIdNum = tonumber(sessionId)
  50.                 steamAvatarUrls[sessionIdNum] = avatarUrl
  51.                 ac.log("Nameplate: Steam avatar - Session " .. sessionId .. " = " .. avatarUrl)
  52.             end
  53.         end
  54.     end
  55. end)
  56.  
  57. -- Get input type from server data (preferred) or fallback to local detection
  58. function getInputType(car)
  59.     if not car then return "unknown" end
  60.    
  61.     -- Try to get session ID and use server data
  62.     local sessionId = car.sessionID
  63.     if sessionId and serverInputModes[sessionId] then
  64.         local serverInputMode = serverInputModes[sessionId]
  65.         ac.log("Nameplate: Using server data for session " .. sessionId .. ": " .. serverInputMode)
  66.         return serverInputMode
  67.     end
  68.    
  69.     -- Fallback to local detection if no server data available
  70.     local index = car.index or car.carIndex or 0
  71.     local steer = car.steer or 0
  72.     local gas = car.gas or 0
  73.     local brake = car.brake or 0
  74.     local currentType = "gamepad"
  75.     local steerAbs = math.abs(steer)
  76.    
  77.     if (steer == 0 or steerAbs > 0.95) and (gas == 0 or gas > 0.95) and (brake == 0 or brake > 0.95) then
  78.         currentType = "keyboard"
  79.     end
  80.     if steerAbs > 0.05 and steerAbs < 0.9 then
  81.         currentType = "wheel"
  82.     end
  83.  
  84.     -- Store last 5 readings for each player
  85.     driverData[index] = driverData[index] or {}
  86.     driverData[index].inputTypeHistory = driverData[index].inputTypeHistory or {}
  87.     table.insert(driverData[index].inputTypeHistory, currentType)
  88.     if #driverData[index].inputTypeHistory > 5 then
  89.         table.remove(driverData[index].inputTypeHistory, 1)
  90.     end
  91.  
  92.     -- Count most frequent
  93.     local count = { keyboard = 0, wheel = 0, gamepad = 0 }
  94.     for _, t in ipairs(driverData[index].inputTypeHistory) do
  95.         count[t] = (count[t] or 0) + 1
  96.     end
  97.     local detected = "gamepad"
  98.     local maxCount = 0
  99.     for k, v in pairs(count) do
  100.         if v > maxCount then
  101.             maxCount = v
  102.             detected = k
  103.         end
  104.     end
  105.     driverData[index].lastInputType = detected
  106.     return detected
  107. end
  108.  
  109. function drawInputIcon(inputType, x, y, alpha)
  110.     if inputType == "wheel" then
  111.         -- Use steering wheel image - bigger size (36x36) and white color
  112.         local success = pcall(function()
  113.             ui.drawImage(
  114.                 "https://i.imgur.com/OEENH81.png",
  115.                 vec2(x, y),
  116.                 vec2(x + 25, y + 25),
  117.                 rgbm(1.0, 1.0, 1.0, alpha)
  118.             )
  119.         end)
  120.         if not success then
  121.             ac.log("Nameplate: Failed to draw wheel icon from URL.")
  122.             ui.dwriteDrawText("🏎", 26, vec2(x, y), rgbm(0.2, 1.0, 0.2, alpha), 1, ui.Alignment.Left, ui.Alignment.Top)
  123.         end
  124.     elseif inputType == "keyboard" then
  125.         ui.dwriteDrawText("⌨", 26, vec2(x, y), rgbm(0.2, 0.8, 1.0, alpha), 1, ui.Alignment.Left, ui.Alignment.Top)
  126.     elseif inputType == "gamepad" then
  127.         -- Use controller image - bigger size (36x36) and white color
  128.         local success = pcall(function()
  129.             ui.drawImage(
  130.                 "https://i.ibb.co/3m56fyZy/gamepad.png",
  131.                 vec2(x, y),
  132.                 vec2(x + 36, y + 36),
  133.                 rgbm(1.0, 1.0, 1.0, alpha)
  134.             )
  135.         end)
  136.         if not success then
  137.             ac.log("Nameplate: Failed to draw gamepad icon from URL.")
  138.             ui.dwriteDrawText("🎮", 26, vec2(x, y), rgbm(1.0, 0.8, 0.2, alpha), 1, ui.Alignment.Left, ui.Alignment.Top)
  139.         end
  140.     else
  141.         -- Unknown - show question mark
  142.         ui.dwriteDrawText("?", 26, vec2(x, y), rgbm(0.7, 0.7, 0.7, alpha), 1, ui.Alignment.Left, ui.Alignment.Top)
  143.     end
  144. end
  145.  
  146. function drawCountryFlag(nationCode, x, y)
  147.     if nationCode and nationCode ~= "" then
  148.         -- Try to draw flag image
  149.         local success = pcall(function()
  150.             ui.drawImage(
  151.                 "/content/gui/flags/" .. nationCode .. ".png",
  152.                 vec2(x, y),
  153.                 vec2(x + 24, y + 18),
  154.                 rgbm(1, 1, 1, 1)
  155.             )
  156.         end)
  157.        
  158.         -- If flag fails, show country code
  159.         if not success then
  160.             ui.dwriteDrawText(nationCode, 16, vec2(x, y + 2), rgbm(0.8, 0.8, 1.0, 1), 1, ui.Alignment.Left, ui.Alignment.Top)
  161.         end
  162.     end
  163. end
  164.  
  165. function drawPingBars(x, y, ping, pingColor)
  166.     local barWidth = 6
  167.     local barHeight = 16
  168.     local barSpacing = 3
  169.     local maxBars = 4
  170.     local bars = 4
  171.    
  172.     if ping > 200 then
  173.         bars = 1
  174.     elseif ping > 150 then
  175.         bars = 2
  176.     elseif ping > 100 then
  177.         bars = 3
  178.     end
  179.    
  180.     for i = 1, maxBars do
  181.         local barX = x + (i - 1) * (barWidth + barSpacing)
  182.         local currentBarHeight = barHeight * (i / maxBars)
  183.         local currentY = y + barHeight - currentBarHeight
  184.         local alpha = (i <= bars) and 1.0 or 0.3
  185.         local color = rgbm(pingColor.r, pingColor.g, pingColor.b, alpha)
  186.         ui.drawRectFilled(vec2(barX, currentY), vec2(barX + barWidth, y + barHeight), color)
  187.     end
  188. end
  189.  
  190. ui.onDriverNameTag(function(car)
  191.     -- Clean the driver name properly
  192.     local rawName = tostring(car.driverName or "unknown")
  193.     local name = rawName:gsub("0x[%x]+", ""):gsub("[^%w%s*%-%.%@]", "")
  194.     name = name:gsub("^%s*(.-)%s*$", "%1")
  195.    
  196.     -- Skip AI traffic cars - only show input modes for real players
  197.     local isRealPlayer = car.isConnected and not car.isAIControlled
  198.     if not isRealPlayer then
  199.         -- For AI cars, just show name without input mode
  200.         ui.pushDWriteFont("Berlin Sans FB")
  201.         ui.drawRectFilled(vec2(-90, 0), vec2(470, 36), rgbm(0, 0, 0, 0.0), 8)
  202.         ui.dwriteDrawText(name, 21, vec2(-80, 6), rgbm(0.8, 0.8, 0.8, 1), 1, ui.Alignment.Left, ui.Alignment.Top)
  203.         ui.popDWriteFont()
  204.         return
  205.     end
  206.    
  207.     local ping = car.ping or car.pingMs or 0
  208.     local pingColor = rgbm(0.2, 1.0, 0.2, 1.0)
  209.     if ping > 250 then
  210.         pingColor = rgbm(1.0, 0.2, 0.2, 1.0)
  211.     elseif ping > 120 then
  212.         pingColor = rgbm(1.0, 1.0, 0.2, 1.0)
  213.     end
  214.    
  215.     -- Get nation code
  216.     local nationCode = ""
  217.     if car.driverNationCode and car.driverNationCode ~= "" then
  218.         nationCode = string.upper(tostring(car.driverNationCode))
  219.     elseif car.nationality and car.nationality ~= "" then
  220.         nationCode = string.upper(tostring(car.nationality))
  221.     else
  222.         nationCode = "ITA"
  223.     end
  224.     local speed = (car.speedKmh or 0)
  225.     local speedText = string.format("%d KM/H", speed)
  226.  
  227.     ui.pushDWriteFont("Berlin Sans FB")
  228.    
  229.     -- Main background - MADE TRANSPARENT to avoid overlap
  230.     ui.drawRectFilled(vec2(-90, 0), vec2(470, 36), rgbm(0, 0, 0, 0.0), 8)
  231.    
  232.     -- Driver name (leftmost)
  233.     ui.dwriteDrawText(name, 21, vec2(-80, 6), rgbm(1,1,1,1), 1, ui.Alignment.Left, ui.Alignment.Top)
  234.  
  235.     -- Layout: Name | Input | Flag | Ping Bars  
  236.     local inputX = 322      -- Input icon position (moved further right to avoid name overlap)
  237.     local flagX = 320       -- Flag position (moved right to make room for input)  
  238.     local barsX = 320       -- Ping bars position (moved right)
  239.     local barsY = 18
  240.  
  241.     -- Input icon (moved further right and better vertical centering) - only for real players
  242.     local inputType = getInputType(car)
  243.     drawInputIcon(inputType, inputX, 35, 1.0)  -- Lower vertical position for better centering
  244.  
  245.     -- Country flag
  246.     drawCountryFlag(nationCode, flagX, 9)
  247.  
  248.     -- Ping bars (rightmost)
  249.     drawPingBars(barsX, barsY, ping, pingColor)
  250.  
  251.     -- رقم البنق يمين الشارة (نفس فكرة السكربت الثاني)
  252.     ui.dwriteDrawText(tostring(ping).."ms", 26, vec2(barsX + 40, 8), pingColor, 1, ui.Alignment.Left, ui.Alignment.Top)
  253.  
  254.     -- Steam Avatar (right of ping)
  255.     local avatarUrl = steamAvatarUrls[car.sessionID]
  256.     if avatarUrl then
  257.         local avatarX = barsX + -321 -- Adjust X position relative to ping bars
  258.         local avatarY = 2 -- Adjust Y position to align with ping text
  259.         local avatarSize = 60 -- Adjust size as needed (e.g., 24x24)
  260.         local success = pcall(function()
  261.             ui.drawImage(
  262.                 avatarUrl,
  263.                 vec2(avatarX, avatarY),
  264.                 vec2(avatarX + avatarSize, avatarY + avatarSize),
  265.                 rgbm(1, 1, 1, 1)
  266.             )
  267.         end)
  268.         if not success then
  269.             ac.log("Nameplate: Failed to draw avatar from URL: " .. avatarUrl)
  270.         end
  271.     end
  272.    
  273.     -- الشريط الأزرق للسرعة
  274.     local minSpeed, maxSpeed = 0, 260
  275.     local speedBoxX = -90
  276.     local speedBoxWMax = 560
  277.     local speedBoxH = 17
  278.     local speedBoxY = 46
  279.     local t = math.max(0, math.min(1, (speed-minSpeed)/(maxSpeed-minSpeed)))
  280.     local minWidth = 15
  281.     local speedBoxW = minWidth + (speedBoxWMax - minWidth) * t
  282.  
  283.     ui.drawRectFilled(vec2(speedBoxX, speedBoxY), vec2(speedBoxX + speedBoxW, speedBoxY + speedBoxH), rgbm(0.1, 0.45, 0.95, 0.89), 7)
  284.     ui.dwriteDrawText(speedText, 18, vec2(speedBoxX + speedBoxWMax/2, speedBoxY - 1), rgbm(1,1,1,1), 1, ui.Alignment.Center, ui.Alignment.Top)
  285.     ui.popDWriteFont()
  286.    
  287.     -- Debug info in console every few seconds - only for real players
  288.     if math.fmod(os.clock(), 5) < 0.1 then
  289.         local sessionId = car.sessionID or "unknown"
  290.         ac.log("Nameplate Debug: Player " .. name .. " - Session: " .. sessionId .. " - Input: " .. inputType .. " - Server data available: " .. tostring(serverInputModes[sessionId] ~= nil))
  291.     end
  292. end)
  293.  
  294. -- Debug logging
  295. ac.log("Nameplate: Enhanced script loaded with server input mode integration!")
  296.  
  297. -- Periodic debug info
  298. local lastDebugTime = 0
  299. function script.update(dt)
  300.     local currentTime = os.clock()
  301.     if currentTime - lastDebugTime > 10 then -- Every 10 seconds
  302.         lastDebugTime = currentTime
  303.         local serverDataCount = 0
  304.         for _ in pairs(serverInputModes) do
  305.             serverDataCount = serverDataCount + 1
  306.         end
  307.         ac.log("Nameplate: Server input modes tracked: " .. serverDataCount .. " | Last broadcast: " ..
  308.                (lastBroadcastReceived > 0 and string.format("%.1fs ago", currentTime - lastDebugTime) or "never"))
  309.     end
  310. end
Add Comment
Please, Sign In to add comment