Skip to main content
The Control Panel isn’t a minigame — it’s a long-lived NUI surface for showing a heist schematic with clickable doors and cameras. It lives in cc_minigames because it shares the same NUI shell, design tokens, and SFX as the minigames. The caller drives it by passing callbacks and updating state with the ControlSet* exports.
The panel and a minigame can not be open at the same time on a given client — both compete for NUI focus. Use IsActive() / ControlIsOpen() to coordinate.

Open the panel

schematic accepts either an inline table or a string. When a string is passed, the resource looks up schematics/<name>.json inside cc_minigames (schematics/paleto_bank.json ships out of the box).

Update state from the callbacks

The panel doesn’t change state on its own — clicks fire your handlers, your handlers decide what to do, then you push the new state with these exports:

Door state shape

Camera state cycling

The shipped test command uses a three-state cycle (active → disabled → hacked → active) but nothing about the panel forces that — the strings are just labels you push. Use whatever vocabulary fits your heist resource.

Test command

/control paleto_bank opens the bundled schematics/paleto_bank.json and wires up a stub handler that:
  • Unlocks any clicked door for 5 seconds, then re-locks it.
  • Cycles cameras active → disabled → hacked → active on click.
  • Logs every state transition to the F8 console.
This is exactly the loop a heist resource will write — it’s a useful blueprint when you’re building the real one.

Cross-resource gotcha

Don’t pass closure callbacks via the :Function() colon-export syntax across resources unless you know what you’re doing. The Control panel uses pcall and a callable check that handles both plain functions and fxv2-wrapped callables, but if you see attempt to call a table value in your console it means the callback didn’t survive the export boundary. Use the standard exports.cc_minigames:ControlOpen({...}) form.