FaiSvl

chat h

Oct 4th, 2025
138
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 14.49 KB | None | 0 0
  1. --[[
  2.     Triple 6ix Chat App
  3.     Simple CSP Lua App for Assetto Corsa
  4.    
  5.     Features:
  6.     - Modern chat interface with drag & resize
  7.     - Player colors and typing indicators  
  8.     - H hotkey to show/hide
  9.     - Auto-hide when inactive
  10.     - Arabic language support
  11.    
  12.     Installation:
  13.     1. Save as chat_app.lua
  14.     2. Place in content/gui/ or apps/lua/
  15.     3. Add to CSPExtraOptions in extra_cfg.yml
  16.     4. Players activate from CSP apps menu
  17. ]]
  18.  
  19. -- إعدادات التطبيق
  20. local config = {
  21.     windowTitle = "💬 Triple 6ix Chat",
  22.     defaultWidth = 450,
  23.     defaultHeight = 350,
  24.     minWidth = 300,
  25.     minHeight = 200,
  26.     maxWidth = 700,
  27.     maxHeight = 600,
  28.     backgroundColor = rgbm(0.08, 0.08, 0.12, 0.95),
  29.     headerColor = rgbm(0.15, 0.15, 0.25, 1.0),
  30.     textColor = rgbm(1, 1, 1, 1),
  31.     systemColor = rgbm(0.7, 0.9, 1.0, 1),
  32.     maxMessages = 100,
  33.     autoHideDelay = 15, -- ثواني
  34.     fadeSpeed = 3.0,
  35.     fontSize = 14,
  36.     headerHeight = 35
  37. }
  38.  
  39. -- متغيرات الحالة
  40. local chatState = {
  41.     visible = true,
  42.     position = vec2(100, 150),
  43.     size = vec2(config.defaultWidth, config.defaultHeight),
  44.     isDragging = false,
  45.     isResizing = false,
  46.     dragOffset = vec2(0, 0),
  47.     lastActivity = 0,
  48.     alpha = 1.0,
  49.     inputText = "",
  50.     messages = {},
  51.     playerColors = {},
  52.     typingPlayers = {},
  53.     scrollOffset = 0,
  54.     maxScroll = 0,
  55.     isInputFocused = false
  56. }
  57.  
  58. -- ألوان اللاعبين المحسنة
  59. local playerColorPalette = {
  60.     rgbm(1.0, 0.5, 0.5, 1),   -- أحمر فاتح
  61.     rgbm(0.5, 1.0, 0.5, 1),   -- أخضر فاتح  
  62.     rgbm(0.5, 0.7, 1.0, 1),   -- أزرق فاتح
  63.     rgbm(1.0, 1.0, 0.6, 1),   -- أصفر فاتح
  64.     rgbm(1.0, 0.6, 1.0, 1),   -- وردي فاتح
  65.     rgbm(0.6, 1.0, 1.0, 1),   -- سماوي فاتح
  66.     rgbm(1.0, 0.8, 0.5, 1),   -- برتقالي فاتح
  67.     rgbm(0.8, 0.6, 1.0, 1),   -- بنفسجي فاتح
  68.     rgbm(0.9, 0.9, 0.5, 1),   -- ذهبي
  69.     rgbm(0.6, 0.9, 0.7, 1)    -- أخضر نعناعي
  70. }
  71.  
  72. -- دالة للحصول على لون اللاعب
  73. local function getPlayerColor(playerName)
  74.     if not chatState.playerColors[playerName] then
  75.         local hash = 0
  76.         for i = 1, #playerName do
  77.             hash = hash + string.byte(playerName, i)
  78.         end
  79.         local colorIndex = (hash % #playerColorPalette) + 1
  80.         chatState.playerColors[playerName] = playerColorPalette[colorIndex]
  81.     end
  82.     return chatState.playerColors[playerName]
  83. end
  84.  
  85. -- دالة لإضافة رسالة
  86. local function addMessage(playerName, message, isSystem)
  87.     if #chatState.messages >= config.maxMessages then
  88.         table.remove(chatState.messages, 1)
  89.     end
  90.    
  91.     table.insert(chatState.messages, {
  92.         player = playerName,
  93.         message = message,
  94.         color = isSystem and config.systemColor or getPlayerColor(playerName),
  95.         timestamp = os.time(),
  96.         isSystem = isSystem or false
  97.     })
  98.    
  99.     chatState.lastActivity = os.clock()
  100.     chatState.scrollOffset = 0 -- التمرير للأسفل عند رسالة جديدة
  101.    
  102.     -- حساب الحد الأقصى للتمرير
  103.     local messageHeight = 22
  104.     local visibleArea = chatState.size.y - config.headerHeight - 60
  105.     local totalHeight = #chatState.messages * messageHeight
  106.     chatState.maxScroll = math.max(0, totalHeight - visibleArea)
  107. end
  108.  
  109. -- دالة للتحقق من النقر داخل منطقة
  110. local function isPointInRect(point, rectPos, rectSize)
  111.     return point.x >= rectPos.x and point.x <= rectPos.x + rectSize.x and
  112.            point.y >= rectPos.y and point.y <= rectPos.y + rectSize.y
  113. end
  114.  
  115. -- دالة رسم الشات المحسنة
  116. local function drawChat()
  117.     if not chatState.visible then return end
  118.    
  119.     local currentTime = os.clock()
  120.    
  121.     -- تلاشي تلقائي
  122.     if currentTime - chatState.lastActivity > config.autoHideDelay then
  123.         chatState.alpha = math.max(0.3, chatState.alpha - config.fadeSpeed * ac.getDeltaT())
  124.     else
  125.         chatState.alpha = math.min(1, chatState.alpha + config.fadeSpeed * ac.getDeltaT())
  126.     end
  127.    
  128.     -- رسم الخلفية الرئيسية مع حواف مدورة
  129.     local bgColor = rgbm(config.backgroundColor.r, config.backgroundColor.g, config.backgroundColor.b, config.backgroundColor.a * chatState.alpha)
  130.     ui.drawRectFilled(chatState.position, chatState.position + chatState.size, bgColor, 12)
  131.    
  132.     -- رسم حدود النافذة
  133.     ui.drawRect(chatState.position, chatState.position + chatState.size, rgbm(0.3, 0.3, 0.4, chatState.alpha), 12, 2)
  134.    
  135.     -- رسم شريط العنوان المحسن
  136.     local headerColor = rgbm(config.headerColor.r, config.headerColor.g, config.headerColor.b, config.headerColor.a * chatState.alpha)
  137.     ui.drawRectFilled(chatState.position, chatState.position + vec2(chatState.size.x, config.headerHeight), headerColor, 12)
  138.    
  139.     -- رسم عنوان النافذة مع أيقونة
  140.     local titleColor = rgbm(config.textColor.r, config.textColor.g, config.textColor.b, config.textColor.a * chatState.alpha)
  141.     ui.drawText(config.windowTitle, chatState.position + vec2(12, 10), 16, titleColor)
  142.    
  143.     -- رسم أزرار التحكم
  144.     local buttonSize = 20
  145.     local buttonY = chatState.position.y + 7
  146.    
  147.     -- زر الإغلاق
  148.     local closeButtonPos = vec2(chatState.position.x + chatState.size.x - 30, buttonY)
  149.     ui.drawRectFilled(closeButtonPos, closeButtonPos + vec2(buttonSize, buttonSize), rgbm(0.9, 0.3, 0.3, chatState.alpha), 4)
  150.     ui.drawText("×", closeButtonPos + vec2(6, 2), 14, titleColor)
  151.    
  152.     -- زر تصغير
  153.     local minimizeButtonPos = vec2(chatState.position.x + chatState.size.x - 55, buttonY)
  154.     ui.drawRectFilled(minimizeButtonPos, minimizeButtonPos + vec2(buttonSize, buttonSize), rgbm(0.5, 0.5, 0.5, chatState.alpha), 4)
  155.     ui.drawText("−", minimizeButtonPos + vec2(6, 2), 14, titleColor)
  156.    
  157.     -- منطقة الرسائل
  158.     local messagesArea = {
  159.         pos = chatState.position + vec2(8, config.headerHeight + 8),
  160.         size = vec2(chatState.size.x - 16, chatState.size.y - config.headerHeight - 50)
  161.     }
  162.    
  163.     -- رسم خلفية منطقة الرسائل
  164.     ui.drawRectFilled(messagesArea.pos, messagesArea.pos + messagesArea.size, rgbm(0.05, 0.05, 0.08, chatState.alpha), 6)
  165.    
  166.     -- رسم الرسائل مع تحسينات
  167.     local messageHeight = 22
  168.     local startY = messagesArea.pos.y + messagesArea.size.y - messageHeight + chatState.scrollOffset
  169.    
  170.     for i = #chatState.messages, 1, -1 do
  171.         local msg = chatState.messages[i]
  172.         local messageY = startY - (i - 1) * messageHeight
  173.        
  174.         if messageY >= messagesArea.pos.y - messageHeight and messageY <= messagesArea.pos.y + messagesArea.size.y then
  175.             local msgColor = rgbm(msg.color.r, msg.color.g, msg.color.b, msg.color.a * chatState.alpha)
  176.            
  177.             -- رسم خلفية الرسالة (متناوبة)
  178.             if i % 2 == 0 then
  179.                 ui.drawRectFilled(vec2(messagesArea.pos.x, messageY), vec2(messagesArea.pos.x + messagesArea.size.x, messageY + messageHeight), rgbm(0.1, 0.1, 0.15, chatState.alpha * 0.3), 3)
  180.             end
  181.            
  182.             -- رسم اسم اللاعب مع تنسيق محسن
  183.             local playerText = msg.isSystem and "🔹 " .. msg.player or msg.player .. ":"
  184.             ui.drawText(playerText, vec2(messagesArea.pos.x + 8, messageY + 2), config.fontSize, msgColor)
  185.            
  186.             -- رسم الرسالة
  187.             local textColor = rgbm(config.textColor.r, config.textColor.g, config.textColor.b, config.textColor.a * chatState.alpha)
  188.             local messageX = msg.isSystem and messagesArea.pos.x + 8 or messagesArea.pos.x + 100
  189.             ui.drawText(msg.message, vec2(messageX, messageY + 2), config.fontSize, textColor)
  190.            
  191.             -- رسم الوقت (صغير)
  192.             local timeStr = os.date("%H:%M", msg.timestamp)
  193.             local timeColor = rgbm(0.6, 0.6, 0.6, chatState.alpha * 0.7)
  194.             ui.drawText(timeStr, vec2(messagesArea.pos.x + messagesArea.size.x - 40, messageY + 2), 10, timeColor)
  195.         end
  196.     end
  197.    
  198.     -- رسم مؤشر الكتابة المحسن
  199.     if next(chatState.typingPlayers) then
  200.         local typingText = "💭 يكتب..."
  201.         local typingColor = rgbm(0.4, 0.9, 0.6, chatState.alpha)
  202.         ui.drawText(typingText, messagesArea.pos + vec2(8, messagesArea.size.y - 20), 12, typingColor)
  203.     end
  204.    
  205.     -- رسم شريط التمرير
  206.     if chatState.maxScroll > 0 then
  207.         local scrollBarX = messagesArea.pos.x + messagesArea.size.x - 8
  208.         local scrollBarHeight = messagesArea.size.y
  209.         local scrollThumbHeight = math.max(20, scrollBarHeight * (messagesArea.size.y / (chatState.maxScroll + messagesArea.size.y)))
  210.         local scrollThumbY = messagesArea.pos.y + (scrollBarHeight - scrollThumbHeight) * (chatState.scrollOffset / chatState.maxScroll)
  211.        
  212.         ui.drawRectFilled(vec2(scrollBarX, scrollThumbY), vec2(scrollBarX + 4, scrollThumbY + scrollThumbHeight), rgbm(0.5, 0.5, 0.5, chatState.alpha), 2)
  213.     end
  214.    
  215.     -- رسم مقبض تغيير الحجم المحسن
  216.     local resizeHandlePos = chatState.position + chatState.size - vec2(20, 20)
  217.     local resizeHandleSize = vec2(20, 20)
  218.     ui.drawText("⋱", resizeHandlePos + vec2(4, 2), 14, rgbm(0.6, 0.6, 0.6, chatState.alpha))
  219.    
  220.     -- رسم معلومات الحالة
  221.     local statusText = string.format("الرسائل: %d | اللاعبين: %d", #chatState.messages, ac.getConnectedCarsCount())
  222.     local statusColor = rgbm(0.5, 0.5, 0.5, chatState.alpha * 0.8)
  223.     ui.drawText(statusText, chatState.position + vec2(8, chatState.size.y - 15), 10, statusColor)
  224. end
  225.  
  226. -- دالة معالجة الإدخال المحسنة
  227. local function handleInput()
  228.     local mousePos = ui.mousePos()
  229.     local mouseDown = ui.mouseDown()
  230.     local mouseClicked = ui.mouseClicked()
  231.    
  232.     if not chatState.visible or chatState.alpha <= 0.1 then return end
  233.    
  234.     -- تحديث النشاط عند التفاعل
  235.     if mouseClicked and isPointInRect(mousePos, chatState.position, chatState.size) then
  236.         chatState.lastActivity = os.clock()
  237.     end
  238.    
  239.     -- التحقق من النقر على زر الإغلاق
  240.     local closeButtonPos = vec2(chatState.position.x + chatState.size.x - 30, chatState.position.y + 7)
  241.     local closeButtonSize = vec2(20, 20)
  242.     if mouseClicked and isPointInRect(mousePos, closeButtonPos, closeButtonSize) then
  243.         chatState.visible = false
  244.         return
  245.     end
  246.    
  247.     -- التحقق من النقر على زر التصغير
  248.     local minimizeButtonPos = vec2(chatState.position.x + chatState.size.x - 55, chatState.position.y + 7)
  249.     if mouseClicked and isPointInRect(mousePos, minimizeButtonPos, closeButtonSize) then
  250.         chatState.alpha = chatState.alpha > 0.5 and 0.3 or 1.0
  251.         chatState.lastActivity = os.clock()
  252.         return
  253.     end
  254.    
  255.     -- التحقق من السحب
  256.     local headerArea = {pos = chatState.position, size = vec2(chatState.size.x - 60, config.headerHeight)}
  257.     if mouseClicked and isPointInRect(mousePos, headerArea.pos, headerArea.size) then
  258.         chatState.isDragging = true
  259.         chatState.dragOffset = mousePos - chatState.position
  260.     elseif not mouseDown then
  261.         chatState.isDragging = false
  262.     end
  263.    
  264.     if chatState.isDragging and mouseDown then
  265.         chatState.position = mousePos - chatState.dragOffset
  266.         chatState.lastActivity = os.clock()
  267.     end
  268.    
  269.     -- التحقق من تغيير الحجم
  270.     local resizeHandlePos = chatState.position + chatState.size - vec2(20, 20)
  271.     local resizeHandleSize = vec2(20, 20)
  272.     if mouseClicked and isPointInRect(mousePos, resizeHandlePos, resizeHandleSize) then
  273.         chatState.isResizing = true
  274.     elseif not mouseDown then
  275.         chatState.isResizing = false
  276.     end
  277.    
  278.     if chatState.isResizing and mouseDown then
  279.         local newSize = mousePos - chatState.position
  280.         chatState.size.x = math.max(config.minWidth, math.min(config.maxWidth, newSize.x))
  281.         chatState.size.y = math.max(config.minHeight, math.min(config.maxHeight, newSize.y))
  282.         chatState.lastActivity = os.clock()
  283.     end
  284.    
  285.     -- التمرير بالعجلة المحسن
  286.     if isPointInRect(mousePos, chatState.position, chatState.size) then
  287.         local scroll = ui.mouseWheel()
  288.         if scroll ~= 0 then
  289.             chatState.scrollOffset = chatState.scrollOffset + scroll * 30
  290.             chatState.scrollOffset = math.max(-chatState.maxScroll, math.min(0, chatState.scrollOffset))
  291.             chatState.lastActivity = os.clock()
  292.         end
  293.     end
  294. end
  295.  
  296. -- دوال الأحداث
  297. function script.update(dt)
  298.     -- تحديث حالة الكتابة
  299.     for player, timer in pairs(chatState.typingPlayers) do
  300.         chatState.typingPlayers[player] = timer - dt
  301.         if chatState.typingPlayers[player] <= 0 then
  302.             chatState.typingPlayers[player] = nil
  303.         end
  304.     end
  305. end
  306.  
  307. function script.drawUI()
  308.     drawChat()
  309.     handleInput()
  310. end
  311.  
  312. -- استقبال الرسائل من السيرفر
  313. function script.onChatMessage(message, authorId, authorName)
  314.     addMessage(authorName, message, false)
  315. end
  316.  
  317. -- استقبال إشعارات الكتابة
  318. function script.onChatInput(text)
  319.     if text and text ~= "" then
  320.         chatState.typingPlayers["أنت"] = 3.0 -- مؤقت الكتابة
  321.     end
  322. end
  323.  
  324. -- دوال الأحداث للاعبين
  325. function script.onPlayerConnected(playerId, playerName)
  326.     addMessage("النظام", playerName .. " انضم إلى السيرفر 🎉", true)
  327. end
  328.  
  329. function script.onPlayerDisconnected(playerId, playerName)
  330.     addMessage("النظام", playerName .. " غادر السيرفر 👋", true)
  331.     chatState.playerColors[playerName] = nil
  332. end
  333.  
  334. -- تفعيل الشات بمفتاح H
  335. function script.onKeyPressed(key)
  336.     if key == ui.Key.H then
  337.         chatState.visible = not chatState.visible
  338.         chatState.lastActivity = os.clock()
  339.         chatState.alpha = 1.0
  340.     end
  341. end
  342.  
  343. -- تهيئة التطبيق
  344. function script.onInit()
  345.     addMessage("النظام", "🚀 مرحباً بك في شات Triple 6ix Chat المتطور!", true)
  346.     addMessage("النظام", "💡 اضغط H لإظهار/إخفاء الشات", true)
  347.     addMessage("النظام", "🎮 يمكنك سحب النافذة وتغيير حجمها", true)
  348.     chatState.lastActivity = os.clock()
  349. end
  350.  
Advertisement
Add Comment
Please, Sign In to add comment