> For the complete documentation index, see [llms.txt](https://visionstore.gitbook.io/visionstore-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://visionstore.gitbook.io/visionstore-docs/scripts/crafting/configuration/npc-vendors.md).

# Hidden NPC vendors

Hidden NPCs that sell benches through a randomised material challenge, and police custody.

Hidden NPCs give players an in-game way to earn a bench:

1. Find the NPC (no blip by default, they're meant to be found).
2. Pay the entry fee.
3. Bring a randomised list of materials (deposits can be partial, progress is saved).
4. Receive a brand new bench with its own progression.

Three vendors are included: weapons, tools and medical. They're configured in `config/npcs.lua`.

{% hint style="warning" %}
`config/npcs.lua` is **server only**, so prices, material pools and rewards never reach players. Keep it under `server_scripts` in `fxmanifest.lua`. Talking to the NPCs needs **ox\_target**.
{% endhint %}

## Options

```lua
Config.NPCOptions = {
    enabled = true,

    benchAccess = {
        enabled       = true,
        autoProvision = true,
        mode          = 'grant',
    },

    interactionTolerance = 8.0,

    rateLimit = {
        state   = 250,
        pay     = 2000,
        deposit = 400,
        custody = 2000,
    },

    log = true,
    -- custody = { ... } (see below)
}
```

| Option                      | Default   | What it does                                                     |
| --------------------------- | --------- | ---------------------------------------------------------------- |
| `enabled`                   | `true`    | Turn the whole feature off with `false`.                         |
| `benchAccess.autoProvision` | `true`    | Writes each NPC's unlock onto its bench type automatically.      |
| `benchAccess.mode`          | `'grant'` | See below.                                                       |
| `interactionTolerance`      | `8.0`     | Server-side distance check for payments and deposits, in metres. |
| `rateLimit`                 |           | Minimum ms between actions, per player.                          |
| `log`                       | `true`    | Log payments, completions and unlocks.                           |

### Unlock mode

Completing an NPC permanently **unlocks** its workbench for that character.

| `benchAccess.mode`  | Meaning                                                                                                      |
| ------------------- | ------------------------------------------------------------------------------------------------------------ |
| `'grant'` (default) | The unlock is one more way past the bench's access rules (jobs / gangs / ACE). Nothing is taken away.        |
| `'require'`         | The unlock is **mandatory**: a player must complete the NPC before they can use benches of that type at all. |

## Adding or editing an NPC

Copy a block in `Config.NPCs`. Here's the included tools vendor:

```lua
tools_dealer = {
    label = 'Scrapyard Foreman',

    client = {
        model               = 'a_m_m_farmer_01',
        coords              = vec4(2654.36, 1693.09, 24.49, 262.23),
        groundOffset        = -1.0,
        scenario            = 'WORLD_HUMAN_CLIPBOARD',
        frozen              = true,
        invincible          = true,
        interactionDistance = 2.0,
        targetIcon          = 'fa-solid fa-screwdriver-wrench',
    },

    workbench = 'tools',
    benchType = 'tools',

    workbenchInfo = {
        label       = 'Tools Bench',
        description = 'Everyday tools, kits and parts that keep a crew running.',
    },

    reward  = { bench = true, amount = 1 },
    payment = { account = 'cash', amount = 75000 },

    challenge = {
        minMaterials = 3,
        maxMaterials = 4,
        materials = {
            { item = 'metalscrap', min = 60, max = 90 },
            { item = 'steel',      min = 40, max = 60 },
            { item = 'plastic',    min = 20, max = 30 },
            { item = 'rubber',     min = 40, max = 60 },
            { item = 'iron',       min = 40, max = 60 },
        },
    },
},
```

| Field                                     | What it does                                                                                 |
| ----------------------------------------- | -------------------------------------------------------------------------------------------- |
| `label`                                   | Menu title.                                                                                  |
| `client.model`                            | Ped model.                                                                                   |
| `client.coords`                           | `vec4(x, y, z, heading)`, with z at foot level.                                              |
| `client.groundOffset`                     | Added to z at spawn (default `-1.0` for standing coords).                                    |
| `client.scenario` / `client.animation`    | An ambient scenario, or `{ dict, name }` when no scenario is set.                            |
| `client.frozen` / `client.invincible`     | Default `true`.                                                                              |
| `client.interactionDistance`              | ox\_target reach (default `2.0`).                                                            |
| `client.targetIcon`                       | Font Awesome icon in the ox\_target menu.                                                    |
| `client.blip`                             | Optional `{ sprite, color, scale, label }`.                                                  |
| `workbench`                               | The unlock id this NPC grants (free text).                                                   |
| `benchType`                               | The bench type from `config/benches.lua` that's sold.                                        |
| `workbenchInfo`                           | `{ label, description }` shown in the menu.                                                  |
| `reward`                                  | `{ bench = true, amount = 1 }` for a bench, or `{ item = 'x', amount = 1 }` for any item.    |
| `payment`                                 | `{ account = 'cash' or 'bank', amount = 0 }`.                                                |
| `challenge.minMaterials` / `maxMaterials` | How many different materials each player is asked for.                                       |
| `challenge.materials`                     | The pool. Each player gets a random pick, each with a random amount between `min` and `max`. |

### The included vendors

| NPC                                 | Sells         | Entry fee     | Location                                |
| ----------------------------------- | ------------- | ------------- | --------------------------------------- |
| Weapon Specialist (`weapon_dealer`) | Weapons Bench | $500,000 cash | `vec4(-2079.75, 2610.07, 3.08, 113.08)` |
| Scrapyard Foreman (`tools_dealer`)  | Tools Bench   | $75,000 cash  | `vec4(2654.36, 1693.09, 24.49, 262.23)` |
| Back-Alley Medic (`medical_dealer`) | Medical Bench | $50,000 bank  | `vec4(615.43, -410.33, 26.03, 356.05)`  |

{% hint style="info" %}
Run `crafting npcs` in the console to see every NPC, its unlock, the bench it's wired to and whether it's in custody. `crafting npc-progress <id>` and `crafting npc-reset <id> [npc]` inspect and reset a player's progress.
{% endhint %}

## Police custody

An officer next to an NPC runs the command. The NPC raises their hands for everyone and stops trading until the next resource restart.

```lua
custody = {
    enabled = true,
    command = 'policecustody',

    jobs     = { 'police', 'sheriff', 'bcso', 'sasp' },
    jobTypes = { 'leo' },

    requireOnDuty = false,
    distance      = 5.0,
    notifyArea    = false,
    log           = true,
},
```

| Option          | Default                             | What it does                         |
| --------------- | ----------------------------------- | ------------------------------------ |
| `command`       | `'policecustody'`                   | The command officers use.            |
| `jobs`          | `police`, `sheriff`, `bcso`, `sasp` | Job names allowed to use it.         |
| `jobTypes`      | `{ 'leo' }`                         | Qbox / QBCore job types allowed too. |
| `requireOnDuty` | `false`                             | Officers must be on duty.            |
| `distance`      | `5.0`                               | Max metres from the NPC.             |
| `notifyArea`    | `false`                             | Also notify players within 30 m.     |
| `log`           | `true`                              | Log custody.                         |

## NPC visuals

`config/npc_display.lua` holds cosmetic settings only: the ped spawn distance (`spawnDistance = 100.0`), the progress icons in the requirement list (✅ 🟡 ❌) and the hands-up animation used for custody.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://visionstore.gitbook.io/visionstore-docs/scripts/crafting/configuration/npc-vendors.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
