local isRDR = not TerraingridActivate and true or false local chatInputActive = false local chatInputActivating = false local chatLoaded = false RegisterNetEvent('chatMessage') RegisterNetEvent('chat:addTemplate') RegisterNetEvent('chat:addMessage') RegisterNetEvent('chat:addSuggestion') RegisterNetEvent('chat:addSuggestions') RegisterNetEvent('chat:addMode') RegisterNetEvent('chat:removeMode') RegisterNetEvent('chat:removeSuggestion') RegisterNetEvent('chat:clear') -- internal events RegisterNetEvent('__cfx_internal:serverPrint') RegisterNetEvent('_chat:messageEntered') --deprecated, use chat:addMessage AddEventHandler('chatMessage', function(author, color, text) local args = { text } if author ~= "" then table.insert(args, 1, author) end SendNUIMessage({ type = 'ON_MESSAGE', message = { color = color, multiline = true, args = args } }) end) AddEventHandler('__cfx_internal:serverPrint', function(msg) print(msg) SendNUIMessage({ type = 'ON_MESSAGE', message = { templateId = 'print', multiline = true, args = { msg }, mode = '_global' } }) end) -- addMessage local addMessage = function(message) if type(message) == 'string' then message = { args = { message } } end SendNUIMessage({ type = 'ON_MESSAGE', message = message }) end exports('addMessage', addMessage) AddEventHandler('chat:addMessage', addMessage) -- addSuggestion local addSuggestion = function(name, help, params) SendNUIMessage({ type = 'ON_SUGGESTION_ADD', suggestion = { name = name, help = help, params = params or nil } }) end exports('addSuggestion', addSuggestion) AddEventHandler('chat:addSuggestion', addSuggestion) AddEventHandler('chat:addSuggestions', function(suggestions) for _, suggestion in ipairs(suggestions) do SendNUIMessage({ type = 'ON_SUGGESTION_ADD', suggestion = suggestion }) end end) AddEventHandler('chat:removeSuggestion', function(name) SendNUIMessage({ type = 'ON_SUGGESTION_REMOVE', name = name }) end) AddEventHandler('chat:addMode', function(mode) SendNUIMessage({ type = 'ON_MODE_ADD', mode = mode }) end) AddEventHandler('chat:removeMode', function(name) SendNUIMessage({ type = 'ON_MODE_REMOVE', name = name }) end) AddEventHandler('chat:addTemplate', function(id, html) SendNUIMessage({ type = 'ON_TEMPLATE_ADD', template = { id = id, html = html } }) end) AddEventHandler('chat:clear', function(name) SendNUIMessage({ type = 'ON_CLEAR' }) end) RegisterNUICallback('chatResult', function(data, cb) chatInputActive = false SetNuiFocus(false) if not data.canceled then local id = PlayerId() --deprecated local r, g, b = 0, 0x99, 255 if data.message:sub(1, 1) == '/' then ExecuteCommand(data.message:sub(2)) else TriggerServerEvent('_chat:messageEntered', GetPlayerName(id), { r, g, b }, data.message, data.mode) end end cb('ok') end) local function refreshCommands() if GetRegisteredCommands then local registeredCommands = GetRegisteredCommands() local suggestions = {} for _, command in ipairs(registeredCommands) do if IsAceAllowed(('command.%s'):format(command.name)) and command.name ~= 'toggleChat' then table.insert(suggestions, { name = '/' .. command.name, help = '' }) end end TriggerEvent('chat:addSuggestions', suggestions) end end local function refreshThemes() local themes = {} for resIdx = 0, GetNumResources() - 1 do local resource = GetResourceByFindIndex(resIdx) if GetResourceState(resource) == 'started' then local numThemes = GetNumResourceMetadata(resource, 'chat_theme') if numThemes > 0 then local themeName = GetResourceMetadata(resource, 'chat_theme') local themeData = json.decode(GetResourceMetadata(resource, 'chat_theme_extra') or 'null') if themeName and themeData then themeData.baseUrl = 'nui://' .. resource .. '/' themes[themeName] = themeData end end end end SendNUIMessage({ type = 'ON_UPDATE_THEMES', themes = themes }) end AddEventHandler('onClientResourceStart', function(resName) Wait(500) refreshCommands() refreshThemes() end) AddEventHandler('onClientResourceStop', function(resName) Wait(500) refreshCommands() refreshThemes() end) RegisterNUICallback('loaded', function(data, cb) TriggerServerEvent('chat:init') refreshCommands() refreshThemes() chatLoaded = true cb('ok') end) local CHAT_HIDE_STATES = { SHOW_WHEN_ACTIVE = 0, ALWAYS_SHOW = 1, ALWAYS_HIDE = 2 } local kvpEntry = GetResourceKvpString('hideState') local chatHideState = kvpEntry and tonumber(kvpEntry) or CHAT_HIDE_STATES.SHOW_WHEN_ACTIVE local isFirstHide = true if not isRDR then if RegisterKeyMapping then RegisterKeyMapping('toggleChat', 'Toggle chat', 'keyboard', 'l') end RegisterCommand('toggleChat', function() if chatHideState == CHAT_HIDE_STATES.SHOW_WHEN_ACTIVE then chatHideState = CHAT_HIDE_STATES.ALWAYS_SHOW elseif chatHideState == CHAT_HIDE_STATES.ALWAYS_SHOW then chatHideState = CHAT_HIDE_STATES.ALWAYS_HIDE elseif chatHideState == CHAT_HIDE_STATES.ALWAYS_HIDE then chatHideState = CHAT_HIDE_STATES.SHOW_WHEN_ACTIVE end isFirstHide = false SetResourceKvp('hideState', tostring(chatHideState)) end, false) end Citizen.CreateThread(function() SetTextChatEnabled(false) SetNuiFocus(false) local lastChatHideState = -1 local origChatHideState = -1 while true do Wait(0) if not chatInputActive then if IsControlPressed(0, isRDR and `INPUT_MP_TEXT_CHAT_ALL` or 245) --[[ INPUT_MP_TEXT_CHAT_ALL ]] then chatInputActive = true chatInputActivating = true SendNUIMessage({ type = 'ON_OPEN' }) end end if chatInputActivating then if not IsControlPressed(0, isRDR and `INPUT_MP_TEXT_CHAT_ALL` or 245) then SetNuiFocus(true) chatInputActivating = false end end if chatLoaded then local forceHide = IsScreenFadedOut() or IsPauseMenuActive() local wasForceHide = false if chatHideState ~= CHAT_HIDE_STATES.ALWAYS_HIDE then if forceHide then origChatHideState = chatHideState chatHideState = CHAT_HIDE_STATES.ALWAYS_HIDE end elseif not forceHide and origChatHideState ~= -1 then chatHideState = origChatHideState origChatHideState = -1 wasForceHide = true end if chatHideState ~= lastChatHideState then lastChatHideState = chatHideState SendNUIMessage({ type = 'ON_SCREEN_STATE_CHANGE', hideState = chatHideState, fromUserInteraction = not forceHide and not isFirstHide and not wasForceHide }) isFirstHide = false end end end end)