> ## 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 Project X ATM Robbery resource

Most of the core logic in this resource is handled by the bridge and escrow. 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 notifications, zones, evidence, stress, interaction targets, and the minigame callback. If you want to change the minigames used for each step, this is the file to edit.

### Minigame Callback

Each approach or ATM breakdown step triggers the `projectx-atmrobbery:client:Minigames` callback with the step name. You can swap any minigame by replacing the export call inside the matching `elseif` block.

```lua theme={null}
lib.callback.register('projectx-atmrobbery:client:Minigames', function(step)
    if Config.TestingMode then return true end
    local p = promise.new()
    if not exports['projectx-bridge']:MinigameCheck("bl_ui") then
        p:resolve(false)
        return Citizen.Await(p)
    end

    if step == 'RopeDrill' then
        local success = exports["bl_ui"]:CircleSum(3, {length = 4, duration = 15000})
        p:resolve(success)
    elseif step == 'Drill' then
        local success = exports["bl_ui"]:WaveMatch(1, {duration = 30000})
        p:resolve(success)
    elseif step == 'C4' then
        local success = exports["bl_ui"]:MineSweeper(3, {grid = 7, duration = 10000, target = 10, previewDuration = 2000})
        p:resolve(success)
    elseif step == 'Thermite' then
        local success = exports["bl_ui"]:DigitDazzle(1, {length = 4, duration = 10000})
        p:resolve(success)
    elseif step == 'GasTank' then
        local success = exports["bl_ui"]:Untangle(1, {numberOfNodes = 10, duration = 15000})
        p:resolve(success)
    elseif step == 'Impersonation' then
        local success = exports["bl_ui"]:PrintLock(1, {grid = 5, duration = 15000, target = 5})
        p:resolve(success)
    elseif step == 'Hacking' then
        local success = exports["bl_ui"]:WordWiz(1, {length = 4, duration = 40000})
        p:resolve(success)
    elseif step == 'Beating' then
        local success = exports["bl_ui"]:WaveMatch(1, {duration = 30000})
        p:resolve(success)
    elseif step == "AtmStep1" then
        local success = exports["bl_ui"]:KeySpam(4, 50)
        p:resolve(success)
    elseif step == "AtmStep2" then
        local success = exports["bl_ui"]:CircleProgress(4, 50)
        p:resolve(success)
    elseif step == "AtmStep3" then
        local success = exports["bl_ui"]:RapidLines(3, 50, 5)
        p:resolve(success)
    elseif step == "AtmStep4" then
        local success = exports["bl_ui"]:CircleShake(1, 50, 3)
        p:resolve(success)
    end

    return Citizen.Await(p)
end)
```

<Info>
  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.
</Info>

The default minigames per step are:

| Step            | Minigame Resource | Function         |
| --------------- | ----------------- | ---------------- |
| `RopeDrill`     | `bl_ui`           | `CircleSum`      |
| `Drill`         | `bl_ui`           | `WaveMatch`      |
| `C4`            | `bl_ui`           | `MineSweeper`    |
| `Thermite`      | `bl_ui`           | `DigitDazzle`    |
| `GasTank`       | `bl_ui`           | `Untangle`       |
| `Impersonation` | `bl_ui`           | `PrintLock`      |
| `Hacking`       | `bl_ui`           | `WordWiz`        |
| `Beating`       | `bl_ui`           | `WaveMatch`      |
| `AtmStep1`      | `bl_ui`           | `KeySpam`        |
| `AtmStep2`      | `bl_ui`           | `CircleProgress` |
| `AtmStep3`      | `bl_ui`           | `RapidLines`     |
| `AtmStep4`      | `bl_ui`           | `CircleShake`    |

### Client Hook

<AccordionGroup>
  <Accordion title="Robbed()" icon="car">
    Called on the client after successfully pulling an ATM from the wall (rope approach). Use this for local effects or client-side XP/UI.

    ```lua theme={null}
    function Robbed()
        -- Trigger something here after pulling the atm from the wall
    end
    ```
  </Accordion>
</AccordionGroup>

***

## opensource/server/server.lua

This file handles dispatch, items, money, logging, police checks, and exposes hooks you can use to run custom logic at specific points.

### Hooks

<AccordionGroup>
  <Accordion title="RobbedServer(source, Approach)" icon="sack-dollar">
    Called after the ATM is looted and the approach is finished.

    ```lua theme={null}
    function RobbedServer(source, Approach)
        -- Award bonus XP, update a scoreboard, etc.
    end
    ```
  </Accordion>

  <Accordion title="SuccessfulStep(source, step)" icon="circle-check">
    Called when a player successfully completes a placed-ATM breakdown step (`AtmStep1`–`AtmStep4`). By default this awards Rope approach XP when `SkillSystem` is enabled.

    ```lua theme={null}
    function SuccessfulStep(source, step)
        -- Custom logic per breakdown step
    end
    ```
  </Accordion>

  <Accordion title="FailedStep(source, step)" icon="circle-xmark">
    Called whenever a player fails a breakdown step.

    ```lua theme={null}
    function FailedStep(source, step)
        -- Apply penalties if needed
    end
    ```
  </Accordion>

  <Accordion title="SuccessfulApproach(source, approach)" icon="circle-check">
    Called when a player successfully completes an approach (e.g. `Drill`, `C4`, `Hacking`).

    ```lua theme={null}
    function SuccessfulApproach(source, approach)
        -- Custom reward / XP logic
    end
    ```
  </Accordion>

  <Accordion title="FailedApproach(source, approach)" icon="circle-xmark">
    Called whenever a player fails an approach.

    ```lua theme={null}
    function FailedApproach(source, approach)
        -- Custom fail handling
    end
    ```
  </Accordion>
</AccordionGroup>
