Two client-side interfaces for things players do with their hands. encore.minigame asks en-minigames to play a short game and tells you whether the player succeeded. encore.radial adds your own entries to the hold-to-open radial menu from en-radialmenu.
Minigames
Every call yields until the game ends and returns whether it succeeded.
| Function | Returns | Stand-in without en-minigames |
|---|---|---|
encore.minigame.isAvailable() | boolean | |
encore.minigame.skillCheck(difficulty?, keys?, options?) | boolean | "Concentrating..." for 2.5 s |
encore.minigame.lockpick(options?) | success, broke | "Picking the lock..." for 6 s. broke is always false |
encore.minigame.hotwire(options?) | boolean | "Hotwiring..." for 8 s |
encore.minigame.sequence(options?) | boolean | "Working..." for 4 s |
encore.minigame.isActive() | boolean | false |
Options
- skillCheck:
difficultyis one difficulty or a list, and each entry is one round. It defaults to'medium'.keysis the list of keys the check may ask for, and defaults to E. - lockpick, hotwire and sequence take either a difficulty string or a table:
| Key | Applies to | Meaning |
|---|---|---|
difficulty | all | 'easy', 'medium' or 'hard'. Default 'medium' |
bonus | all | 0 to 1. Makes the game easier. Pass skill bonuses here |
title | all | Heading shown during the game |
pins | lockpick | Number of pins |
length | sequence | Number of steps |
Fallback behaviour
- When en-minigames isn't running, a cancellable progress bar stands in, and
en-minigames is not running; minigames fall back to progress barsis logged once. Finishing the bar counts as success. - The stand-in bar uses UI services, so if en-ui isn't running either, every minigame returns
false. - If an en-minigames export errors, the call returns
falseand a warning is logged once. - To stop a running game from code, call
exports['en-minigames']:Cancel().
Security
A minigame result is decided on the player's client. When success unlocks something on the server, check the rest there: that the player has the lockpick, is next to the door or vehicle, and isn't sending successes faster than the game can be played.
Examples
-- lockpicking a door, eased by the player's skill
local bonus = encore.skills.getBonus('lockpick')
local picked, broke = encore.minigame.lockpick({ difficulty = 'hard', bonus = bonus })
if broke then TriggerServerEvent('my-doors:pickBroke') end
if picked then TriggerServerEvent('my-doors:unlock', doorId) end-- two rounds of skill check, then a short sequence
if not encore.minigame.skillCheck({ 'easy', 'medium' }) then return end
if encore.minigame.sequence({ difficulty = 'easy', length = 5, title = 'Rewire the panel' }) then
TriggerServerEvent('my-generator:repaired', generatorId)
end-- hotwiring
if encore.minigame.hotwire({ difficulty = 'medium', bonus = encore.skills.getBonus('hotwire') }) then
TriggerServerEvent('my-vehicles:hotwired', NetworkGetNetworkIdFromEntity(vehicle))
endContract
To replace en-minigames, a resource named en-minigames must provide these client exports:
| Export | Returns |
|---|---|
SkillCheck(difficulty, keys, options) | boolean |
Lockpick(options) | success, broke (two values) |
Hotwire(options) | boolean |
Sequence(options) | boolean |
IsActive() | boolean |
Results are compared with == true, so return exactly true for success. The same two approaches as the inventory interface apply: ship it under that name, or run an adapter named en-minigames.
Radial menu
Players hold F1 (rebindable) to open the radial menu, point at an entry and release to pick it. EndCore ships four submenus that hide themselves when irrelevant: 'survivor', 'vehicle', 'base' and 'party'. Your resource can add entries to the top ring, to those submenus, or add submenus of its own.
| Function | Notes |
|---|---|
encore.radial.isAvailable() | Whether en-radialmenu is started |
encore.radial.addItem(item) | Adds or replaces an entry. Raises an error unless item.id is a string |
encore.radial.removeItem(id) | Forgets the entry and removes it if en-radialmenu is running |
Item fields
| Field | Default | Meaning |
|---|---|---|
id | required | Unique id. Prefix it with your resource |
label | id | Text shown on the entry |
icon | none | An icon name |
parent | top ring | The id of the submenu this entry belongs in |
order | 100 | Lower numbers come first |
submenu | false | true makes this entry a submenu. A submenu with no visible children is hidden |
canShow | always | function() returning boolean, checked each time the menu opens. Keep it quick |
onSelect | none | Runs in a new thread in your resource, so it may wait |
keepOpen | false | true keeps the menu open after this entry is picked |
Behaviour
- Items are copied and remembered in your resource. They are applied now if en-radialmenu is running, and applied again each time it starts, so start order doesn't matter.
- Unlike target options,
removeItemforgets the entry, so it doesn't come back after a restart. - en-radialmenu drops a resource's entries when that resource stops.
- Without en-radialmenu, nothing is shown and nothing errors.
Examples
-- a submenu of your own, with an entry inside it
encore.radial.addItem({ id = 'my-radio:menu', label = 'Radio', icon = 'radio', submenu = true, order = 60 })
encore.radial.addItem({
id = 'my-radio:toggle',
parent = 'my-radio:menu',
label = 'Power',
icon = 'radio',
onSelect = function() ExecuteCommand('radio') end,
})-- an entry in the built-in Survivor submenu, only while carrying a radio
encore.radial.addItem({
id = 'my-radio:quick',
parent = 'survivor',
label = 'Radio',
icon = 'radio',
order = 50,
canShow = function()
return encore.inventory.getItemCount('radio') > 0
end,
onSelect = function()
local values = encore.inputDialog('Tune radio', {
{ type = 'number', label = 'Channel', required = true, min = 1, max = 999 },
})
if values then TriggerServerEvent('my-radio:join', values[1]) end
end,
})Contract
A replacement named en-radialmenu must export AddItem(item) and RemoveItem(id) on the client. item.onSelect arrives already wrapped in a thread. Re-adding an item with the same id should replace it.