# GARAGE V2

## ⚠️ ACE Permissions Setup (READ THIS FIRST!)

**YOU MUST ADD THIS TO YOUR `server.cfg` OR THE `/creategarage` COMMAND WILL NOT WORK!**

```cfg
# Add this line to your server.cfg to allow admins to create garages
add_ace group.admin risk-garagev2.creategarage allow
```

**Without this ACE permission:**

* The `/creategarage` command will do nothing
* You cannot create any garages, tow yards, or job garages
* The garage manager UI will not open

**How to use after adding the permission:**

1. Restart your server OR type `refresh` and `ensure risk-garagev2`
2. Join the server as an admin
3. Type `/creategarage` in chat
4. The garage manager UI will open

**Common mistakes:**

* ❌ Forgetting to add the ACE line → Command does nothing
* ❌ Adding it to `config.lua` instead of `server.cfg` → Won't work
* ❌ Not restarting after adding → Server doesn't recognize permission
* ✅ Add to `server.cfg`, restart server, use `/creategarage`

***

### Overview

Risk Garage V2 is a comprehensive vehicle management system that provides:

**Core Features:**

* **Multiple garage types:** Public garages, towing yards, and job-specific garages
* **Vehicle class system:** Separate garages for cars, planes/helis, and boats
* **In-game garage creation:** Admins can create and manage garages using `/creategarage` command
* **Police impound system:** Officers can impound vehicles with fines and lock timers
* **Persistent state tracking:** Fuel, damage, and customization preserved across spawns
* **Modern UI:** Responsive interface with vehicle search, favorites, and editing
* **Multi-framework:** Works with ESX, QBCore, and QBox (auto-detected)

**User Access:**

* ✅ You have access to: `config.lua` only
* ❌ You do NOT have access to: `client.lua`, `server.lua`

All configuration is done through `config.lua`. Backend logic is protected.

***

### Vehicle Class System

Every garage, tow yard, and job garage is bound to **ONE vehicle class**. This is chosen when creating the garage and cannot be changed later (you must delete and recreate).

**Available classes:**

| Class   | Description                                | Examples                                  |
| ------- | ------------------------------------------ | ----------------------------------------- |
| `car`   | All land vehicles (excluding planes/helis) | Cars, bikes, trucks, motorcycles          |
| `plane` | Planes AND helicopters                     | Any aircraft (both fixed-wing and rotary) |
| `boat`  | Boats and water vehicles                   | Boats, jet skis, submarines               |

**How it works:**

* When creating a garage with `/creategarage`, you choose the class during setup
* The garage will ONLY show vehicles of that class
* NPCs, blips, and markers are configured per-class (see configuration sections below)
* Vehicles are auto-detected based on their model using native GTA checks

**Class override (rarely needed):**

```lua
-- Only use if you have a custom vehicle that doesn't classify correctly
Config.ClassByModel = {
    -- ['my_addon_heli'] = 'plane',
    -- ['custom_yacht']  = 'boat',
}
```

***

### General Configuration

```lua
Config.Debug  = false                          -- true = print debug info to console, false = silent
Config.Locale = 'en'                           -- Check -> risk-garagev2\locales

Config.SpawnVehicleModel   = 'sultan'          -- fallback vehicle model used as transparent preview during spawn point placement
Config.SpawnVehicleModelByClass = {            -- per-class transparent preview model used while placing spawn points
    car   = 'sultan',                          -- model for car garages
    plane = 'cuban800',                        -- model for plane garages
    boat  = 'dinghy',                          -- model for boat garages
}

Config.MaxSpawnPoints      = 5                 -- max spawn points per garage
Config.TowingYardFee       = 500               -- price in dollars to retrieve a vehicle from a tow yard (per retrieval)
Config.NearbyVehicleRadius = 20.0              -- radius in meters to scan for nearby owned vehicles in the "Einparken" tab
Config.NpcInteractDistance = 2.5               -- distance in meters to trigger E-key interaction with ANY garage NPC

-- Values that mean "no job vehicle" (= personal vehicle)
Config.PersonalNoJobValues = { '', 'unemployed' }
```

**Placement controls (used during `/creategarage`):**

```lua
Config.NpcPlacementDistance  = 3.0             -- initial distance from the player when placement starts
Config.PlacementMinDistance  = 1.0             -- minimum distance allowed while dragging the NPC
Config.PlacementMaxDistance  = 12.0            -- maximum distance allowed while dragging the NPC
Config.PlacementSensitivity  = 0.5             -- mouse sensitivity for distance changes (lower = slower)
Config.PlacementRotationStep = 15.0            -- degrees the NPC rotates per scroll tick
Config.SpawnRotationStep     = 6.0             -- degrees the spawn vehicle rotates per scroll tick (finer for precise alignment)
```

***

### Main Garage (Public Garages)

Public garages accessible by all players. Players can store and retrieve their personal vehicles.

#### Mode System

```lua
Config.MainGarage = {
    Mode = 'global',  -- 'global' = retrieve vehicle at any main garage
                      -- 'local'  = only at the garage where it was last parked
```

**Mode comparison:**

| Mode     | Vehicle retrieval | Use case                    |
| -------- | ----------------- | --------------------------- |
| `global` | Any main garage   | Convenience-focused servers |
| `local`  | Only where parked | Realism-focused servers     |

#### NPC Models (Per Class)

```lua
    Npc = {
        car   = { Model = 'a_m_m_prolhost_01' }, -- ped model for car garages
        plane = { Model = 's_m_y_pilot_01'   }, -- ped model for plane/heli garages
        boat  = { Model = 's_m_m_dockwork_01' }, -- ped model for boat garages
    },
```

#### Blip Configuration (Per Class)

```lua
    Blip = {
        car = {
            Enabled    = true,                 -- true = show a map blip, false = no blip
            Label      = 'Garage',             -- label shown on the map
            Sprite     = 357,                  -- blip sprite id (357 = car garage)
            Color      = 3,                    -- blip color id (indexed, 0-85)
            Scale      = 0.8,                  -- blip size on the map (1.0 = default)
            Display    = 4,                    -- 2 = main map + minimap, 4 = minimap + main map
            ShortRange = true,                 -- true = only visible when close by
        },
        plane = {
            Enabled    = true,
            Label      = 'Plane Garage',
            Sprite     = 307,                  -- 307 = plane / heli pad
            Color      = 3,
            Scale      = 0.8,
            Display    = 4,
            ShortRange = true,
        },
        boat = {
            Enabled    = true,
            Label      = 'Boat Garage',
            Sprite     = 427,                  -- 427 = boat dock
            Color      = 3,
            Scale      = 0.8,
            Display    = 4,
            ShortRange = true,
        },
    },
```

**Blip sprite reference:** <https://docs.fivem.net/docs/game-references/blips/>

#### Marker Configuration (Per Class)

```lua
    Marker = {
        car = {
            Enabled      = false,              -- true = draw a ground marker, false = no marker
            Type         = 1,                  -- marker type: 1 = cylinder, 2 = up-arrow, 27 = flat ring
            Size         = vector3(1.5, 1.5, 1.0), -- marker size on x, y, z
            Color        = { r = 19, g = 255, b = 58, a = 100 }, -- RGBA 0-255
            Bob          = false,              -- true = bounce up and down
            Rotate       = false,              -- true = rotate around z axis
            DrawDistance = 25.0,               -- meters from NPC within which the marker is drawn
        },
        plane = {
            Enabled      = false,
            Type         = 1,
            Size         = vector3(1.5, 1.5, 1.0),
            Color        = { r = 84, g = 180, b = 255, a = 100 }, -- sky blue
            Bob          = false,
            Rotate       = false,
            DrawDistance = 25.0,
        },
        boat = {
            Enabled      = false,
            Type         = 1,
            Size         = vector3(1.5, 1.5, 1.0),
            Color        = { r = 64, g = 220, b = 235, a = 100 }, -- water cyan
            Bob          = false,
            Rotate       = false,
            DrawDistance = 25.0,
        },
    },
}
```

**Marker type reference:** <https://docs.fivem.net/docs/game-references/markers/>

***

### Job Garage (Private Depot)

Private garages visible only to players with matching job. Vehicles are stored per-job.

#### Configuration

```lua
Config.JobGarage = {
    Mode = 'global',  -- 'global' = retrieve at any job garage of that job
                      -- 'local'  = only at the garage where it was last parked

    Npc = {
        car   = { Model = 's_m_m_fibsec_01'  }, -- FIB security
        plane = { Model = 's_m_y_pilot_01'   },
        boat  = { Model = 's_m_m_dockwork_01' },
    },

    Blip = {
        car = {
            Enabled    = true,
            Label      = 'Job Garage',
            Sprite     = 357,
            Color      = 3,
            Scale      = 0.8,
            Display    = 4,
            ShortRange = true,
        },
        -- plane and boat configs similar to Main Garage
    },

    Marker = {
        car = {
            Enabled        = false,
            Type           = 1,
            Size           = vector3(1.5, 1.5, 1.0),
            Color          = { r = 66, g = 183, b = 255, a = 100 },
            Bob            = false,
            Rotate         = false,
            DrawDistance   = 25.0,
            UseGarageColor = true,             -- true = use the per-garage color picked during /creategarage
        },
        -- plane and boat configs...
    },
}
```

**Job garage colors:**

* When `UseGarageColor = true`, the marker uses the color you pick during garage creation
* Each job garage can have its own custom color (police = blue, ambulance = red, etc.)

***

### Tow Yard (Impound Retrieval)

Players retrieve impounded vehicles here by paying the configured fee.

#### Configuration

```lua
Config.TowYard = {
    -- No mode setting - tow yards always list the player's impounded vehicles

    Npc = {
        car   = { Model = 's_m_y_xmech_02'   }, -- mechanic
        plane = { Model = 's_m_y_pilot_01'   },
        boat  = { Model = 's_m_m_dockwork_01' },
    },

    Blip = {
        car = {
            Enabled    = true,
            Label      = 'Towing Yard',
            Sprite     = 68,                   -- 68 = tow truck
            Color      = 17,                   -- 17 = orange
            Scale      = 0.8,
            Display    = 4,
            ShortRange = true,
        },
        -- plane and boat configs...
    },

    Marker = {
        car = {
            Enabled      = false,
            Type         = 1,
            Size         = vector3(1.5, 1.5, 1.0),
            Color        = { r = 255, g = 164, b = 84, a = 100 }, -- tow orange
            Bob          = false,
            Rotate       = false,
            DrawDistance = 25.0,
        },
        -- plane and boat configs...
    },
}
```

***

### Police Impound System

Officers can drive vehicles into impound zones and set reason, fine, and lock timer.

#### Configuration

```lua
Config.Impound = {
    AllowedJobs = {                            -- jobs allowed to impound + minimum grade
        police  = 0,                           -- 0 = any grade can impound
        sheriff = 0,
    },

    ReasonPresets = {                          -- reason presets officer cycles through
        'Illegally parked',
        'Abandoned vehicle',
        'Evidence',
        'Stolen vehicle',
    },

    FinePresets = {                            -- fine amount presets in dollars
        500,
        1500,
        5000,
        15000,
    },

    LockPresets = {                            -- lock duration presets in hours
        0,                                     -- 0 = no lock, owner can free-buy immediately
        1,
        6,
        12,
        24,
        48,
    },

    FineDestination = 'society',               -- 'society' = deposit into job account (e.g. society_police)
                                               -- 'void'    = removed from the world

    Zones = {                                  -- zones where officers can impound (drive into radius + press E)
        { coords = vector3(474.1797, -1022.2791, 29.1014), radius = 2.5 },
        -- Add more impound zones here
    },

    Marker = {                                 -- marker drawn at each impound zone
        type   = 39,
        size   = vector3(1.0, 1.0, 1.0),
        color  = { r = 0, g = 0, b = 225, a = 120 },
        bob    = false,
        rotate = false,
    },
}
```

**How it works:**

1. Officer drives vehicle into impound zone
2. Presses `E` to open impound menu
3. Selects reason, fine, and lock duration
4. Clicks "IMPOUND VEHICLE"
5. Vehicle is removed from world and marked as impounded
6. Owner must go to tow yard and pay fine to retrieve (if not locked)
7. If locked, owner must wait until lock timer expires

**Lock system:**

* `0 hours` = No lock, owner can pay fine immediately
* `1-48 hours` = Owner must wait X hours before they can pay fine
* Lock timer is real-time (not in-game time)

***

### Creating Garages (Admin Guide)

#### Step 1: Add ACE Permission to server.cfg

```cfg
add_ace group.admin risk-garagev2.creategarage allow
```

Restart your server.

#### Step 2: Use /creategarage Command

1. Type `/creategarage` in chat
2. Garage Manager UI opens

#### Step 3: Click "+ NEW GARAGE"

Choose garage type:

* **GARAGE** = Public garage (all players)
* **TOWING YARD** = Impound retrieval location
* **JOB GARAGE** = Job-specific garage

#### Step 4: Choose Vehicle Class

* **CARS** = All land vehicles
* **PLANES & HELIS** = Aircraft
* **BOATS** = Water vehicles

#### Step 5: Enter Name

* For GARAGE: "Legion Square Garage"
* For TOW YARD: "LSPD Impound"
* For JOB GARAGE: "Police Mission Row Garage"

*For job garages, you'll also pick the job and color.*

#### Step 6: Place NPC

* **Mouse** = Move NPC position
* **Scroll** = Rotate NPC
* **Enter** = Confirm position
* **ESC** = Cancel

#### Step 7: Place Spawn Points (0-5)

* **Mouse** = Move spawn vehicle preview
* **Scroll** = Rotate spawn vehicle
* **E** = Add spawn point (max 5)
* **Enter** = Finish (minimum 1 spawn point required)
* **ESC** = Cancel

**Tips for spawn points:**

* Place them away from walls/obstacles
* Leave space between spawn points
* For boats: place in water
* For planes: place on runway or helipad
* Test by spawning a vehicle after creation

#### Managing Garages

**Garage Manager features:**

* **SAVE** = Rename garage (click, edit name, click SAVE)
* **EDIT** = Reposition NPC and spawn points
* **DEL** = Delete garage (confirmation required)
* **TP** = Teleport to garage location

***

### Identifier Override (Multicharacter)

**Default behavior works for:**

* ✅ Standard ESX
* ✅ ESX with esx\_multicharacter
* ✅ Standard QBCore
* ✅ Standard QBox

**Only override if:**

* Your custom multichar system stores a different identifier format
* Database `owner` / `citizenid` column doesn't match framework default

```lua
-- Example 1: Custom multichar with metadata
Config.GetIdentifier = function(src)
    local ESX = exports['es_extended']:getSharedObject()
    local xPlayer = ESX.GetPlayerFromId(src)
    if not xPlayer then return nil end
    local meta = xPlayer.getMeta and xPlayer.getMeta('charIdentifier')
    if meta and meta ~= '' then return meta end
    return xPlayer.getIdentifier()
end

-- Example 2: Direct license lookup (slow, last resort)
Config.GetIdentifier = function(src)
    for _, id in ipairs(GetPlayerIdentifiers(src)) do
        if id:sub(1, 8) == 'license:' then return id end
    end
    return nil
end
```

**Leave as `nil` if your setup works by default:**

```lua
Config.GetIdentifier = nil  -- Use framework default (recommended)
```

***

### Notify / HelpNotify System

Swap in your custom notification system:

```lua
Config.UseCustomNotify     = false  -- true = use custom notify below
Config.UseCustomHelpNotify = false  -- true = use custom helpnotify below

Config.Functions = {
    ["notify"] = function(ntype, title, text, time)
        exports['risk-notify']:Notify({
            type = ntype or 'info',
            message = text,
            title = title or 'Notify',
            duration = time or 10000
        })
    end,
    ["helpnotify"] = function(key, text)
        exports["risk-notify"]:HelpNotify(key, text)
    end,
}
```

**Parameters:**

* `ntype`: `'success'`, `'error'`, `'info'`, `'warning'`
* `title`: Notification title
* `text`: Notification message
* `time`: Duration in milliseconds
* `key`: Key name for help notify (e.g., `"E"`)

***

### Fuel System Integration

Default is LegacyFuel. Swap for your fuel system:

```lua
Config.FuelFunctions = {
    ["GetFuel"] = function(vehicle)
        return exports['LegacyFuel']:GetFuel(vehicle)
    end,
    ["SetFuel"] = function(vehicle, amount)
        exports['LegacyFuel']:SetFuel(vehicle, amount)
    end,
}
```

**Common alternatives:**

**ps-fuel:**

```lua
["GetFuel"] = function(vehicle)
    return exports['ps-fuel']:GetFuel(vehicle)
end,
["SetFuel"] = function(vehicle, amount)
    exports['ps-fuel']:SetFuel(vehicle, amount)
end,
```

**ox\_fuel:**

```lua
["GetFuel"] = function(vehicle)
    return GetVehicleFuelLevel(vehicle)
end,
["SetFuel"] = function(vehicle, amount)
    Entity(vehicle).state.fuel = amount
end,
```

***

### Persistent Parking Integration

Sync with scripts that keep vehicles in world across restarts (e.g., AdvancedParking, Renewed-Vehiclesystem).

```lua
Config.PersistentParking = {
    Enabled = false,  -- true = enable hooks below

    -- Server-side: called when vehicle is parked/impounded
    -- Tell external script to FORGET this plate
    OnPark = function(plate)
        exports['AdvancedParking']:DeleteVehicleUsingData(nil, nil, plate, true)
    end,

    -- Client-side: called after vehicle spawns from garage
    -- Tell external script to TRACK this vehicle
    OnSpawn = function(veh, plate)
        exports['AdvancedParking']:UpdateVehicle(veh)
    end,
}
```

**How it works:**

1. Player parks vehicle in garage
2. `OnPark` fires → External script removes vehicle from world
3. Player spawns vehicle from garage
4. `OnSpawn` fires → External script starts tracking again

***

### Complete Examples

#### Example 1: Casual RP Server

**Features:**

* Global mode (retrieve anywhere)
* Public garages everywhere
* Low tow fee
* Minimal impound penalties

```lua
Config.MainGarage = { Mode = 'global' }
Config.JobGarage = { Mode = 'global' }
Config.TowingYardFee = 250
Config.Impound = {
    AllowedJobs = { police = 0 },
    FinePresets = { 250, 500, 1000, 2500 },
    LockPresets = { 0, 1, 3, 6 },
    FineDestination = 'void',
}
```

#### Example 2: Serious RP Server

**Features:**

* Local mode (retrieve only where parked)
* High tow fee
* Strict impound system
* Fines go to police society

```lua
Config.MainGarage = { Mode = 'local' }
Config.JobGarage = { Mode = 'local' }
Config.TowingYardFee = 1500
Config.Impound = {
    AllowedJobs = { police = 1, sheriff = 1 },  -- Only grade 1+
    FinePresets = { 1000, 3000, 7500, 15000 },
    LockPresets = { 0, 6, 12, 24, 48 },
    FineDestination = 'society',
}
```

#### Example 3: Economy-Focused Server

**Features:**

* Dynamic tow fees
* Society integration
* High penalties for illegal parking

```lua
Config.TowingYardFee = 2500
Config.Impound = {
    AllowedJobs = { police = 0, sheriff = 0, marshals = 0 },
    ReasonPresets = {
        'Illegally Parked - Zone 1',
        'Abandoned - Public Road',
        'Crime Evidence',
        'Stolen Vehicle Recovery',
    },
    FinePresets = { 500, 2500, 7500, 25000 },
    LockPresets = { 0, 3, 12, 24, 72 },
    FineDestination = 'society',
}
```

***

### Troubleshooting

#### `/creategarage` command does nothing

**Cause:** ACE permission missing\
**Solution:** Add `add_ace group.admin risk-garagev2.creategarage allow` to `server.cfg` and restart

#### Vehicles don't show in garage UI

**Cause 1:** Wrong vehicle class selected\
**Solution:** Delete garage, recreate with correct class (car/plane/boat)

**Cause 2:** Identifier mismatch (multicharacter)\
**Solution:** Enable `Config.Debug = true`, check console for identifier, compare with database `owner` / `citizenid` column

**Cause 3:** Job column mismatch\
**Solution:** Check `Config.PersonalNoJobValues`, add your server's default job value

#### Vehicle spawns damaged

**Cause:** Stored damage state from last park\
**Solution:** Working as intended. Vehicle damage is preserved. Player must repair before parking or accept damage.

#### Impound menu doesn't open for officers

**Cause 1:** Job not in `Config.Impound.AllowedJobs`\
**Solution:** Add job name to AllowedJobs with grade requirement

**Cause 2:** Grade too low\
**Solution:** Lower grade requirement (e.g., `police = 0` instead of `police = 2`)

#### Blips don't appear on map

**Cause:** `Enabled = false` in blip config\
**Solution:** Set `Enabled = true` for the class you're using

#### Marker doesn't show at garage

**Cause:** `Enabled = false` in marker config\
**Solution:** Set `Enabled = true` for the class, adjust DrawDistance if needed

***

### Best Practices

**Garage placement:**

* ✅ Place garages near popular spawn points
* ✅ Separate car/plane/boat garages logically (cars in city, planes at airport, boats at docks)
* ✅ Test spawn points by spawning vehicles after creation
* ❌ Don't place spawn points too close to walls or other vehicles
* ❌ Don't overlap impound zones with garage zones

**Vehicle class usage:**

* ✅ Use `car` class for standard parking lots
* ✅ Use `plane` class at airports/helipads only
* ✅ Use `boat` class at marinas/docks only
* ❌ Don't mix classes in one location

**Impound system:**

* ✅ Set realistic lock timers (1-24 hours typical)
* ✅ Use `society` destination for police funding
* ✅ Place impound zones at police stations
* ❌ Don't set extremely long locks (48+ hours discourages players)
* ❌ Don't make fines too high (players quit)

**Job garages:**

* ✅ Use custom colors per job (blue for police, red for ambulance)
* ✅ Place at job headquarters
* ✅ Create multiple spawn points for busy jobs
* ❌ Don't use same color for all job garages

**Performance:**

* ✅ Keep `Config.Debug = false` in production
* ✅ Use markers sparingly (high DrawDistance = more expensive)
* ✅ Limit garages to \~20-30 total across all types
* ❌ Don't enable markers everywhere (use blips instead)

***

### Quick Start Checklist

**Server setup:**

* \[ ] Add ACE permission to `server.cfg`: `add_ace group.admin risk-garagev2.creategarage allow`
* \[ ] Restart server
* \[ ] Configure `Config.Locale` (if not using English)
* \[ ] Configure `Config.TowingYardFee`
* \[ ] Configure `Config.Impound.AllowedJobs`

**Create main garages:**

* \[ ] Join server as admin
* \[ ] Type `/creategarage`
* \[ ] Click "+ NEW GARAGE"
* \[ ] Choose "GARAGE"
* \[ ] Choose vehicle class (`car` for first garage)
* \[ ] Enter name (e.g., "Legion Square Garage")
* \[ ] Place NPC (Mouse to position, Scroll to rotate, Enter to confirm)
* \[ ] Place spawn points (E to add, Enter when done)
* \[ ] Test by spawning a vehicle

**Create tow yard:**

* \[ ] Type `/creategarage`
* \[ ] Choose "TOWING YARD"
* \[ ] Choose vehicle class (usually `car`)
* \[ ] Enter name (e.g., "LSPD Impound")
* \[ ] Place NPC and spawn points

**Create job garage (optional):**

* \[ ] Type `/creategarage`
* \[ ] Choose "JOB GARAGE"
* \[ ] Choose vehicle class
* \[ ] Select job from dropdown
* \[ ] Pick color
* \[ ] Enter name (e.g., "Police Garage")
* \[ ] Place NPC and spawn points

**Configure impound zones:**

* \[ ] Edit `Config.Impound.Zones` in `config.lua`
* \[ ] Add coordinates and radius for each impound location
* \[ ] Test by driving vehicle into zone as police officer

**Test everything:**

* \[ ] Park vehicle in garage (Einparken tab)
* \[ ] Retrieve vehicle from garage (Ausparken tab)
* \[ ] Impound vehicle as police officer
* \[ ] Retrieve impounded vehicle from tow yard
* \[ ] Test job garage access (only correct job can see it)
* \[ ] Verify fuel/damage preservation


---

# Agent Instructions: 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://risk-scripts.gitbook.io/risk-scripts/scripts/garage-v2.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.
