The Job Panel#
Bus Driver Job has one window, and the dispatcher at the depot opens it: walk up and press E, or use your third eye when Config.Target is on. The camera cuts to the dispatcher, framed on the left half of the screen, and the panel takes the right half. Your own character is hidden from your view while you talk, so it never stands in the shot.
Part | What it shows |
|---|---|
Dispatcher card | Who you are talking to and their greeting — |
Level | Your level, the experience toward the next one, and a Level Bonus tooltip with your current pay bonus and crash protection — exactly what the server will apply. |
Routes | A carousel of every route: picture, name, description, number of stops, level required, pay and experience. |
Your Bus | The bus picker. Locked buses are drawn dimmed with the level they need, so drivers can see what the next level unlocks. |
Start route / Abandon route | The footer. While you are on a route it offers to abandon it instead, with a confirmation, and keeps the panel open so you can pick another. |
Statistics | The daily, season and all-time leaderboards. Hidden when |
Guide | A driver's guide: how the job works, step by step, and a few tips. |
The panel opens for everyone, so anybody can browse routes and statistics. The job is checked when a route is started — by the panel, and again by the server.
Driving a Route#
A route is one run from the depot, through every stop in order, and back to the depot. Routes are driven solo: a bus has one driver.
Take a route
Pick a route and a bus and press Start route. The server checks your job, your level against the route and the bus, and that you have no route open. The screen fades, a bus appears in the first free bay and you are seated at the wheel, with the keys and the uniform on. A tracker shows the stops served and any crash penalty.
Drive to the stop
The next stop is marked on the map with a GPS route. Stops are served in order, so there is only ever one to head for. Within 10 m, at the wheel of your bus, the prompt Stop Bus appears — press E.
Let the passengers on and off
The bus is held on the handbrake and the camera swings out to the stop. Passengers riding with you get off first, then the people waiting queue alongside the bus and climb in through the front door, one at a time. When the last door closes the camera comes back and the next stop is marked.
Reach the terminus
The last stop empties the bus instead of filling it. Once everybody is off you are told to take the bus back to the depot.
Hand the bus back
Drive to the hand-back point (
Config.DeliverBusCoords) and press E within 5 m. You step out, the bus is taken away, the uniform comes off and the route is paid.
Every step that is worth money is confirmed by the server before it counts:
Step | What the server checks |
|---|---|
Starting | The job (unless |
Serving a stop | It is the next stop in order, you are within |
Handing back | Every stop is served, you are within |
Abandoning a route — from the panel, by losing the job, or by disconnecting — removes the bus and its passengers and pays nothing. A partial run is never paid.
Routes#
Four routes ship ready to drive. They are what the panel's carousel lists, in the order of Config.Tasks:
Route | Level | Stops | Pays | Experience |
|---|---|---|---|---|
Cityline Route — a short beginner loop through the city centre | 1 | 6 | $400 | 300 |
Uptown Express — across the uptown district's busy avenues | 2 | 8 | $600 | 500 |
Grand Avenue Route — high-traffic intersections and commercial zones | 3 | 8 | $600 | 500 |
Interstate Express — a long haul up the highway to Paleto Bay | 4 | 2 | $800 | 700 |
Adding a route#
A route is one entry in Config.Tasks. Copy an existing one, give it a new taskOrder and list its stops in the order they must be served:
Config.Tasks = {
-- ...the shipped routes...
{
taskOrder = 5, -- unique id, never reuse one
name = "Vinewood Loop",
img = "bus_driver_2.png", -- a shipped picture, or a full https:// URL
description = "Tourists and late shoppers between Vinewood and the boulevard.",
recommendedPlayers = { 1 },
levelRequired = 5,
experience = 650,
money = 750,
longRide = false, -- true packs more passengers on at every stop
route = {
[1] = vector3(303.53, -766.33, 29.31),
[2] = vector3(-529.46, -718.97, 33.08),
[3] = vector3(-1011.6, -307.85, 37.87),
},
},
}Field | What it does |
|---|---|
| The route's id. Routes are found by this number, not by their place in the list, so it must be unique. |
| What the card shows. |
| The cover picture: one of the shipped |
| Kept for the card. Not enforced — routes are driven solo. |
| The lowest level that may take the route. Checked by the server. |
| What finishing the route is worth, before the bus multiplier, the level bonus and the crash penalty. |
| Optional. |
| The stops, as |
Buses and Spawn Bays#
The buses a driver can pick from live in Config.Vehicles. Levelling into a bigger bus is the reward: its multiplier applies to the whole route's pay.
Bus | Model | Unlocks at | Pay multiplier |
|---|---|---|---|
Bus |
| Level 1 | ×1.00 |
Dashound Coach |
| Level 6 | ×1.18 |
Airport Bus |
| Level 14 | ×1.35 |
Config.Vehicles = {
{ model = 'bus', label = 'Bus', img = 'bus.png', levelRequired = 1, moneyMultiplier = 1.0 },
{ model = 'coach', label = 'Dashound Coach', img = 'coach.png', levelRequired = 6, moneyMultiplier = 1.18 },
{ model = 'airbus', label = 'Airport Bus', img = 'airbus.png', levelRequired = 14, moneyMultiplier = 1.35 },
-- Any spawn name that exists on your server works, base game or add-on.
-- img takes a shipped picture or a full https:// image URL:
-- { model = 'pbus2', label = 'Festival Bus', img = 'https://example.com/pbus2.png', levelRequired = 20, moneyMultiplier = 1.5 },
}There is no seat count to fill in. How many passengers a bus carries is read off the model when it spawns — every seat except the one by the front door, which is kept clear as the doorway — so swapping a model needs no bookkeeping. The level is checked again by the server when the route starts, so the picker is a convenience, not the security boundary.
Spawn bays and the hand-back point#
The bus appears in the first free bay of Config.VehicleSpawnCoords. A bay counts as blocked when any vehicle stands in the box the bus would fill — 2.5 m either side of the bay and 6.5 m ahead and behind, along the bay's heading. When every bay is blocked the driver is told the parking lot is full instead of having a bus dropped on top of a car.
Config.VehicleSpawnCoords = {
[1] = vector4(839.7751, -1934.2310, 28.9909, 353.2833),
[2] = vector4(835.6471, -1933.7159, 28.9580, 355.7019),
}
Config.DeliverBusCoords = vector3(847.8856, -1952.8138, 28.99) -- where the route is handed in
Config.SpawnInsideVehicle = true -- seat the driver straight away
Config.SpawnFade = { enabled = true, outTime = 400, holdTime = 200, inTime = 700 }
Config.Fuel = false -- true fills the tank on spawn
Config.FuelExport = exports['LegacyFuel']Keep bays side by side and more than 2.5 m apart across their heading — the shipped two are 4.16 m apart. Placed nose to tail they need a good 13 m, the length of a bus. Config.SpawnFade hides the jump into the driver's seat behind a short fade; set enabled = false for a hard cut.
Passengers and Fares#
NPC passengers#
The server keeps the count aboard and decides both halves of every stop from it: some of the riders get off, then the people waiting get on — never more than the room the bus has left. Anyone who cannot fit, or who fails to reach the door after two tries, walks off down the street instead of being dropped into a seat.
Config.PassengersPerStop = {
minBoard = 1, -- fewest people waiting at a stop
maxBoard = 3, -- most people waiting at a stop
maxAlight = 2, -- most that get off at any one stop
longRideBonus = 1, -- added to both board numbers on a longRide route
}Keep these numbers small. On a GTA bus only the front door works, so every passenger boards and alights through it one at a time — about three seconds a head, with the bus held at the stop for all of it. Six people boarding is a twenty-second stop; two or three is the few seconds it should be.
Player passengers#
When another player sits in your bus while you are on a route, they are charged Config.MultiplayerTicketPrice ($20) and you are paid the same amount, in one server-side step — neither side can be charged without the other being paid. Both get a notification, and the fare counts toward your earnings on the leaderboard.
You must be on a route, at the wheel of the bus that route spawned.
The same passenger is charged by the same driver at most once per
Config.TicketCooldown(180 seconds), so hopping between seats never bills twice.The fare is paid from cash. A passenger who cannot cover it is told so and stepped out of the bus.
Pay, Crashes and Levels#
A route is paid once, when the bus is handed back, from the route's money in the config. The crash penalty comes off first, then the bus multiplier and the level's pay bonus are applied to what is left:
pay = (route money - crash penalty) × bus multiplier × (1 + pay bonus %)
-- Cityline Route ($400), level 10, in the Bus, 2 crashes:
-- crash penalty = 2 × $10 × (1 - 10%) = $18
-- pay = ($400 - $18) × 1.00 × 1.05 = $401The pay never drops below zero — a wrecked route pays nothing rather than costing money. Experience is always the route's full experience. With qs-job-management running the payout is handed to it; otherwise it goes into the driver's cash.
Crashes#
A hit that takes more than a scrape off the bus while you are in it is reported to the server, which counts it at most once every three seconds and up to Config.MaxCrashesPerRoute (20) per run. Each one costs Config.CrashMonetaryPayment ($10), reduced by your crash protection. You are told what each crash cost, and the tracker keeps the running total.
Levels#
Levelling never stops. The experience for the next level is BaseExperience × GrowthFactor^(level − 1) — 1,000 then 10% more each level. Every level adds PayBonus (0.5%) to the pay and CrashProtection (1%) off each crash, and both stop growing at Config.MaxBonusLevel (60), so a veteran driver can never drive for free money.
Level | Total XP to reach | Pay bonus | Crash protection | Unlocks |
|---|---|---|---|---|
1 | 0 | +0.5% | 1% | Cityline Route, Bus |
2 | 1,000 | +1% | 2% | Uptown Express |
3 | 2,100 | +1.5% | 3% | Grand Avenue Route |
4 | 3,310 | +2% | 4% | Interstate Express |
6 | 6,105 | +3% | 6% | Dashound Coach |
14 | 24,518 | +7% | 14% | Airport Bus |
20 | 51,151 | +10% | 20% | — |
60 | 2,757,986 | +30% | 60% | The bonus cap |
A level-up is announced the moment it happens. The figures above are for the shipped config and shift with Config.LevelExperience.
Statistics and Leaderboards#
The Statistics button in the job panel opens three rankings. Each lists place, player name, experience and earnings — route pay plus ticket fares — and highlights your own row. A search box finds a driver, and each timed board shows when it ends, what the top three will be paid and who won last time.
Board | Ranks by | Resets | Top three paid |
|---|---|---|---|
All-Time | Total experience | Never | — |
Season | Experience this month | 00:00 on the 1st of every month | $5,000 · $3,500 · $2,000 |
Daily | Experience today | 00:00 every day | $500 · $350 · $200 |
Times are the server's clock. At a reset the top three are paid in cash on the spot; a winner who is offline has the prize banked and paid when they next log in. If the server was down over midnight or a month boundary, the missed reset runs as soon as it starts again — and never twice. The boards refresh every Config.UpdateLeaderboard minutes (1), show up to Config.MaxLeaderboardPlayers drivers (100), and only list drivers with experience in that period.
Config.LeaderBoardRewards = {
Daily = { [1] = 500, [2] = 350, [3] = 200 },
Season = { [1] = 5000, [2] = 3500, [3] = 2000 },
}
-- Which name the boards show for a player:
-- 'character' → the character's first and last name
-- 'cfx' → the FiveM account name
-- 'both' → "Character Name (FiveM Name)"
Config.PlayerNameSource = 'character'The FiveM name only exists while somebody is connected, so with 'cfx' or 'both' an offline driver's row falls back to their character name and is never left blank. Other players' identifiers never leave the server: each player is only told their own place.
The Guide#
The Guide button shows one walkthrough of the job — an intro, six steps and four tips. Every line comes from config/translations.lua (GUIDE_INTRO, GUIDE_STEP_1 to GUIDE_STEP_6, GUIDE_TIP_1 to GUIDE_TIP_4), so you can reword it to match your server's rules. To drop a line, delete its key from your language and from en — a key missing from one language falls back to English.
The Uniform#
With Config.Clothing = true, the driver changes into a uniform when a route starts and back into their own clothes when it ends — completed, abandoned or lost with the job. The outfit is set at the top of client/clothing.lua, one table for male characters and one for female:
local playerClothing = {
male = {
["arms"] = 11,
["tshirt_1"] = 15, ["tshirt_2"] = 0,
["torso_1"] = 13, ["torso_2"] = 2,
["pants_1"] = 24, ["pants_2"] = 0,
["shoes_1"] = 10, ["shoes_2"] = 0,
["helmet_1"] = -1, ["helmet_2"] = 0,
["chain_1"] = 0, ["chain_2"] = 0,
},
female = { ... },
}Uncomment any other slot — decals, ears, mask, bags, glasses, vest, watches, bracelets — to include it. On ESX the outfit goes through esx_skin and skinchanger; on QBCore and Qbox through the qb-clothing events. Set Config.QuasarInventory = true if you run qs-inventory, so the inventory is locked while the clothes change. With Config.Clothing = false nobody changes clothes, and on-duty tracking keeps working.
The Depot: Blip, Dispatcher and Marker#
Piece | Set with | Notes |
|---|---|---|
Map blip |
| "Bus Station", sprite 513. Shown only to players who may drive, and redrawn when their job changes. |
Dispatcher |
| Position and heading ( |
Floating marker |
| The blue cone over the dispatcher's head: shape, height, colour, size, gentle bob and how far away it shows. |
Prompts |
| 3D "[E]" text at the dispatcher, the stops and the hand-back point, on top of the hint in the corner. The key is control 38, E. |
Route blip |
| The blip and GPS line on the next stop while a route runs. |
Moving the depot means moving three things together: the dispatcher (Config.StartJob.ped.coords), the spawn bays (Config.VehicleSpawnCoords) and the hand-back point (Config.DeliverBusCoords). The blip and the marker follow the dispatcher on their own. The dialogue camera is fixed by design and only frames the dispatcher when the player is within 6 m of them.
The Config Reference#
Every option in config/config.lua, by section, with its shipped value. Restart the resource after changing any of them.
1. General — job, interaction, interface and money
Option | Default | What it does |
|---|---|---|
| auto | Detected from the running core. Leave the line as it is. |
| auto | Detected from the running key script, |
|
| Whether starting a route needs a job. |
|
| The jobs allowed to drive. On ESX each is registered in the database on start. |
|
|
|
|
|
|
|
| Draws the "[E] …" prompts in the world. |
|
| The key for the dispatcher, the stops and the hand-back. Any FiveM control id. |
|
| The name on the leaderboards: |
|
| The dispatcher card in the panel. Empty hides that part. |
|
|
|
|
|
|
|
| Locks qs-inventory during the outfit change. |
|
| The driver uniform while on a route. |
|
| Creates the three bus driver quests in qs-inventory and reports progress. |
2. World — blip, dispatcher, buses and bays
Option | Default | What it does |
|---|---|---|
|
| The depot blip on the map. |
| Bus Station, 513, 1.0, 12, 2 |
|
| the depot, | The dispatcher's |
| bus · coach · airbus | The bus picker: |
| two bays | The bays the bus can appear in ( |
| the depot | Where the bus is handed back for the route to pay ( |
|
| Seats the driver in the bus instead of beside it. |
| on, 400 / 200 / 700 ms | The fade that hides the spawn: |
|
| Fills the tank to 100 on spawn through the export's |
| sprite 1, size 1.0, color 3 | The blip on the next stop. |
3. Routes — tasks and reach distances
Option | Default | What it does |
|---|---|---|
| four routes | The routes in the panel. See Adding a route above. |
|
| How close to a stop the server accepts the driver, in metres. Keep it wider than the 10 m prompt so lag never costs a stop. |
|
| The same for the hand-back point. |
4. Levels — the curve and the perks
Option | Default | What it does |
|---|---|---|
|
| Experience needed to leave level 1. |
|
| How much more each level needs than the last. |
|
| Percent added to the route pay per level. |
|
| Percent taken off each crash penalty per level, never above 100. |
|
| The level the two perks stop growing at. Levelling itself is not capped. |
5. Passengers — stops, fares and crashes
Option | Default | What it does |
|---|---|---|
| 1 – 3 board, 2 alight, +1 |
|
|
| What a player passenger is charged, and the driver paid. |
|
| Seconds before the same driver can charge the same passenger again. |
|
| Money taken off the payout per crash, before crash protection. |
|
| The most crashes one run can be penalised for. |
6. Leaderboard
Option | Default | What it does |
|---|---|---|
|
|
|
|
| Minutes between refreshes. |
|
| Drivers per board. Higher costs query time on every refresh. |
| daily 500/350/200, season 5000/3500/2000 | What the top three are paid at each reset. |
7. Marker — the cone over the dispatcher
Config.PedMarker = {
enabled = true, -- false hides it completely
type = 0, -- 0 is the upside-down cone; any FiveM marker id
height = 1.2, -- metres above the dispatcher's head
distance = 25.0, -- how close you have to be to see it
bob = true, -- drifts gently up and down
size = { x = 0.25, y = 0.25, z = 0.18 },
color = { r = 59, g = 130, b = 246, a = 170 }, -- 0–255 each
}8. Debug, webhooks and language
Option | Default | What it does |
|---|---|---|
|
| Traces what the script does in the server console and F8. Leave it off on a live server. |
| off | Discord logs. See Integrations. |
|
| Set in |



