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

# GARAGES

Advanced garage system: public garages, job garages, impound, vehicle keys, hotwiring and more.

Public garages, job garages, impound lots, vehicle transfers and a key system, in one resource.

{% embed url="<https://www.youtube.com/watch?v=KwqEu6wlFiI>" %}

***

## ✅ Requirements

|                  |                                                                                                       |
| ---------------- | ----------------------------------------------------------------------------------------------------- |
| **Frameworks**   | ESX, QBCore, QBX (detected automatically)                                                             |
| **Dependencies** | [ox\_lib](https://github.com/overextended/ox_lib), [oxmysql](https://github.com/overextended/oxmysql) |
| **Inventories**  | Only for the trunk and glovebox preview: ox\_inventory, qb, qs, ps, lj, codem                         |

***

## 📥 Installation

```
ensure ox_lib
ensure oxmysql
ensure s_garages
```

There is no SQL file. The script adds its columns to your vehicle table on first start.

***

## ⚙️ Configuration

Everything in `config/` is open. Every option in `config/config.lua` is commented in the file; this page covers the ones that need more than a comment.

### General

| Option                 | Default | Notes                                                                                                      |
| ---------------------- | ------- | ---------------------------------------------------------------------------------------------------------- |
| `RestoreVehicleDamage` | `true`  | Doors, windows and tyres come out as they were stored. With `false` only engine and body health do.        |
| `UniqueGarages`        | `false` | A vehicle can only be taken out of the garage it was stored in. Moving it to another costs `TransferCost`. |
| `ReturnVehicleCost`    | `5000`  | Fee to get back a vehicle that was destroyed or despawned on the street. `0` disables it.                  |
| `TransferToPlayerCost` | `false` | Price to transfer a vehicle to another player. `false` removes the option from the UI.                     |

<figure><img src="https://3876653196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwqRMsiuM4yL1gQLB2nlA%2Fuploads%2Fgit-blob-ca6e3b6723ece29e40d78a7b22286993b6f4592b%2F06-transfer.jpg?alt=media" alt="Transfer to another player, from the vehicle card."><figcaption><p>Transfer to another player, from the vehicle card.</p></figcaption></figure>

Models listed in `TransferBlacklist` cannot be transferred to players. To block whole categories, see `AddToBlackListTransfer()` in `config/sv-functions.lua`.

In the prompts (`OpenGarage`, `SaveVehicle`, `Impound`), `icon` is optional: a Font Awesome name, or a table keyed by garage type.

### Public garages

<figure><img src="https://3876653196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwqRMsiuM4yL1gQLB2nlA%2Fuploads%2Fgit-blob-dbcc7d39b10db36adca9a25fb476cca0e983bc25%2F01-garage-list.jpg?alt=media" alt="A public garage. The expanded card shows fuel, engine, body, and the trunk and glovebox items."><figcaption><p>A public garage. The expanded card shows fuel, engine, body, and the trunk and glovebox items.</p></figcaption></figure>

Add an entry to `Garages`:

```lua
['my_garage'] = {
    label = 'My Garage',
    coords = vec3(x, y, z),          -- where the player opens and stores
    spawn = vec4(x, y, z, heading),
    garageType = 'car',              -- 'car', 'air', 'sea' or 'all'
    blip = { show = true, sprite = 357, color = 3, scale = 0.5 },
    -- job = 'police',               -- optional: only this job
},
```

### Job garages

Entries go in `JobGarages`. There are two styles.

<figure><img src="https://3876653196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwqRMsiuM4yL1gQLB2nlA%2Fuploads%2Fgit-blob-e071916d861ada0f1a1b0e0221464af06eb7b9ae%2F02-job-fleet.jpg?alt=media" alt="A jobRegular garage with the fleet tab and the personal tab."><figcaption><p>A jobRegular garage with the fleet tab and the personal tab.</p></figcaption></figure>

**jobRegular** is a fleet written in the config. The vehicles are not in the database.

```lua
['police_garage'] = {
    label = 'Police Garage',
    coords = vec3(x, y, z),
    spawn = vec4(x, y, z, heading),
    garageType = 'car',
    style = 'jobRegular',
    job = { 'police', 'sheriff' },
    maxMods = true,
    vehicles = {
        { model = 'police', label = 'Police Cruiser', plate = 'LSPD001' },
        { model = 'police2', label = 'Police SUV', plate = 'LSPD002', rank = 4, liveries = { 0, 2 } },
    },
},
```

* The garage lists the vehicles in the order written.
* `rank`: minimum job grade to take the vehicle out. Optional.
* `liveries`: the livery indices offered for that model. Omit it to offer all of them; a single index is applied without opening the menu.

<figure><img src="https://3876653196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwqRMsiuM4yL1gQLB2nlA%2Fuploads%2Fgit-blob-06a08972a6cce7e3a252ce84c8a9df5ed8a937c2%2F03-livery.jpg?alt=media" alt="Livery selection when a fleet vehicle has more than one livery offered."><figcaption><p>Livery selection when a fleet vehicle has more than one livery offered.</p></figcaption></figure>

* Fleet vehicles are recognised by model. Anyone with the garage's job can drive, lock and start them, and nobody else can.
* `JobRegularUniquePlates = true` gives every spawn its own plate (the first three letters of `plate` plus random digits). Turn it on if your inventory keys trunks by plate, otherwise every fleet vehicle shares one trunk.

**jobOwned** holds personal vehicles that players hand over to the job with `/jobcar`. Every member of the job can then take them out.

```lua
['ballas_garage'] = {
    label = 'Ballas Garage',
    coords = vec3(x, y, z),
    spawn = vec4(x, y, z, heading),
    garageType = 'car',
    style = 'jobOwned',
    job = { 'ballas' },
},
```

**Personal tab.** `JobGaragesPersonalTab = true` (every job garage) or `ShowPersonalVehicles = true` (inside one garage) adds a second tab with the player's own vehicles, stored and taken out like in a public garage.

### Impound

<figure><img src="https://3876653196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwqRMsiuM4yL1gQLB2nlA%2Fuploads%2Fgit-blob-f12e1e15455ea4ef783ceeb23003617a104b5883%2F05-impound-form.jpg?alt=media" alt="The impound form: reason, duration, fee and lot."><figcaption><p>The impound form: reason, duration, fee and lot.</p></figcaption></figure>

```lua
ImpoundEnabled = true,
ImpoundCommand = 'impound',
ImpoundAuthorizedJobs = { 'police', 'sheriff' },
ImpoundMaxFee = 15000,
```

Lots go in `Impounds`, release times in `ImpoundTimeOptions`.

Authorized jobs see every impounded vehicle, 30 at a time, and the search box looks up plate or model on the server. Everyone else sees only their own vehicles and their job's.

<figure><img src="https://3876653196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwqRMsiuM4yL1gQLB2nlA%2Fuploads%2Fgit-blob-e9759314660a5773bdc825526461d7de02748110%2F04-impound-card.jpg?alt=media" alt="An impounded vehicle seen from a public garage: lot, officer, reason and release time."><figcaption><p>An impounded vehicle seen from a public garage: lot, officer, reason and release time.</p></figcaption></figure>

### Keys and hotwire

With `KeysSystem.enabled = true` the script handles keys by itself: the player gets them when the vehicle leaves the garage, `/givekeys` shares them, and a vehicle without keys can be hotwired if `Hotwire.enabled = true`.

To use another key script, set `KeysSystem.enabled = false` and uncomment the matching block in `GiveVehicleKeys` (`config/cl-functions.lua`). Blocks for qb-vehiclekeys, qbx\_vehiclekeys, jaksam and qs-vehiclekeys are already there.

{% hint style="warning" %}
In `GiveVehicleKeys(plate, vehicle)` use the `vehicle` argument, not `GetVehiclePedIsIn`. The function runs right after the spawn, before the player is inside.
{% endhint %}

`EngineSystem.enabled = false` removes the engine command and keybind, for servers that already have one.

### Trunk and glovebox preview

`EnableTrunkAndGloveBoxCheck = true` shows the trunk and glovebox items inside the vehicle card.

* **ox\_inventory**: needs the function below.
* **Other inventories**: read from the `trunk` and `glovebox` columns of the vehicle table, when your inventory keeps them there.

For ox\_inventory, add this at the end of `ox_inventory/modules/inventory/server.lua` (not the root `server.lua`):

```lua
function Inventory.GetItemsGarage(inv)
    if not inv then return nil end

    local inventory = Inventories[inv]
    if not inventory or not inventory.items then return nil end

    local cleanItems = {}
    for slot, item in pairs(inventory.items) do
        if item and item.name then
            cleanItems[#cleanItems + 1] = {
                name = item.name,
                count = item.count or 1,
                slot = item.slot or slot,
            }
        end
    end

    return cleanItems
end

exports('GetItemsGarage', Inventory.GetItemsGarage)
```

### Other files

| File                      | What it holds                                                                                           |
| ------------------------- | ------------------------------------------------------------------------------------------------------- |
| `config/cl-functions.lua` | `Notify`, `GiveVehicleKeys`, `RemoveVehicleKeys`, `VehicleGetFuel`, `VehicleSetFuel`, `CanAccessGarage` |
| `config/sv-functions.lua` | `AddToBlackListTransfer`                                                                                |
| `config/webhooks.lua`     | One Discord webhook per log type. Empty string disables it.                                             |
| `config/locales/`         | `en` and `pt-pt`. Copy one to add a language; missing keys fall back to English.                        |
| `config/framework/`       | Table and column names, inventory detection, image paths.                                               |

`CanAccessGarage()` runs before any garage opens. Return `false` to block it.

***

## 💬 Commands

| Command                   | Who             | Description                                  |
| ------------------------- | --------------- | -------------------------------------------- |
| `/vehicle-add [id]`       | Admin           | Add a vehicle to a player's garage           |
| `/vehicle-delete [plate]` | Admin           | Delete a vehicle from the database           |
| `/vehicle-plate`          | Admin           | Change a vehicle's plate                     |
| `/vehicle-keys`           | Admin           | Get keys for the vehicle you are in          |
| `/givekeys [id]`          | Everyone        | Give keys to a nearby player                 |
| `/togglelocks`            | Everyone        | Lock or unlock (default key `L`)             |
| `/engine`                 | Everyone        | Engine on or off (default key `G`)           |
| `/jobcar`                 | Job members     | Hand a personal vehicle over to a job garage |
| `/impound`                | Authorized jobs | Impound a nearby vehicle                     |

Every name can be changed in `config/config.lua`. Admin commands need the group in `AdminCommands.requiredGroup`.

***

## 🔌 Integration

### Keys

Only with `KeysSystem.enabled = true`.

```lua
-- server
exports.s_garages:GiveKeys(playerId, plate)
exports.s_garages:RemoveKeys(playerId, plate)
local has = exports.s_garages:HasKeys(playerId, plate)

-- client
local has = exports.s_garages:HasKeys(plate)
TriggerServerEvent('s_garages:keys:acquire', plate)
```

{% hint style="warning" %}
`s_garages:keys:acquire` only gives keys for a vehicle the server can see within 7.5 m of the player. For a vehicle that is not spawned yet, or one created only on the client (`CreateVehicle` with `isNetwork = false`), use the `GiveKeys` server export.
{% endhint %}

The lock state of a vehicle is in `Entity(vehicle).state.sg_lock` (`1` unlocked, `2` locked).

### Vehicle state

For dealerships, rentals or a valet: tell the garage that a vehicle is on the street or put away, instead of writing the state column.

```lua
-- server
exports.s_garages:MarkVehicleOut(plate)
exports.s_garages:MarkVehicleStored(plate)
local out = exports.s_garages:IsVehicleOut(plate)
local plates = exports.s_garages:GetOutVehicles()   -- { [plate] = true }

-- client
exports.s_garages:SetVehicleOutside(plate)           -- only for a vehicle the player owns
```

### External garages

A housing script, for example, can open a garage at its own coords.

```lua
-- client
exports.s_garages:OpenExternalGarage({
    scopeName = 'house_42',                            -- unique name of this garage
    spawn = { x = 0.0, y = 0.0, z = 0.0, w = 0.0 },    -- w = heading
    label = 'Home Garage',                             -- optional
    garageType = 'car',                                -- optional, default 'car'
})

exports.s_garages:StoreExternalGarage({
    scopeName = 'house_42',
    spawn = { x = 0.0, y = 0.0, z = 0.0, w = 0.0 },
    garageType = 'car',
})
```

{% hint style="warning" %}
`s_garages` does not check who may use an external garage. Call these exports only for players your script has already authorized.
{% endhint %}

With `UniqueGarages = true` the garage lists only the vehicles stored under its `scopeName`. With `false` it lists all of the player's vehicles.

***

## 🗂️ Database

|               | ESX              | QBCore / QBX      |
| ------------- | ---------------- | ----------------- |
| Vehicle table | `owned_vehicles` | `player_vehicles` |
| Owner column  | `owner`          | `citizenid`       |
| Props column  | `vehicle`        | `mods`            |
| State column  | `stored`         | `state`           |

Different names on your server? Change them in `config/framework/sv-framework.lua`.

State values: `0` on the street, `1` stored, `2` impounded.

Columns the script adds: `s_garage_nickname`, `s_garage_favorite`, `s_garage_name`, `s_garage_impound_data`, `s_garage_impound_garage`, `s_garage_jobowned`, `s_garage_return_cost`, `s_garage_lastused`.
