EndCore Framework

en-minigames

Skill check, lockpick, hotwire and sequence minigames with difficulty levels and a bonus that skills can feed into.

en-minigames provides short skill games that other resources use for risky actions. Four games ship:

  • Skill check: a needle sweeps a dial; press the shown key while it is in the zone. One round per difficulty entry.
  • Lockpick: move the pick to find the sweet spot, then turn. Forcing it too hard for too long snaps the pick.
  • Hotwire: drag each wire to its match before time runs out.
  • Sequence: watch a run of directions, then repeat it with WASD or the arrow keys.

Every game has easy, medium and hard levels and accepts a bonus from 0 to 1 that makes it easier. That is how skills and perks feed in. Escape cancels a game.

At a glance

Depends onen-core, en-ui
SideClient only
Start afteren-core, en-ui. Callers using encore.minigame.* work without it
Database tablesNone
Config filesNone; tuning constants are in html/app.js
Key bindsNone

How it works

  • Only one game runs at a time. A second call while one is running fails immediately.
  • Every export yields until the game ends, then returns the result.
  • Lockpick and hotwire take mouse focus. Skill check and sequence take keyboard focus without showing the cursor.
  • Games are cancelled when the player dies (encore:client:playerDied) or unloads (encore:client:playerUnloaded).
Warning

Results are decided on the client. Treat a success as a request: validate distance, items and rate limits on the server before you open a door or hand out loot.

Fallback without en-minigames

The encore.minigame.* library functions fall back to an encore.progress bar when en-minigames is not running, so gameplay never blocks: skill check 2.5 s, lockpick 6 s, hotwire 8 s, sequence 4 s. See Minigames and radial.

Configuration

There is no config file. Each constant in html/app.js is a list indexed easy, medium, hard.

GameConstantsEffect of bonus
Skill checkzone size SC_SIZE = [72, 46, 28] degrees; needle speed SC_SPEED = [230, 310, 400] deg/s; default key pool ['E']zone x(1 + bonus x 0.5), speed x(1 - bonus x 0.25)
LockpickLP_TOLERANCE = [16, 10, 6] degrees either side; LP_STRAIN = [1.6, 1.2, 0.9] s of forcing before it breaks; pins default to level + 1 (1-5); turn keys D, Space, E, ArrowRighttolerance x(1 + bonus x 0.6), strain x(1 + bonus x 0.8)
Hotwirewires HW_COUNT = [4, 5, 6]; time HW_TIME = [16000, 13000, 10000] mstime x(1 + bonus x 0.5)
Sequencelength SQ_LENGTH = [4, 6, 8] (3-12); show time SQ_SHOW = [720, 560, 420] ms per step; input time length x 1100 ms x (1 + bonus x 0.5)more input time

Making hard lockpicks a little more forgiving:

js
// html/app.js
const LP_TOLERANCE = [16, 10, 8];
const LP_STRAIN = [1.6, 1.2, 1.0];

Exports

Client exports on exports['en-minigames']. All of them yield until the game ends.

ExportArgumentsReturns
SkillCheckdifficulty, keys?, options?. difficulty is a string or a list (one round each); keys e.g. { 'E', 'Q' }boolean
Lockpickoptions?: a difficulty string or { difficulty, pins, bonus, title }success, broke
Hotwireoptions?: { difficulty, bonus, title }boolean
Sequenceoptions?: { difficulty, length, bonus, title }boolean
IsActiveboolean
CancelEnds the current game as cancelled (the caller gets false)

encore.minigame (preferred)

FunctionReturns
encore.minigame.skillCheck(difficulty?, keys?, options?)boolean
encore.minigame.lockpick(options?)success, broke
encore.minigame.hotwire(options?)boolean
encore.minigame.sequence(options?)boolean
encore.minigame.isActive()boolean
encore.minigame.isAvailable()boolean

Events

EventSidePayload
en-minigames:client:startedClient localgame
en-minigames:client:finishedClient localgame, success

Examples

Lockpick a door with a skill bonus, and handle a broken pick:

lua
-- client
local bonus = 0.2 -- e.g. from a skills resource
local picked, broke = encore.minigame.lockpick({ difficulty = 'hard', pins = 3, bonus = bonus, title = 'Pharmacy door' })

if broke then
    TriggerServerEvent('my-res:pickBroke') -- server removes one lockpick (validate and rate-limit there)
elseif picked then
    TriggerServerEvent('my-res:doorOpened')
end

Three skill check rounds with two possible keys, then a hotwire:

lua
-- client
if encore.minigame.skillCheck({ 'easy', 'medium', 'medium' }, { 'E', 'F' }) then
    local started = encore.minigame.hotwire({ difficulty = 'medium', title = 'Hotwire' })
    if started then
        TriggerServerEvent('my-res:hotwired', NetworkGetNetworkIdFromEntity(GetVehiclePedIsIn(PlayerPedId(), false)))
    end
end