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

# STASHES & SHOPS

FiveM script for shop and stash interactions via `ox_inventory`, using ped spawns. **Without touching any ox\_inventory files**, all shops and stashes can be added or removed through config.

***

## Features

* **No changes to ox\_inventory** — shops and stashes are managed entirely from config
* **Ped spawn** — customizable NPCs at each point
* **Target + keybind** — `ox_target` or key (default E)
* **Multiple access** — the same shop/stash can be opened at more than one location
* **Job/group support** — shop or stash limited to specific jobs
* **Performance** — peds only spawn when a player is nearby (50 m)
* **Localization** — UI strings via config (`tr`, `en`, `de`, …)

## Dependencies

| Resource      | Required |
| ------------- | -------- |
| ox\_lib       | ✅        |
| ox\_target    | ✅        |
| ox\_inventory | ✅        |

## Installation

1. Copy the `axcos_ss` folder into `resources`.
2. In `server.cfg`, add `ensure axcos_ss` **after** `ox_inventory`:

```cfg
ensure ox_lib
ensure ox_target
ensure ox_inventory
ensure axcos_ss
```

3. Edit `config/config.lua` for your server.

> **Important:** `axcos_ss` must start after `ox_inventory`. Otherwise `RegisterShop` / `RegisterStash` may not work correctly.

## File structure

```
axcos_ss/
├── fxmanifest.lua          # Resource manifest (escrow_ignore is defined here)
├── config/
│   ├── config.lua          # Shop/stash, global settings, Config.Locale
│   └── locales.lua         # Strings (tr, en, de, …)
├── client/
│   └── client.lua          # Ped spawn, ox_target, keybind (Escrow: usually encrypted)
├── server/
│   └── server.lua          # RegisterShop, RegisterStash, re-registers if ox_inventory restarts
└── README.md               # This file (escrow_ignore: plaintext)
```

## Configuration

### Global settings

Values in `config/config.lua` are specific to your server. Example:

```lua
Config.Locale = 'tr'             -- must match a key in config/locales.lua
Config.Interaction = 'both'        -- 'target' | 'keybind' | 'both'
Config.KeybindDistance = 2.5     -- key interaction distance (meters)
Config.KeybindDefaultKey = 'E'     -- default for FiveM keybind registration
```

| Setting                    | Description                                      | Notes                                               |
| -------------------------- | ------------------------------------------------ | --------------------------------------------------- |
| `Config.Locale`            | Language key in `config/locales.lua`             | e.g. `tr`, `en`, `de`                               |
| `Config.Interaction`       | Global: target only, key only, or both           | Can be overridden per shop/stash with `interaction` |
| `Config.KeybindDistance`   | Max distance to ped when using key               | —                                                   |
| `Config.KeybindDefaultKey` | Initial key; player can change in FiveM settings | —                                                   |

## Localization

In-game text (keybind prompt, default labels, debug messages) is defined in `config/locales.lua`.

### Choosing a language

Set the active language in `config/config.lua`:

```lua
Config.Locale = 'tr'  -- or 'en', 'de'
```

### Built-in languages

| Code | Language |
| ---- | -------- |
| `tr` | Turkish  |
| `en` | English  |
| `de` | German   |

### Adding a new language

Copy a full locale table in `config/locales.lua` and translate every key (e.g. add `fr`):

```lua
Config.Locales = {
    tr = { ... },
    en = { ... },
    de = { ... },
    fr = {
        keybind_open = '[%s] Ouvrir',
        keybind_description = 'Ouvrir le magasin / la réserve',
        default_shop = 'Magasin',
        default_stash = 'Réserve',
        debug_shop_skip = '%s: pas d’inventaire, ignoré',
        debug_stash_skip = '%s: pas de peds, ignoré',
        debug_register_shop_error = 'RegisterShop %s erreur: %s',
        debug_register_stash_error = 'RegisterStash %s erreur: %s',
    },
}
```

### Locale keys

| Key                          | Description                                           |
| ---------------------------- | ----------------------------------------------------- |
| `keybind_open`               | Key interaction prompt (`[E] Open` — `%s` is the key) |
| `keybind_description`        | Description in FiveM keybind settings                 |
| `default_shop`               | Default shop name if no `label` in config             |
| `default_stash`              | Default stash name if no `label` in config            |
| `debug_shop_skip`            | Debug: shop skipped (inventory issues)                |
| `debug_stash_skip`           | Debug: stash skipped (peds issues)                    |
| `debug_register_shop_error`  | Debug: `RegisterShop` error text                      |
| `debug_register_stash_error` | Debug: `RegisterStash` error text                     |

## Shops

Shops are defined in `Config.Shops`. Each shop has a unique **key**.

### Shop fields

| Field         | Type   | Required | Description                             |
| ------------- | ------ | -------- | --------------------------------------- |
| `label`       | string | ✅        | Name shown when inventory opens         |
| `inventory`   | table  | ✅        | List of items for sale                  |
| `peds`        | table  | ✅        | Ped spawn points                        |
| `groups`      | table  | ❌        | Job/group restriction. `nil` = everyone |
| `icon`        | string | ❌        | ox\_target icon (Font Awesome)          |
| `interaction` | string | ❌        | Override interaction type for this shop |

### Inventory format

Each line follows ox\_inventory item format:

```lua
{ name = 'item_name', price = 100 }
```

Extra options:

```lua
{ name = 'weapon_pistol', price = 5000, metadata = { registered = true }, license = 'weapon' }
{ name = 'gold_bar', price = 1000, currency = 'black_money' }
{ name = 'medikit', price = 50, grade = 2 }  -- only grade 2+ can see
```

### Shop example

```lua
Config.Shops = {
    ['barShop'] = {
        label = 'Bar',
        groups = { ['bartender'] = 0 },
        icon = 'fas fa-beer',
        inventory = {
            { name = 'ax_beer', price = 35 },
            { name = 'ax_water', price = 15 },
        },
        peds = {
            {
                coords = vec3(127.0, -1284.0, 29.28),
                heading = 120.0,
                model = `s_f_y_bartender_01`,
                scenario = 'WORLD_HUMAN_AA_COFFEE',
            },
        },
    },
}
```

## Stashes

Stashes are defined in `Config.Stashes`. Each stash has a unique **key** (stash name).

### Stash fields

| Field         | Type    | Required | Description                         |
| ------------- | ------- | -------- | ----------------------------------- |
| `label`       | string  | ✅        | Stash name                          |
| `slots`       | number  | ✅        | Slot count                          |
| `weight`      | number  | ✅        | Max weight (grams)                  |
| `owner`       | boolean | ✅        | `false` = shared, `true` = personal |
| `peds`        | table   | ✅        | Ped spawn points                    |
| `groups`      | table   | ❌        | Job/group restriction               |
| `icon`        | string  | ❌        | ox\_target icon                     |
| `interaction` | string  | ❌        | Override interaction type           |

### Owner behavior

| Value   | Description                                    |
| ------- | ---------------------------------------------- |
| `false` | Shared stash — everyone sees the same contents |
| `true`  | Personal stash — each player has their own     |

### Stash example

```lua
Config.Stashes = {
    ['policeArmory'] = {
        label = 'Police armory',
        slots = 100,
        weight = 500000,
        owner = false,
        groups = { ['police'] = 0 },
        icon = 'fas fa-shield-alt',
        peds = {
            {
                coords = vec3(452.3, -991.4, 30.7),
                heading = 0.0,
                model = `s_m_m_security_01`,
                scenario = 'WORLD_HUMAN_CLIPBOARD',
            },
        },
    },
}
```

## Interaction types

### `target`

Only `ox_target` (eye). Player looks at the ped and uses left click or the default target key.

### `keybind`

Only key. When close to a ped, `[E] Open` (or your locale) appears. The key can be changed in **Settings → Key bindings** in FiveM (search: `axcos_ss`).

### `both`

Target and key both work.

### Per shop/stash override

```lua
['secretShop'] = {
    label = 'Secret shop',
    interaction = 'target',  -- this shop is target-only
    -- ...
}
```

## Multiple access points

You can add several peds for the same shop or stash. Each point opens the same storage.

```lua
peds = {
    { coords = vec3(-223.83, -334.96, 29.10), heading = 299.82, model = `s_f_y_clubbar_01`, scenario = 'WORLD_HUMAN_AA_COFFEE' },
    { coords = vec3(-217.27, -297.17, 29.10), heading = 219.01, model = `s_f_y_clubbar_01`, scenario = 'WORLD_HUMAN_AA_COFFEE' },
    -- more points...
}
```

### Ped fields

| Field      | Type    | Required | Description                                      |
| ---------- | ------- | -------- | ------------------------------------------------ |
| `coords`   | vector3 | ✅        | Ped position                                     |
| `heading`  | number  | ✅        | Ped heading (degrees)                            |
| `model`    | hash    | ✅        | Ped model hash (e.g. `s_f_y_bartender_01`)       |
| `scenario` | string  | ❌        | GTA scenario name (e.g. `WORLD_HUMAN_AA_COFFEE`) |

## Group / job access

Use `groups` to restrict who can use a shop or stash:

```lua
groups = { ['police'] = 0 }              -- police only, grade 0+
groups = { ['mechanic'] = 2 }            -- mechanic only, grade 2+
groups = { ['admin'] = 0, ['mod'] = 0 }  -- admin or mod
groups = nil                             -- everyone
```

`ox_target` checks groups through your framework (ESX, QBCore, ox\_core, ND Core, etc.).

## Adding a new shop or stash

### New shop

1. Add a new entry to `Config.Shops` in `config/config.lua`.
2. The key must be unique (e.g. `['myShop']`).
3. `inventory` and `peds` are required.
4. Restart the server or run `ensure axcos_ss`.

### New stash

1. Add a new entry to `Config.Stashes` in `config/config.lua`.
2. The key is the stash name (e.g. `['myStash']`).
3. `slots`, `weight`, `owner`, and `peds` are required.
4. Restart the server.

### Removing

Delete the shop/stash block from `config/config.lua`.

## FAQ

<details>

<summary>Do I have to edit `shops.lua` or `stashes.lua` in ox_inventory?</summary>

**No.** axcos\_ss registers shops and stashes with `RegisterShop` and `RegisterStash` from its own config. You do not need to edit ox\_inventory files.

</details>

<details>

<summary>What about existing ox_inventory shops/stashes?</summary>

Shops/stashes you define in config are registered (or override) in ox\_inventory. Any shop/stash that exists in ox\_inventory but not in axcos\_ss (e.g. `policelocker`, `emslocker`) keeps working as before.

</details>

<details>

<summary>How do I change the key?</summary>

In-game: **ESC → Settings → Key bindings** and search for **axcos\_ss** or the text from `keybind_description` in `config/locales.lua` (e.g. “Open shop / stash”). The default is set by `Config.KeybindDefaultKey`.

</details>

<details>

<summary>What is the ped distance?</summary>

Peds spawn when a player is within **50 m** and are removed when the player leaves. This is for performance.

</details>

<details>

<summary>How do I change the language?</summary>

Set `Config.Locale` in `config/config.lua` (e.g. `'en'`, `'de'`). Available codes: `tr`, `en`, `de`. To add a language, edit `config/locales.lua`.

</details>

<details>

<summary>How do I enable debug?</summary>

Use convar `+set axcos_ss_debug 1` to print debug messages. Shops/stashes with missing inventory or peds are skipped and logged to the console.

</details>

<details>

<summary>Shops/stashes break after restarting ox_inventory</summary>

**ox\_inventory** restarts can clear server-side registrations. This script re-runs `RegisterShop` / `RegisterStash` every time `ox_inventory` **starts**. If something still fails, try `restart axcos_ss` in the console.

</details>

<details>

<summary>I changed config but nothing updated</summary>

Lua config is read at resource start: use `ensure axcos_ss` or a full server restart. If you only changed ped coords, leaving and re-entering the area can be enough; for list changes, restart the resource.

</details>

<details>

<summary>Do I need axcos_core or another AXCOS script?</summary>

**No.** This resource only depends on `ox_lib`, `ox_target`, and `ox_inventory`.

</details>

<details>

<summary>I can’t use the same stash from multiple points</summary>

The server should register all ped locations for one stash via `RegisterStash`. In `config/config.lua`, make sure the same stash has multiple entries under `peds`. If you still get “can’t open” type messages, check ox\_inventory distance limits (default 10) and `groups`.

</details>

## Version

**1.0.0** — AXCOS


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://axcos.gitbook.io/axcos-docs/scripts/stashes-and-shops.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
