> ## Documentation Index
> Fetch the complete documentation index at: https://docs.projectx.gg/llms.txt
> Use this file to discover all available pages before exploring further.

# Open Source

> Open source files available for customization in the K4MB1 Maze Bank Robbery resource

Most of the core logic in this resource is handled by the bridge. The open source files are provided so you can customize minigames, add hooks for custom systems, and extend the script without touching escrow.

## opensource/client/client.lua

This file handles zone creation, drawtext, evidence, stress, and the minigame callback. To change which minigame runs for a step, edit the `projectx-mazebankrobbery-k4mb1:client:Minigames` callback.

### Minigame Callback

Each robbery step triggers the callback with the step name. Replace the export call inside the matching `elseif` block to swap minigames.

```lua theme={null}
lib.callback.register('projectx-mazebankrobbery-k4mb1:client:Minigames', function(Step)
    local p = promise.new()
    if Step == "C4Garage" then
        if not exports['projectx-bridge']:MinigameCheck("SN-Hacking") then p:resolve(false) return Citizen.Await(p) end
        local success = exports['SN-Hacking']:Thermite(6, 12, 10000, 2, 1, 3000)
        p:resolve(success)
    elseif Step == "OfficeDoorKeypad" then
        if not exports['projectx-bridge']:MinigameCheck("SN-Hacking") then p:resolve(false) return Citizen.Await(p) end
        local gameData = {totalNumbers = 20, seconds = 20, timesToChangeNumbers = 4, amountOfGames = 1, incrementByAmount = 5}
        local success = exports['pure-minigames']:numberCounter(gameData)
        p:resolve(success)
    elseif Step == "SecurityRoomDoor" then
        p:resolve(true)
    -- ... additional steps ...
    else
        p:resolve(false)
    end
    return Citizen.Await(p)
end)
```

> [!NOTE]
>
> You can return `true` unconditionally inside any step block to skip that minigame entirely. This is useful for testing or for steps you want to make automatic.
>
> For `OfficeDoorKeypad`, the bridge `MinigameCheck` may reference SN-Hacking, but the default implementation uses **pure-minigames** `numberCounter` — match your `server.cfg` ensures to the exports you actually call.

The default minigames per step are:

| Step | Minigame Resource | Function |
| - | - | - |
| `C4Garage` | `SN-Hacking` | `Thermite` |
| `GarageFusebox` | `SN-Hacking` | `SkillBar` |
| `GarageKeypad` | `simonsays` | `StartSimonSays` |
| `Generator` | `SN-Hacking` | `Thermite` |
| `Smoke` | `lightsout` | `StartLightsOut` |
| `BackDoorKeypad` | `SN-Hacking` | `MemoryCards` (medium) |
| `BackDoorKeypad2` | `utk_fingerprint` | `Start` (event) |
| `OfficeDoorKeypad` | `pure-minigames` | `numberCounter` |
| `Search` | `ox_lib` | `skillCheck` |
| `Computer` | `SN-Hacking` | `MemoryCards` (medium) |
| `OfficeMiniVault` | `pd-safe` | `createSafe` |
| `UtilityGenerator` | `SN-Hacking` | `Thermite` |
| `UtilityFusebox` | `SN-Hacking` | `Thermite` |
| `UtilityTemp` | `SN-Hacking` | `MemoryCards` (hard) |
| `MiniOfficeVault` | `pd-safe` | `createSafe` |
| `SecurityRoomDoor` | — | skipped (`return true`) |
| `SecurityRoomComputer` | `simonsays` | `StartSimonSays` |
| `SecurityRoomControl` | `SN-Hacking` | `MemoryCards` (medium) |
| `DrillGarageGate` | `SN-Hacking` | `SkillBar` |
| `MainVault2` | `lightsout` | `StartLightsOut` |
| `PuzzleDrill` | `SN-Hacking` | `SkillBar` |
| `VaultLeft` | `SN-Hacking` | `ColorPicker` |
| `VaultRight` | `SN-Hacking` | `ColorPicker` |
| `DrillVaultGate` | `SN-Hacking` | `SkillBar` |
| `Drilling` | `SN-Hacking` | `SkillBar` |

***

## opensource/server/server.lua

This file handles dispatch, item/money functions, logging, admin reset command registration, and server hooks for custom logic.

### Hooks

> [accordion] SuccessfulStep(source, step)
>
> Called whenever a player successfully completes a robbery step. Use this to award XP, trigger events, or log individual step completions.
>
> ```lua theme={null}
> function SuccessfulStep(source, step)
>     if step == "BackDoorKeypad" then
>         -- exports['projectx-bridge']:AddExperience(source, 'criminal', 10)
>     end
> end
> ```
>
> Step names match keys in `Config.MazeBankSteps` inside `config/config.lua`.

> [accordion] FailedStep(source, step)
>
> Called whenever a player fails a robbery step. Use this to remove XP, items, or apply penalties.
>
> ```lua theme={null}
> function FailedStep(source, step)
>     if step == "BackDoorKeypad" then
>         -- RemoveItem(source, 'x_device', 1)
>     end
> end
> ```

> [accordion] InitialCheck(source, Interaction)
>
> Called before a player can start the robbery. Return `false` to block them.
>
> ```lua theme={null}
> function InitialCheck(source, Interaction)
>     return true
>     -- Add a group check, tablet requirement, or any other gate here
> end
> ```

> [accordion] RobberyResetHook()
>
> Called whenever the heist resets. Use this to update a scoreboard, trigger an external event, or notify players.
>
> ```lua theme={null}
> function RobberyResetHook()
>     -- TriggerEvent('my-scoreboard:update')
> end
> ```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.