EndCore Framework

UI services

Notifications, progress bars, input dialogs, interaction prompts and NPC conversations rendered by en-ui, with every option and the behaviour when en-ui is missing.

en-ui renders the interface pieces every resource needs: notifications, a progress bar, an input dialog, an interaction prompt and an NPC conversation screen. They are drawn on en-ui's own page, so every resource gets the same look without building anything. You call them through the library.

Add en-ui to your resource's dependencies if you use these.

At a glance

SideFunctionBlocksWithout en-ui
serverencore.notify(target, data)noSent anyway; clients without en-ui show nothing
clientencore.notify(data)noPrinted to the F8 console
clientencore.progress(data)yesReturns false
clientencore.cancelProgress()noDoes nothing
clientencore.isProgressActive()noReturns false
clientencore.inputDialog(title, fields, options?)yesReturns nil
clientencore.showPrompt(data)noDoes nothing
clientencore.hidePrompt()noDoes nothing
clientencore.dialog.show(data)yesReturns nil
clientencore.dialog.close()noDoes nothing
clientencore.dialog.isOpen()noReturns false

When a client call finds en-ui isn't running, it logs en-ui is not running; interface calls will do nothing once.

Blocking calls yield, so run them from a thread, event handler, target onSelect or radial onSelect.

Notifications

A notification appears on the left of the screen, above the vitals, and disappears after its duration.

lua
-- client
encore.notify({ title = 'Radio', description = 'Signal lost', type = 'warning' })
encore.notify('Saved')    -- a plain string becomes { description = 'Saved' }

-- server
encore.notify(source, { description = 'You found 3 scrap', type = 'success' })
encore.notify(-1, { title = 'Broadcast', description = 'Horde inbound at Sandy Shores', type = 'error' })

On the server, target is a player id, or -1 for everyone. A target of 0 or nil prints the message to the server console, which is handy for commands run from the console.

Options

FieldTypeDefaultBehaviour
titlestring?noneHeading line
descriptionstring?noneBody text
typestring'inform''inform', 'success', 'warning' or 'error'. 'info' works as an alias for 'inform'. Unknown values become 'inform'
durationnumber5000Milliseconds, clamped between 1500 and 15000
iconstring?by typeAn icon name. Defaults: inform uses info, success uses check, warning and error use alert. Unknown names show the alert glyph
  • At most five notifications are visible. The oldest leaves first.
  • error notifications are announced with role="alert", the others with role="status".
  • Text is inserted as text, never HTML, so showing a player-authored string is safe.

Progress bar

encore.progress(data) shows a bar at the lower centre of the screen, runs an optional animation, and blocks until the bar finishes or is cancelled. It returns true only if the bar completed.

FieldTypeDefaultBehaviour
labelstring''Text above the bar
durationnumber3000Milliseconds, minimum 100
canCancelbooleantruePlayers cancel with X. A keycap hint is shown. Set false to prevent cancelling
disabletablenoneControls disabled every frame while the bar runs (below)
animtablenoneAn animation or scenario to play (below)
maxDistancenumber?noneCancels if the player moves further than this many metres from where they started
useWhileDeadboolean?falseUnless set, the bar cancels when the player dies or ragdolls

disable keys:

KeyDisables
moveWalking, sprinting, jumping and stealth
carSteering, accelerating, braking and leaving a vehicle
combatFiring, attacking, aiming and melee
mouseLooking around

anim takes one of two shapes:

  • { dict, clip, flag?, blendIn?, blendOut? } plays an animation. flag defaults to 49, blend speeds to 3.0. If the dictionary doesn't exist or doesn't load within 3 seconds, the bar runs without it.
  • { scenario, playEnter? } starts a scenario in place. playEnter defaults to true.

The player's tasks are cleared when the bar ends, if an animation started.

Other behaviour:

  • Only one bar runs at a time. Calling encore.progress while one is active returns false immediately.
  • The bar shows the time remaining, then "Done" or "Stopped".
  • encore.cancelProgress() cancels the active bar, and its encore.progress call returns false.
  • encore.isProgressActive() tells you whether a bar is running.
lua
local done = encore.progress({
    label = 'Bandaging',
    duration = 4000,
    disable = { move = true, combat = true },
    anim = { dict = 'missheistdockssetup1clipboard@idle_a', clip = 'idle_a' },
    maxDistance = 2.0,
})

if done then
    TriggerServerEvent('my-medical:bandaged')
end
Warning

A progress bar runs on the player's client. The server can't tell whether it really finished. Before rewarding, check on the server what you can: that the player has the item, is in the right place, and isn't calling the event faster than the bar allows.

Input dialog

encore.inputDialog(title, fields, options?) opens a modal form and blocks until the player submits or cancels. It returns the values in field order, or nil if they cancelled.

Options

FieldTypeDefaultMeaning
descriptionstring?noneText under the title
submitLabelstring?'Confirm'Label of the submit button
allowCancelboolean?trueWhether the player can cancel (including with Escape)

Fields

Every field accepts type, label, required, description, default and placeholder, plus the keys for its type:

typeControlExtra keysValue returned
'input'Text box. Also used for unknown typespassword = true masks the text; min and max are character countsstring
'number'Number boxmin, max, stepnumber, or nothing if left empty
'select'Drop-downoptions = { { value, label? }, ... }; placeholder defaults to 'Choose…'The chosen option's value, as a string
'date'Date pickermin, max'YYYY-MM-DD'
'textarea'Three-row text areamin and max are character countsstring
'checkbox'Toggle switchdefault is a booleanboolean

Validation

Fields are checked in place, with plain-language errors, before the form submits:

  • required: "This field is required."
  • Numbers: "Enter a number.", "Must be at least N.", "Must be at most N."
  • Dates: "Pick a date."
  • Text: min and max apply to the length of the trimmed value.

Behaviour

  • The dialog takes NUI focus while it is open, and Tab stays inside it.
  • Only one dialog can be open. A second call returns nil straight away.
  • If en-ui stops while a dialog is open, the call returns nil.
  • To close an open dialog from code, call exports['en-ui']:CloseInputDialog(). The waiting call returns nil.
  • An empty optional number comes back as nothing, so read values by position (values[2]) rather than relying on the length of the list.
lua
local values = encore.inputDialog('Name your base', {
    { type = 'input', label = 'Name', required = true, min = 3, max = 24 },
    { type = 'select', label = 'Access', options = {
        { value = 'party', label = 'Party members' },
        { value = 'private', label = 'Only me' },
    }, default = 'party' },
    { type = 'checkbox', label = 'Show on map', default = true },
}, { submitLabel = 'Claim' })

if not values then return end

local name, access, showBlip = values[1], values[2], values[3]
TriggerServerEvent('my-bases:claim', name, access, showBlip)
Danger

Dialog validation runs in the player's NUI and can be bypassed. When you send the values to the server, validate them again there: types, lengths, allowed option values and permissions.

lua
-- server
RegisterNetEvent('my-bases:claim', function(name, access, showBlip)
    local src = source
    if type(name) ~= 'string' then return end
    name = encore.string.trim(name)
    if #name < 3 or #name > 24 then return end
    if access ~= 'party' and access ~= 'private' then return end
    showBlip = showBlip == true
    -- ...
end)

Interaction prompt

A prompt is a keycap and a label at the lower centre of the screen. It stays until you hide it.

FieldTypeDefaultMeaning
keystring'E'Text in the keycap
labelstring''What pressing the key does
subjectstring?noneSmaller secondary text, such as the place or object
lua
encore.showPrompt({ key = 'E', label = 'Open locker', subject = 'Police Station' })
-- later
encore.hidePrompt()

The prompt and the progress bar share a spot on screen. While a bar is running it takes the spot, and the prompt returns when the bar ends.

Prompts are also the fallback interaction when en-target isn't running. See Targeting interface.

NPC conversations

encore.dialog is the conversation screen every survivor, trader and dealer uses. The camera frames the character you are talking to, their lines type in one page at a time, and the player picks a reply. Each encore.dialog.show call is one step of the conversation.

lua
local choice = encore.dialog.show(data)   -- the chosen choice id, or nil if they left
encore.dialog.close()
local open = encore.dialog.isOpen()

Fields

FieldTypeDefaultMeaning
speakerstring''Name of who is talking (up to 60 characters)
titlestring?noneSecondary heading, such as a place (up to 60 characters)
linesstring or string[]requiredWhat they say. Each line is a page (up to 800 characters each)
choicestable?noneReplies (below). With no choices, the conversation closes after the last line
detailstable?noneLabel and value rows shown with the choices, such as a reward
pednumber?noneThe ped to frame. The camera moves to their face and the player turns toward them
cameraboolean?truefalse keeps the normal camera, and releases a framing camera left from an earlier step
holdboolean?falseKeep the screen and camera up after a choice, ready for your next step
allowLeaveboolean?trueShow the Leave control

Each choice is { id, label, icon?, disabled?, reason?, leave? }:

KeyMeaning
idReturned when picked. Defaults to the choice's position as a string
labelButton text (up to 120 characters)
iconAn icon name. Unknown names are not shown
disabledShown but can't be picked
reasonWhy a disabled choice is locked, for example 'Needs level 5'
leaveMarks the choice as the way out and styles it that way. Picking it still returns its id

Each detail is { label, value, tone? }, where tone is 'accent' or 'danger'.

Behaviour

  • Choices are numbered with keycaps and appear once the last line has finished typing.
  • show returns nil if the player leaves, or reaches the end of a step that has no choices. The screen closes.
  • Without hold, the screen and camera close as soon as a choice is made. With hold = true they stay up. If you don't show another step within 5 seconds, they close by themselves so the player is never stuck.
  • Only one step can be waiting at a time. A second show while one is waiting returns nil.
  • The conversation closes automatically when the player dies or their character unloads.

Example

lua
local ped = data.entity

local choice = encore.dialog.show({
    ped = ped,
    speaker = 'Sgt. Rhodes',
    title = 'Burton Checkpoint',
    lines = { 'You look like you can carry things.', 'I have work, if you want it.' },
    details = { { label = 'Reward', value = '$150 · 200 XP', tone = 'accent' } },
    choices = {
        { id = 'accept', label = "I'll do it", icon = 'check' },
        { id = 'trade', label = "Let's trade", icon = 'cash' },
        { id = 'hard', label = 'The hard job', disabled = true, reason = 'Needs level 5' },
        { id = 'leave', label = 'Not now', leave = true },
    },
    hold = true,
})

if choice == 'accept' then
    encore.dialog.show({
        ped = ped,
        speaker = 'Sgt. Rhodes',
        lines = 'Bring me three medkits from the hospital. Don’t die.',
    })
    TriggerServerEvent('my-quests:accept', 'rhodes_medkits')
else
    encore.dialog.close()
    if choice == 'trade' then openTrader('rhodes') end
end