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

# THE TRAIN JOB

Freight train job with crane stations, random routes, an ingame step by step   route builder, a real rail timetable, a battle pass style level system and a   buyable autopilot. Supports ESX, QBCore an

## Train Job

### Overview

Risk Train Job puts your players in the cab of a freight train. They pick up the train somewhere on the map, drive it over the real GTA rails, stop at crane stations and move the containers with the crane themselves.

| Feature                      | What it does                                                                                                                                   |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| **Random routes**            | Every shift picks one of your routes at random. The train waits somewhere else on the map every time — the player has to get there first       |
| **Route builder**            | `/trainroute` guides you step by step: train spawn, up to 4 cranes, optional end point. At the end you copy the finished route into the config |
| **Crane stations**           | The camera turns to the side, the player shunts the wagon under the crane and unloads or loads the container                                   |
| **Unload and load stations** | Unload stations lift containers off the train, load stations put new ones on the empty wagons                                                  |
| **Rail timetable**           | Arrival times come from the real length of the rails, not from the straight line — detours around hills and bays are timed correctly           |
| **Bonuses**                  | Perfect / good crane alignment and arriving on time pay extra, arriving late costs a percentage                                                |
| **Levels**                   | 20 levels with money and item rewards like a battle pass. Higher levels drive longer trains (2 to 8 wagons)                                    |
| **Autopilot**                | Unlocked by a level and bought once. Drives the train and stops exactly at every crane                                                         |
| **Driver log**               | Career stats and the last shifts of every character                                                                                            |
| **Own HUD**                  | Speedometer, gears, timetable countdown and a braking assistant before every station                                                           |
| **Hidden drivers**           | Players on a shift do not see each other's trains, so nobody blocks the rails for anybody else                                                 |
| **Multi-framework**          | ESX, QBCore and QBox, auto-detected                                                                                                            |

### Requirements

* `oxmysql`, `mysql-async` or `ghmattimysql` — detected automatically
* ESX, QBCore or QBox — detected automatically
* Optional: `ox_inventory`, `codem-inventory`, `origen_inventory` or `qb-inventory` for item rewards — the framework inventory is used otherwise
* Optional: `risk-notify` if you want to use the custom notify functions

{% hint style="info" %}
The database tables are created automatically on the first start. `trainjob.sql` is only there for manual setup or to review the schema.
{% endhint %}

### User Access

Everything you need is in these files.

| File                      | What it is for                                                                   |
| ------------------------- | -------------------------------------------------------------------------------- |
| `config/config.lua`       | Every setting of the job, fully commented                                        |
| `config/routes.lua`       | Who may use the route builder, the depot NPC and all train routes                |
| `locales/*.lua`           | en / de — every text of the job, add your own language here                      |
| `inventory/inventory.lua` | How level reward items are given — add your inventory here if it is not detected |
| `database/database.lua`   | Which MySQL resource is used — detected automatically                            |
| `trainjob.sql`            | The database schema                                                              |

* The whole interface takes its color from `Config.UIColor` — no CSS work needed.
* Every text in the interface comes from `locales/*.lua`.
* Routes are built ingame and pasted into `config/routes.lua`. You never measure or type distances yourself.

{% hint style="success" %}
If something is not covered by a config option, open a ticket and we will add one.
{% endhint %}

***

### Setup Order

{% hint style="danger" %}
**Read this before anything else.**

The route builder `/trainroute` only works for admins. Make sure you are allowed — the easiest way is to put your own license into `Config.Setup.Licenses` in `config/routes.lua`. Your license is shown in txAdmin under **Players**.

Without permission the builder answers with **"You are not allowed to use the route builder"**, and the server console prints a line that tells you exactly which check failed.
{% endhint %}

1. Add the resource and start it — the database tables are created automatically
2. Open `config/routes.lua` and put your license into `Config.Setup.Licenses` (or check `Config.Setup.Groups`)
3. Set `Config.Depot` to the place where your players should start their shifts
4. Restart the resource and type `/trainroute` ingame
5. Build your routes step by step and paste each one into `Config.Routes`
6. Restart the resource and drive one shift on every route
7. Set `Config.Setup.Enabled = false` when your routes are done

The script ships with one example route. It works out of the box — keep it, change it or delete it.

***

### General Configuration

```lua
-- Debug prints in the F8 / server console (true = on, false = clean production mode)
Config.Debug = false

-- Language. Must match a file name in the locales/ folder without .lua ("en", "de")
Config.Locale = "en"

-- Account the wages are paid into: "bank" or "cash"
Config.PayAccount = "bank"

-- Minutes a player has to wait before the next shift, after finishing or ending one (0 = no cooldown)
Config.CooldownMinutes = 10

-- Minutes after which an unfinished shift is ended automatically (protects against stuck shifts)
Config.MaxShiftMinutes = 60

-- Teleports the player back to the depot when a shift ends (true = on, false = the player stays where the shift ended)
Config.TeleportOnEnd = true

-- If true, players on a shift do not see other players on a shift (and their trains).
-- Everybody else still sees all train drivers, and the drivers still see everybody else.
Config.HideOtherDrivers = true
```

{% hint style="warning" %}
`Config.MaxShiftMinutes` counts from the moment the shift is started at the depot — including the way to the train. The example route has about 46 minutes of timetable plus four crane stations. Give your longest route enough time, otherwise the shift ends in the middle of it.
{% endhint %}

#### UI Theme

One color drives the whole interface — briefing, HUD, crane view, notifications and the route builder.

```lua
-- Main color of the whole UI as a hex code (e.g. "#feff13" yellow, "#3fa9ff" blue, "#ff4fd8" pink).
Config.UIColor = "#ffff00"
```

#### Notifications

```lua
-- If true, use the custom notify function below; if false, use the built-in train job notify (signal style)
Config.UseCustomNotify = false
-- If true, use the custom helpnotify function below; if false, use the built-in train job key prompt (signal style)
Config.UseCustomHelpNotify = false

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,
}
```

With both switches on `false` the job uses its own notifications and key prompts. Set a switch to `true` and replace the export call with your own notify resource if you want the job to look like the rest of your server.

***

### Routes

A **route** is one train spawn, 1 to 4 crane stations and an optional end point. All routes start from the same depot NPC.

When a player opens the briefing, the script picks one of your routes at random and shows it. After **START SHIFT** the GPS leads the player to the train — it waits somewhere on the map and only appears when the player is close.

{% hint style="info" %}
**Why random?** The train waits in another place every shift, the stations change, and the job never feels like the same loop twice. The more routes you build, the more variety your players get.
{% endhint %}

#### The depot

```lua
-- The depot NPC: the only place where players open the briefing and start a shift
Config.Depot = {
    name   = "Port of Los Santos",                              -- name of the map blip
    coords = vector4(2619.3228, 1691.8204, 31.8694, 269.0218),  -- x, y, z, heading of the NPC
    blip   = true,                                              -- true = show the depot on the map
}
```

The heading is the direction the NPC looks. Players open the briefing by standing in front of him and pressing `E`. When a shift ends they are brought back here (`Config.TeleportOnEnd`).

#### A route in the config

```lua
-- Train routes, one is picked at random every shift. Build new ones with /trainroute (see above)
-- track = rail metres for the timetable, /trainroute writes them for you
Config.Routes = {
    {
        name      = "Northern Freight Loop",                   -- shown in the shift briefing
        spawn     = vector3(2611.1484, 1708.7343, 26.7912),   -- train spawn, must lie on the rails
        direction = false,                                    -- true / false, swap it if the train faces the wrong way
        stops = {                                             -- 1 - 4 crane stations in driving order, kind = "unload" or "load"
            { name = "Grapeseed Yard",     kind = "unload", crane = vector4(2978.5198, 3802.9573, 55.5991, 352.0550), track = 2262.2 },
            { name = "Paleto Bay Freight", kind = "load",   crane = vector4(1436.1984, 6402.1865, 32.8778, 259.0950), track = 5613.7 },
            { name = "Paleto Forest Mill", kind = "unload", crane = vector4(-476.7607, 5244.7993, 88.7085, 338.7629), track = 8288.1 },
            { name = "Tataviam Loading",   kind = "load",   crane = vector4(2201.9768, 1367.8318, 79.3935, 37.8949),  track = 16313.8 },
        },
        finish = { name = "Depot", coords = vector3(2611.1484, 1708.7343, 26.7912), track = 29679.3 }, -- optional end point, remove it to end at the last station
    },
}
```

| Field            | What it means                                                                                                                          |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `name`           | Shown in the briefing                                                                                                                  |
| `spawn`          | Where the train waits. Must lie on the rails — the builder places it for you                                                           |
| `direction`      | Which way the train faces. If it faces away from the first station, swap `true` / `false`                                              |
| `stops`          | The crane stations in the order the train reaches them                                                                                 |
| `name` (station) | Shown in the briefing, on the map and in the HUD — rename them freely                                                                  |
| `kind`           | `"unload"` = the crane lifts containers off the train, `"load"` = containers wait on the pad and the crane puts them on empty wagons   |
| `crane`          | Position and heading of the crane                                                                                                      |
| `track`          | Rail metres from the spawn to this point, used for the timetable. Written by the builder                                               |
| `finish`         | Optional. After the last station the player delivers the train there and the shift ends. Without it the shift ends at the last station |

{% hint style="warning" %}
Never type the `track` numbers by hand. They are measured along the rails by the route builder. Without them the timetable falls back to the straight line, which is far too short where the rails make a detour — the server console warns you on start.
{% endhint %}

{% hint style="info" %}
A route that another player is driving right now is skipped, so two trains never spawn inside each other. If every route is taken, the player gets **"All trains are out right now"**. More routes = more drivers at the same time.
{% endhint %}

#### How many containers a station handles

You do not set this anywhere, the script works it out for every shift:

* The train gets **2 to 8 loaded wagons**, depending on the player's level
* Every station handles **4 containers at most** — a crane pad holds 4
* The train starts fully loaded, the **unload stations share the wagons evenly**, every wagon is unloaded exactly once
* **Load stations refill the empty wagons**, again shared evenly and 4 at most per station
* A station that has nothing to do in this shift is skipped automatically

| Route layout                        | Max wagons |
| ----------------------------------- | ---------- |
| 1 unload                            | 4          |
| 1 unload + 1 load                   | 4          |
| 2 unload                            | 8          |
| **2 unload + 2 load (recommended)** | **8**      |

{% hint style="success" %}
**We recommend 4 stations: unload, load, unload, load.** That gives high level players the full 8 wagons and every station has work to do.
{% endhint %}

{% hint style="warning" %}
Two load stations directly after each other (`unload, load, load, unload`) waste one: the first load station fills every empty wagon, so the second one never has anything to do.
{% endhint %}

***

### Route Builder

The builder sits at the top of `config/routes.lua`:

```lua
-- Route builder: /trainroute opens and closes a small step by step panel (step 1 / 6). Press E at every step:
-- train spawn, stations, end point. At the end copy the route and paste it into Config.Routes below.
Config.Setup = {
    Enabled = true,                              -- true = /trainroute exists, set it to false when your routes are done
    Groups  = { "admin", "superadmin", "god" },  -- admin groups allowed to use them (ESX group, QBCore / QBox permission)
    Licenses = {                                 -- players allowed by their license, works on every framework
        "license:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    },
    Ace     = "risktrainjob.setup",              -- also allowed: everyone with this ace permission (optional, server.cfg)
                                                 -- players allowed to use all commands (add_ace ... command allow) always may
}
```

#### Who may use it

Four ways to be allowed — any one of them is enough:

| Check        | Who passes                                                                                                        |
| ------------ | ----------------------------------------------------------------------------------------------------------------- |
| `Licenses`   | Every license in the list. Works on every framework and is the safe way if your groups are set up differently     |
| `Groups`     | ESX: the `group` of the character (`/setgroup`). QBCore / QBox: the permission (`god`, `admin`)                   |
| All commands | Everyone with `add_ace <group> command allow` — the default admin line of every ESX, QBCore and QBox `server.cfg` |
| `Ace`        | Everyone you give the ace yourself, e.g. `add_ace group.moderator risktrainjob.setup allow`                       |

{% hint style="info" %}
When somebody is denied, the server console prints one line with his license, his group and the result of every check. Copy the license from there straight into `Licenses`.
{% endhint %}

#### Building a route

Type `/trainroute`. A small panel opens on the right and guides you step by step — **01 / 06**, **02 / 06** and so on. It only shows the current step and the keys you need for it.

The panel stays open until you type `/trainroute` again. No key, menu or txAdmin closes it, and closing it keeps your progress. Between the steps you walk, drive, fly or use noclip as usual.

| Step                  | What to do                                                                                                                                                                                                                                                 |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **1 — Train spawn**   | Stand right next to the rails where the train should wait and press `E`. A see-through preview train appears. `X` turns it around, `ENTER` keeps it. Turn it so the locomotive points towards your first station — once kept, the preview train disappears |
| **2 to 5 — Stations** | Go to the station, stand next to the rails and press `E`. A see-through crane follows your aim — place it and press `ENTER`. After the first station `G` jumps straight to the end point                                                                   |
| **6 — End point**     | Stand next to the rails where the train is delivered at the end of the shift and press `E` — or `ENTER` for no end point                                                                                                                                   |
| **Done**              | The panel shows the rail length and the timetable of the whole route. Press `M` for the mouse and click **COPY ROUTE**                                                                                                                                     |

Paste the copied block into `Config.Routes` in `config/routes.lua`, rename the stations ("Station 1", "Station 2" ...) and restart the resource.

#### Placing a crane

Two thick yellow bars show where the rails have to be. Move the crane until both bars stand on the rails.

| Key                     | Action                                                                   |
| ----------------------- | ------------------------------------------------------------------------ |
| Aim                     | Move the crane                                                           |
| `SCROLL`                | Rotate (`SHIFT` = faster)                                                |
| `PAGE UP` / `PAGE DOWN` | Height                                                                   |
| `X`                     | Switch between unload and load station                                   |
| `H`                     | Test lift — the crane lowers, grabs a test container and raises it again |
| `ENTER`                 | Place the crane                                                          |
| `BACKSPACE`             | Throw away this crane                                                    |

{% hint style="info" %}
If the test lift cannot reach the ground, the crane stands too high or on uneven ground. Adjust it with `PAGE UP` / `PAGE DOWN` and test again.
{% endhint %}

#### Keys that are always there

| Key                   | Action                                                                                              |
| --------------------- | --------------------------------------------------------------------------------------------------- |
| `Z`                   | Undo the last step. Press it **twice** — the first press only asks, so it never happens by accident |
| `M`                   | Mouse on — to copy the route or use the buttons. `ESC` or `M` turns it off again                    |
| **NEW ROUTE** (mouse) | Throws away the current route and starts from scratch                                               |

{% hint style="success" %}
`BACKSPACE` only ever throws away the preview train or the crane you are placing **right now**. It never removes anything you already saved.
{% endhint %}

{% hint style="warning" %}
Place the stations in the order the train reaches them. The builder measures the rails from the spawn through every crane to the end point — a wrong order makes the timetable wrong. The panel warns you when the preview train points the other way than your station order.
{% endhint %}

#### Measuring the routes in your config

Changed a coordinate by hand? Open `/trainroute`, press `M` and click **MEASURE CONFIG ROUTES**. Every route in `Config.Routes` is measured again and shown with fresh `track` numbers and its own copy button. Replace the old route block with the new one.

***

### Driving

#### Keys

```lua
-- Default keys. Every player can rebind them in GTA Settings > Key Bindings > FiveM
-- Gears: R = reverse, N = neutral, M = manual (hold throttle yourself), A = cruise control, AP = autopilot (bought, see Config.Autopilot)
Config.Keys = {
    Throttle       = "W",        -- hold to accelerate in M and R, raises the cruise speed in A
    Brake          = "S",        -- hold to brake, lowers the cruise speed in A
    GearUp         = "UP",       -- next gear (R -> N -> M -> A -> AP), arrow up
    GearDown       = "DOWN",     -- previous gear, arrow down
    EmergencyBrake = "X",        -- full emergency brake, releases itself when the train stands still
    EndShift       = "G",        -- opens the "end shift" dialog while you are on a shift
    ShuntLeft      = "A",        -- crane view: moves the train to the left of the screen
    ShuntRight     = "D",        -- crane view: moves the train to the right of the screen
    CraneAuto      = "E",        -- crane view: unloads / loads the aligned wagon automatically (lower, grab, raise, set down)
}
```

{% hint style="info" %}
These are only the defaults. Every player can change them in **GTA Settings > Key Bindings > FiveM**, and a player's own binding always wins.
{% endhint %}

#### Train physics

```lua
-- How the train drives. Speeds are in km/h
Config.Driving = {
    MaxSpeed       = 120,   -- top speed in km/h 
    ReverseSpeed   = 15,   -- top speed in km/h in the reverse gear (R)
    CruiseStep     = 5,    -- km/h the cruise speed changes per throttle / brake press in gear A 
    Acceleration   = 5.0,  -- km/h gained per second at full power with no wagons attached
    BrakeForce     = 7.5,  -- km/h lost per second at full brake with no wagons attached
    EmergencyBrake = 16.0, -- km/h lost per second with the emergency brake
    Coasting       = 0.25, -- km/h lost per second without throttle (rolling resistance)
    WeightPerWagon = 0.12, -- extra weight per wagon (0.12 = every wagon makes the train 12% heavier -> slower to accelerate and to brake)
}
```

A long train at a high level accelerates and brakes slower. That is intended — the braking assistant before every station shows when to start braking.

#### Autopilot

```lua
-- Autopilot (gear AP): drives on its own and stops exactly at every station.
-- It is unlocked by a level of Config.Levels (autopilot = true) and then bought once from the shift briefing
Config.Autopilot = {
    Enabled        = true,   -- true = players can unlock and buy the autopilot, false = the AP gear does not exist
    Price          = 25000,  -- one time price in $
    Account        = "bank", -- account the price is taken from: "bank" or "cash"
    Speed          = 60,     -- km/h the autopilot drives between stations (never above Config.Driving.MaxSpeed)
    OnTimeBonus    = false,  -- true = autopilot legs still get the on time bonus, false = no on time bonus as soon as
                             -- the autopilot was used on the way to that station (a late arrival is still penalised).
                             -- The bonus for grabbing the container is never affected, that is always the player's work
}
```

{% hint style="info" %}
**Why no on time bonus with the autopilot?** The autopilot does the driving, so arriving on time is not the player's achievement. The crane work stays his own — the grab bonus is always paid.
{% endhint %}

***

### Stations and Timetable

#### Stopping at a crane

```lua
-- Stopping at a crane station. The train stops next to the crane, the camera turns to the side and the player
-- shunts the train until the container sits under the crane, then drives the crane himself
Config.Stop = {
    Radius           = 25.0,  -- meters between the next container and the crane where the crane view starts (train standing still)
    MaxSpeed         = 3,     -- km/h, the server rejects a train that is faster than this at the crane
    ApproachDistance = 400.0, -- meters before a station where the braking assistant appears
    PerfectDistance  = 0.30,  -- meters between wagon centre and crane for the "perfect" bonus
    GoodDistance     = 0.80,  -- meters between wagon centre and crane for the "good" bonus
    MaxDistance      = 1.50,  -- meters, farther away the crane cannot grab the container (shunt closer)
}
```

A train that passes a station without stopping loses the cargo of that station.

#### Timetable

```lua
-- Timetable
Config.Timetable = {
    ReferenceSpeed = 40, -- average km/h used to calculate the scheduled arrival time (lower = more relaxed timetable)
    GraceSeconds   = 30, -- seconds a train may be late without a penalty
}
```

Every leg gets `rail metres ÷ ReferenceSpeed` as its time. The first leg gets one extra minute for boarding. The countdown only starts when the train has appeared — the way to the train is not timed.

#### Pay and bonuses

```lua
-- Bonuses and penalties, in percent of the station pay
Config.Bonus = {
    PerfectStop  = 15,  -- percent bonus per container that was grabbed with perfect alignment
    GoodStop     = 5,   -- percent bonus per container that was grabbed with good alignment
    OnTime       = 10,  -- percent bonus for arriving before the timetable time
    Late         = -10, -- percent penalty for arriving later than the timetable time + GraceSeconds
}

-- Pay and experience for ONE finished station (every planned container handled).
-- The bonus percentages above are added on top of the pay, the average of all containers of that station counts
Config.Station = {
    pay = 450, -- money for a finished station
    xp  = 60,  -- experience for a finished station
}

-- Bonus XP for completing a whole shift
Config.ShiftCompleteXP = 100
```

* A station that is only partly done pays its share — 1 of 4 containers pays a quarter of the pay and XP
* The shift bonus XP is only paid when at least one container was moved

{% hint style="success" %}
Pay, XP, rewards and the autopilot purchase are all calculated on the server. The server also checks that the player sits in his own train, stands still at the right crane and really moved the container before anything is paid.
{% endhint %}

***

### Levels and Rewards

```lua
-- Levels and their rewards, like a battle pass. Players start at level 0 and climb with experience.
-- [level] = { xp = total experience needed for this level, pay = money paid out on reaching it,
--             item = item name the player also gets ("" = no item), amount = how many of that item,
--             autopilot = true -> from this level on the autopilot can be BOUGHT in the shift briefing }
-- Only the first level with autopilot = true counts, more of them do no harm.
-- The item names must exist on your server (the inventory wrapper is in inventory/inventory.lua).
-- The number of loaded wagons grows with the level automatically: 2 at level 0 up to 8 at the highest level,
-- but never more than the stations of a route can take (4 per unload station)
Config.Levels = {
    [1]  = { xp = 900   , pay = 1500 , item = "",            amount = 1 },
    [2]  = { xp = 2100  , pay = 2000 , item = "",            amount = 1 },
    [3]  = { xp = 3600  , pay = 2500 , item = "water",       amount = 4 },
    [4]  = { xp = 5400  , pay = 3000 , item = "",            amount = 1 },
    [5]  = { xp = 7500  , pay = 3500 , item = "",            amount = 1, autopilot = true },
    -- ...
    [20] = { xp = 75000 , pay = 40000, item = "",            amount = 1 },
}
```

Rewards are collected by hand on the **Career** page of the briefing. If the inventory is full, the reward stays waiting until there is space.

{% hint style="info" %}
With the default values a full shift on a 4 station route gives 340 XP (4 × 60 + 100). Level 1 takes about 3 shifts, level 5 about 22 and level 20 about 220. Lower the `xp` values for faster levelling, raise them for slower.
{% endhint %}

***

### Depot NPC, Sounds and Blips

```lua
-- Depot NPC. The shift briefing opens in front of him (no marker at the depot)
Config.DepotNpc = {
    Model    = "s_m_m_lsmetro_01",      -- ped model (GTA LS Metro worker)
    Scenario = "WORLD_HUMAN_CLIPBOARD", -- idle animation, "" = he just stands
}

-- Crane sounds at a station (files in html/sounds). Volume from 0.0 (off) to 1.0 (full)
Config.Sounds = {
    Move  = 0.1, -- motor sound that loops while the crane moves (move.mp3)
    Place = 0.05, -- sound when a container is set down on the pad (place.mp3)
}

-- Map blips. sprite / color = GTA blip ids, scale = size
Config.Blips = {
    Depot = { sprite = 795, color = 5, scale = 0.85 }, -- the depot
    Stop  = { sprite = 1,   color = 5, scale = 0.85 }, -- next stop during a shift
    Train = { sprite = 795, color = 2, scale = 0.80 }, -- your train while you are outside of it
}
```

Blip sprites and colors: [FiveM blip reference](https://docs.fivem.net/docs/game-references/blips/). Ped models: [FiveM ped models](https://docs.fivem.net/docs/game-references/ped-models/).

***

### Exports and Events

The job needs no exports or events from other scripts. Pay goes through your framework, items through `inventory/inventory.lua`, and notifications through `Config.Functions` — those three places are where you connect your own resources.

***

### Driver Guide

#### Starting a shift

1. Walk to the depot NPC and press `E`
2. The briefing shows today's route: stations, distance, timetable, your train and the pay range
3. Press **START SHIFT**
4. Follow the GPS to your train — it appears when you are close
5. Walk to the locomotive and board it with `F` — the timetable starts now

#### Driving

* Change gears with the arrow keys: **R** reverse, **N** neutral, **M** manual, **A** cruise control, **AP** autopilot
* The HUD shows the next station, the distance and the time left
* 400 m before a station the braking assistant appears — follow it and stop next to the crane

#### At a station

1. Stand still next to the crane — the camera turns to the side
2. Shunt with `A` / `D` until the wagon sits under the crane
3. Press `E` — the crane unloads or loads the container on its own
4. Repeat for every wagon of this station, then drive on

#### Ending a shift

* After the last station drive to the end point (if the route has one) and stop there
* `G` opens the end shift dialog at any time — wages already earned stay yours

***

### Database Tables

| Table                    | Content                                                                          |
| ------------------------ | -------------------------------------------------------------------------------- |
| `trainjob_player_data`   | Level, XP, cooldown, career stats, autopilot and collected rewards per character |
| `trainjob_shift_history` | The last shifts of every character for the driver log                            |

***

### Troubleshooting

<details>

<summary>"You are not allowed to use the route builder"</summary>

**Cause:** None of the four permission checks passed. The server console prints a line with your license, your group and every check result. **Solution:** Put your license into `Config.Setup.Licenses` in `config/routes.lua` and restart the resource.

</details>

<details>

<summary>/trainroute does nothing at all</summary>

**Cause:** `Config.Setup.Enabled = false`. **Solution:** Set it to `true` while you build routes and restart the resource.

</details>

<details>

<summary>"No drivable rails within 25 m"</summary>

**Cause:** You are standing too far away from the tracks, or on decoration rails. Not every rail in GTA is a real track — some are only scenery that no train can drive on. **Solution:** Stand directly next to rails you have seen ambient trains drive on. The message shows how far away the nearest real rails are.

</details>

<details>

<summary>The train faces the wrong way</summary>

**Cause:** The `direction` of the route does not match the rails at the spawn. **Solution:** Swap `direction = true` / `false` for that route and restart the resource.

</details>

<details>

<summary>The timetable is far too short on one leg</summary>

**Cause:** The route has no `track` numbers, so the straight line is used. The server console shows "has no track distances" on start. **Solution:** Open `/trainroute`, press `M`, click **MEASURE CONFIG ROUTES** and replace the route block with the new one.

</details>

<details>

<summary>"All trains are out right now"</summary>

**Cause:** Every route is being driven by another player. **Solution:** Intended — two trains may not spawn inside each other. Build more routes to allow more drivers at the same time.

</details>

<details>

<summary>A route never shows up</summary>

**Cause:** The route has no spawn or no valid crane. The server console names the route and the reason on start. **Solution:** Fix the named field, or build the route again with `/trainroute`.

</details>

<details>

<summary>The shift ends in the middle of a route</summary>

**Cause:** `Config.MaxShiftMinutes` is shorter than the route takes. **Solution:** Raise it. It also counts the way to the train.

</details>

<details>

<summary>COPY ROUTE does not copy</summary>

**Cause:** The clipboard was blocked. **Solution:** The route block gets selected automatically — press `CTRL + C` to copy it.

</details>

<details>

<summary>Level rewards are not given</summary>

**Cause 1:** The item name does not exist on your server. **Solution:** Use an item from your inventory or set `item = ""`.

**Cause 2:** The inventory is full. **Solution:** Intended — the reward waits on the Career page until there is space.

</details>

***

### Best Practices

#### Routes

* ✅ Build at least as many routes as you expect drivers at the same time
* ✅ Use 4 stations in the order unload, load, unload, load
* ✅ Put the end point near a road so the player gets home easily
* ✅ Drive every route once after pasting it
* ❌ Don't type `track` numbers by hand
* ❌ Don't place stations out of driving order
* ❌ Don't put two load stations directly after each other

#### Balancing

* ✅ Lower `ReferenceSpeed` for a relaxed timetable, raise it for a hard one
* ✅ Keep `Config.MaxShiftMinutes` well above your longest route
* ❌ Don't set `CooldownMinutes = 0` on a live server — players farm the job

#### Production

* ✅ Set `Config.Debug = false`
* ✅ Set `Config.Setup.Enabled = false` when your routes are done
* ✅ Remove licenses from `Config.Setup.Licenses` that should not build routes

***

### Quick Start Checklist

**Server setup**

* [ ] Add the resource and start it — tables are created automatically
* [ ] Put your license into `Config.Setup.Licenses`
* [ ] Set `Config.Locale`, `Config.PayAccount` and `Config.UIColor`
* [ ] Set `Config.Depot` to your depot position

**Build the routes**

* [ ] `/trainroute` → `E` at the spawn → `E` at every station → `E` at the end point → `M` → **COPY ROUTE**
* [ ] Paste every route into `Config.Routes` and rename the stations
* [ ] Restart the resource and check the server console for route warnings

**Configure the job**

* [ ] Adjust `Config.Station`, `Config.Bonus` and `Config.Timetable`
* [ ] Change the `Config.Levels` rewards to items that exist on your server
* [ ] Set `Config.MaxShiftMinutes` above your longest route

**Test everything**

* [ ] Open the briefing at the depot NPC — a route is shown
* [ ] Start a shift and follow the GPS — the train appears when you are close
* [ ] Board with `F`, drive to the first station, unload with the crane
* [ ] Drive a load station and fill the empty wagons
* [ ] Deliver the train at the end point — the shift ends and you are back at the depot
* [ ] Set `Config.Setup.Enabled = false`
