Commands#
Text UI registers no chat or console commands. Prompts are created by your scripts through the exports below, or placed in shared/config.lua — see Usage.
Client Exports#
Every export is client-side. To show a prompt from the server, trigger a client event of your own and call the export there.
Export | Does |
|---|---|
| Shows the on-screen prompt. |
| Changes the text of the on-screen prompt in place. |
| Hides the on-screen prompt. |
| Pins a prompt to a coordinate, with its own distances, key, event, job and condition. |
| Changes the text of a 3D prompt. |
| Removes a 3D prompt. |
| A quick display-only 3D label with fixed distances. |
| A prompt that follows another player, with up to two keys. |
| Removes a prompt on a player. |
On-screen prompt — displayTextUI, changeText, hideTextUI
-- displayTextUI(text, key?, align?)
exports['qs-textui']:displayTextUI('Open the garage', 'E', 'center-right')
-- changeText(text, key?) — new text without sliding in again
exports['qs-textui']:changeText('Close the garage', 'E')
exports['qs-textui']:hideTextUI()Parameter | Default | Meaning |
|---|---|---|
| — | The text. GTA codes and HTML allowed. |
|
| The label on the badge. Display only — your script handles the keypress. |
|
| One of the nine screen positions. An unknown value falls back to the default. |
There is one on-screen prompt at a time: a second displayTextUI replaces the first. Show it when the player enters the zone and hide it when they leave — not on every frame:
local garage = vector3(215.8, -810.1, 30.7)
local shown = false
CreateThread(function()
while true do
local near = #(GetEntityCoords(PlayerPedId()) - garage) < 3.0
if near and not shown then
exports['qs-textui']:displayTextUI('Open the garage', 'E')
shown = true
elseif not near and shown then
exports['qs-textui']:hideTextUI()
shown = false
end
if near and IsControlJustReleased(0, 38) then
-- open your garage here
end
Wait(near and 0 or 500)
end
end)3D text at a coordinate — create3DTextUI, update3DTextUI, DeleteDrawText3D
A prompt floating over a point, with its own distances and an event fired when its key is pressed. Call it once — when your script starts, or when the thing it points at appears — not in a loop.
exports['qs-textui']:create3DTextUI('pd_duty', {
coords = vector3(441.8, -982.1, 30.7),
displayDist = 8.0,
interactDist = 2.0,
enableKeyClick = true,
keyNum = 38,
key = 'E',
text = 'Go on duty',
job = 'police',
canInteract = function()
return not IsPedInAnyVehicle(PlayerPedId(), false)
end,
triggerData = {
triggerName = 'my-police:server:toggleDuty',
args = { station = 'mrpd' },
isServer = true
},
invokingResource = GetCurrentResourceName()
})
exports['qs-textui']:update3DTextUI('pd_duty', 'Go off duty')
exports['qs-textui']:DeleteDrawText3D('pd_duty')Field | Required | Meaning |
|---|---|---|
| Yes | A |
| Yes | Distance at which the marker appears. |
| Yes | Distance at which the text shows and the key works. |
| Yes | The label on the badge — also for a prompt with no key. |
| Yes | The text. GTA codes and HTML allowed. |
| No | Control id of the key (38 = E). Without it, the prompt only informs. |
| No | Leave it |
| No | The event fired on the keypress: |
| No |
|
| No | A function. Return |
| No | A resource name. When that resource stops, the prompt is removed. Pass |
The event arrives with args as its only parameter:
-- server side, because isServer = true
RegisterNetEvent('my-police:server:toggleDuty', function(args)
local src = source
print(src, args.station)
end)An id that already exists is ignored, so calling create3DTextUI twice with the same id keeps the first. To change anything but the text, delete it and create it again. update3DTextUI changes only the text, and does nothing if the text is the same.
Quick 3D label — drawText3D
-- drawText3D(x, y, z, text, id, key)
exports['qs-textui']:drawText3D(100.0, 200.0, 30.0, 'Warehouse', 'warehouse_label', 'E')
exports['qs-textui']:DeleteDrawText3D('warehouse_label')A display-only label with fixed distances — a marker from 5 m, the text from 2 m — and no keypress of its own: the calling script checks the key itself. Calling it again with the same id moves the label and updates its text, so it is safe to call on every frame. It is what the DrawText3D wrapper uses.
Labels from drawText3D belong to the resource that created them and are removed when it stops. The same call is available as a client event:
TriggerEvent('textui:DrawText3D', x, y, z, text, id, key)3D text on a player — create3DTextUIOnPlayers, DeleteDrawText3DOnPlayers
A prompt that floats over another player and follows them. It takes a second key and a second event, for two actions on the same person:
local promptId = 'cuffed_' .. targetServerId
exports['qs-textui']:create3DTextUIOnPlayers(promptId, {
id = targetServerId, -- server id of the player to follow
displayDist = 6.0,
interactDist = 2.0,
enableKeyClick = true,
key = 'E',
text = 'Search · ~c~[G] Escort',
keyNum = 38,
triggerData = {
triggerName = 'my-police:client:search',
args = { target = targetServerId }
},
keyNum2 = 47,
triggerData2 = {
triggerName = 'my-police:client:escort',
args = { target = targetServerId }
}
})
exports['qs-textui']:DeleteDrawText3DOnPlayers(promptId)Field | Meaning |
|---|---|
| Server id of the player the prompt follows. |
| Must be |
| The first key and its event. |
| Optional second key and its event. |
| As in |
The badge shows one key, so mention the second one in the text, as above. Player prompts are not filtered by job — decide who gets one before creating it — and they are not removed when your resource stops, so delete them yourself.
When qs-textui Restarts#
Prompts created through exports live in Text UI's memory. Restarting qs-textui clears them, while the scripts that created them keep running. Either restart those scripts too, or create your prompts again whenever Text UI starts:
local function createPrompts()
exports['qs-textui']:create3DTextUI('pd_duty', { --[[ ... ]] })
end
CreateThread(createPrompts)
AddEventHandler('onClientResourceStart', function(resource)
if resource == 'qs-textui' then
createPrompts()
end
end)


