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 on | en-core, en-ui |
| Side | Client only |
| Start after | en-core, en-ui. Callers using encore.minigame.* work without it |
| Database tables | None |
| Config files | None; tuning constants are in html/app.js |
| Key binds | None |
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).
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.
| Game | Constants | Effect of bonus |
|---|---|---|
| Skill check | zone 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) |
| Lockpick | LP_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, ArrowRight | tolerance x(1 + bonus x 0.6), strain x(1 + bonus x 0.8) |
| Hotwire | wires HW_COUNT = [4, 5, 6]; time HW_TIME = [16000, 13000, 10000] ms | time x(1 + bonus x 0.5) |
| Sequence | length 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:
// 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.
| Export | Arguments | Returns |
|---|---|---|
SkillCheck | difficulty, keys?, options?. difficulty is a string or a list (one round each); keys e.g. { 'E', 'Q' } | boolean |
Lockpick | options?: a difficulty string or { difficulty, pins, bonus, title } | success, broke |
Hotwire | options?: { difficulty, bonus, title } | boolean |
Sequence | options?: { difficulty, length, bonus, title } | boolean |
IsActive | boolean | |
Cancel | Ends the current game as cancelled (the caller gets false) |
encore.minigame (preferred)
| Function | Returns |
|---|---|
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
| Event | Side | Payload |
|---|---|---|
en-minigames:client:started | Client local | game |
en-minigames:client:finished | Client local | game, success |
Examples
Lockpick a door with a skill bonus, and handle a broken pick:
-- 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')
endThree skill check rounds with two possible keys, then a hotwire:
-- 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