Configuration
Everything lives in config.lua. Career ranks live in data/career.lua, texts in locales/. New settings are added to this file as each phase ships.
General
| Setting | Default | Meaning |
|---|---|---|
Config.Framework | 'auto' | 'auto', 'qbox', 'qbcore', 'esx' |
Config.Locale | 'en' | Any file name in locales/. Missing keys fall back to English. |
Config.Debug.Enabled | false | Extra console output (state changes, bridge failures) |
Config.Debug.Commands | false | Enables /busdebug, /buscareer, /busxp, /busreputation (also needs the ns-bus.dev ACE) |
Database
| Setting | Default | Meaning |
|---|---|---|
Config.Database.AutoMigrate | true | Create and update tables on start. Never deletes data. |
Admin
| Setting | Default | Meaning |
|---|---|---|
Config.Admin.Ace | 'ns-bus.admin' | ACE that always grants admin tools |
Config.Admin.DevAce | 'ns-bus.dev' | ACE for developer commands |
Config.Admin.UseFrameworkAdmin | true | Also accept the framework's admin groups below |
Config.Admin.QBPermissions | { 'admin', 'god' } | Qbox / QBCore permission names |
Config.Admin.EsxGroups | { 'admin', 'superadmin' } | ESX groups |
Security
| Setting | Default | Meaning |
|---|---|---|
Config.Security.RateLimits | see file | Minimum ms between two calls of the same action per player. default applies to actions without their own value. |
Config.Security.FlagThrottleMs | 30000 | How often the same suspicious-behaviour warning may be printed per player |
Config.Security.MaxTextLength | 64 | Max length of free text sent by clients |
Config.Security.MaxAverageSpeed | 40.0 | m/s, straight line from where the bus left the last stop. A stop reached faster is refused and flagged (a teleport earns nothing faster than driving). false: off. Admin test drives are not checked |
Career
| Setting | Default | Meaning |
|---|---|---|
Config.Career.MaxLevel | 50 | Highest level |
Config.Career.NotifyLevelUp | true | Notification and sound on level up |
Config.Career.Command | 'busprofile' | Player command for the career menu (also at the depot desk and in the route menu); nil turns it off |
Config.XP.Base / Growth / Exponent | 400 / 45 / 1.45 | XP from level L to L+1 = floor(Base + Growth * (L-1)^Exponent) |
Config.XP.MaxSingleAward | 5000 | Safety cap for a single XP award |
Config.Reputation.Start | 50 | Reputation of a new driver |
Config.Reputation.Min / Max | 0 / 100 | Reputation range |
Config.Reputation.MaxSingleChange | 25 | Safety cap for a single change |
With the default curve: level 5 at 1,988 XP, level 10 at 7,064, level 20 at 30,949, level 30 at 78,938, level 50 at 267,368.
Ranks (data/career.lua)
{ level = 10, key = 'rank_city' }, -- name from locales
{ level = 10, key = 'rank_city', label = 'Metro' }, -- fixed name, ignores locales
Levels must be ascending and start at 1.
Job and shift
| Setting | Default | Meaning |
|---|---|---|
Config.Job.Required | false | Only players with one of Config.Job.Names can start a shift |
Config.Job.Names | { 'bus' } | Accepted jobs |
Config.Job.RequireDuty | false | Also require the framework's on-duty flag |
Config.Shift.TargetDistance | 2.5 | Target reach at the desk |
Config.Shift.DeskDistance | 8.0 | Server-checked max distance to the desk |
Config.Shift.RequireVehicleReturn | true | The bus must be returned before ending the shift |
Depots
Config.Depots = {
{
id = 'downtown',
label = 'depot_downtown', -- locale key or plain text
blip = { enabled = true, sprite = 513, color = 5, scale = 0.8 },
ped = { model = 's_m_m_gentransport', coords = vector4(...) },
vehicleSpawns = { vector4(...), vector4(...) }, -- first free bay is used
returnPoint = { coords = vector3(...), radius = 12.0 },
servicePoint = { coords = vector3(...), radius = 10.0 }, -- optional, Phase 13
},
}
The default coordinates are examples. Stand where you want the desk or a bay and run /buscoords (needs dev commands): the vector4 is copied to your clipboard. Invalid depots are skipped with a console message.
Vehicles
| Setting | Default | Meaning |
|---|---|---|
Config.Vehicles.PlatePrefix | 'NSB' | Up to 3 characters, 5 random digits follow |
Config.Vehicles.WarpIntoVehicle | true | Put the driver in the seat after spawning |
Config.Vehicles.StartFuel | 100.0 | Fuel on spawn when Config.Condition is off (otherwise the bus keeps its own fuel) |
Config.Vehicles.SpawnClearRadius | 4.0 | A bay counts as free when no vehicle is closer than this |
Config.Vehicles.SpawnTimeoutMs | 5000 | Give up spawning after this long |
Config.Vehicles.WatchIntervalMs | 5000 | How often active buses are checked (only while any exist) |
Config.Vehicles.DestroyedEngineHealth | -3500.0 | Engine health at which the bus counts as destroyed |
Config.Vehicles.AbandonDistance | 300.0 | Driver farther than this from the bus... |
Config.Vehicles.AbandonSeconds | 300 | ...for this long: the bus is taken back (a warning comes first) |
Config.Vehicles.Models | 5 models | id, model, label, tier, capacity, requiredLevel, class, optional seats and doors |
Config.VehicleKeys | 'auto' | 'qbx_vehiclekeys', 'qb-vehiclekeys', 'custom', 'none' |
With VehicleKeys = 'custom', ns-bus fires the server events ns-bus:giveKeys and ns-bus:removeKeys with (src, netId, plate).
Bus condition, fuel, cleaning and upgrades
Every driver has their own bus of each model. Fuel, engine, body, cleanliness and interior (0 to 100) and the distance it has driven carry over to the next shift (table ns_bus_fleet). The server keeps the numbers; the game only shows them.
| Setting | Default | Meaning |
|---|---|---|
Config.Condition.Enabled | true | false: buses spawn as before, no wear, no service bay, no upgrades |
Config.Condition.Start | all 100 | Condition of a driver's new bus |
Config.Condition.FuelMode | 'auto' | 'auto': a running fuel resource (ox_fuel, LegacyFuel, cdn-fuel, ps-fuel) owns the fuel and the bus reports its level; otherwise ns-bus uses fuel by distance. 'internal' / 'external' to force one |
Config.Condition.FuelPerKm | 2.5 | Percent per km in internal mode; a model can set its own fuelPerKm |
Config.Condition.FuelWarnAt | { 25, 10 } | Notices when fuel drops below these |
Config.Condition.MinFuelToStart / MinEngineToStart | 15 / 25 | A route cannot start below these |
Config.Condition.DirtPerKm / LitterPerPassenger | 1.2 / 0.6 | Cleanliness lost per km and per passenger |
Config.Condition.WearPerPassenger | 0.15 | Interior wear per passenger |
Config.Condition.AcceptWorldRepairs | false | true: repairs by other scripts during a shift count. Mechanic scripts can call exports['ns-bus']:RepairBus(src) instead |
Config.Condition.WreckedRestore | 35 | A destroyed bus comes back from the yard with this engine and body |
Config.Condition.Mood | see file | Mood points passengers lose when they sit down in a dirty (below 40), worn (below 40) or damaged (body below 50) bus |
Config.Condition.ComplaintChance | 0.35 | Chance of a complaint notice in such a bus, once per route |
Config.Condition.SafeDriving | see file | A body health drop of CollisionDrop points between two readings during a route is a collision or an impact the driver's game felt (a velocity change of ImpactKmh within a tenth of a second; hard braking stays far below it). At most one per CollisionGapMs, so scraping along a wall counts once: ScorePenalty route score points, Reputation per collision (at most MaxReputationPerRun), and a perfect route needs none. A route without a collision of at least MinKm km gives XpBonus XP |
Config.Condition.Service | see file | Service bay: Base + missing points * PerPoint seconds (at most Max); CostPerPoint money per missing point (default only the repair costs, $2 per point of engine + body + interior), paid from Account |
Config.Condition.MaxSampleSpeed | 60.0 | m/s; a longer jump between two checks is a teleport and uses no fuel |
Config.Condition.PushIntervalMs | 5000 | Condition updates to the driver at most this often |
Service bay. Drive the bus into Config.Depots[].servicePoint (or the return point when a depot has none) and press G (or use the buttons in the dashboard's Vehicle tab): refuel, clean or repair. The server checks that the bus stays in the bay until the time is up; a route cannot start during a service.
Upgrades (data/upgrades.lua) are fitted at the depot desk and belong to that driver's bus of that model. Each level has a price, a career level and optionally a distance the bus must have driven. Active in this version: comfort (passenger mood), economy engine tune (fuel use), easy-clean interior (dirt), heavy-duty seats (interior wear). Brakes, engine, suspension, doors and safety are defined but not offered yet; new effects can be read with Condition.Effect(rec, name).
Stops
Stops are created in game with /busadmin and stored in the database. config.lua only holds the rules:
| Setting | Default | Meaning |
|---|---|---|
Config.Stops.SeedExamples | true | Insert the example stops from data/stops.lua once, on the very first start |
Config.Stops.ShowBlips | false | Show every enabled stop on the map for all players |
Config.Stops.Blip | sprite 513, color 3, scale 0.6 | Default blip for new stops |
Config.Stops.DefaultRadius | 15.0 | Zone in which the bus counts as at the stop |
Config.Stops.MinRadius / MaxRadius | 5.0 / 40.0 | Allowed radius range |
Config.Stops.MaxLayoutDistance | 40.0 | Every captured point must be this close to the bus position |
Config.Stops.MaxNpcSpawns | 8 | NPC spawn points per stop |
Config.Stops.AdminDrawDistance | 150.0 | Admin markers are drawn for stops closer than this |
Creating a stop
- Park a bus where it should stop (or stand there) and run
/busadmin. - Create stop, set a code (
legion_01) and a display name. - Select Bus stopping position: your vehicle's position and heading are stored.
- Walk to the sidewalk and capture the waiting area, NPC spawn points, boarding and exit points and the ticket inspection area.
- Save stop.
Routes
Routes are built in game with /busadmin and stored in the database.
| Setting | Default | Meaning |
|---|---|---|
Config.Routes.SeedExamples | true | Insert the example route from data/routes.lua once (only if all its stops exist) |
Config.Routes.MinStops / MaxStops | 2 / 30 | Stops per route (in-service stops count toward the minimum) |
Config.Routes.DefaultColor | 5 | Map color of new routes |
Config.Routes.MinPayoutMultiplier / MaxPayoutMultiplier | 0.5 / 3.0 | Allowed payout multiplier range |
Config.Routes.MaxDwellSeconds | 120 | Longest waiting time override per stop |
Config.Routes.ReturnCodeSuffix | '_r' | Code suffix of generated return routes |
Config.Routes.CopyCodeSuffix | '_copy' | Code suffix of duplicated routes |
Building a route
/busadmin→ Create route, set a code (ls-101) and a name.- Stops → Add stop in driving order. Open a stop to move it, skip it on express routes, set a waiting time or remove it.
- Set type, difficulty, direction, required level, minimum bus tier and payout multiplier.
- The route is drawn on your map while you edit. Save route.
- For the way back, open the saved route and pick Create return route.
Driving routes
| Setting | Default | Meaning |
|---|---|---|
Config.Runs.MenuKey | 'F6' | Default key of the route menu (players can rebind it in GTA settings) |
Config.Runs.MaxStopSpeed | 2.0 | m/s; the bus must be (almost) stopped to arrive |
Config.Runs.MaxHeadingDiff | 100.0 | Degrees from the stop's captured heading; false disables the check |
Config.Runs.DefaultDwellSeconds | 6 | Doors-open time when the route sets none |
Config.Runs.ZoneMargin | 5.0 | Extra meters the server accepts (network delay) |
Config.Runs.PassDistance | 80.0 | A "drove past" report is accepted only this close to the stop |
Config.Runs.MaxSkipAhead | 3 | Arriving up to this many stops ahead marks the ones between as missed |
Config.Runs.ExpectedSpeed / RoadFactor | 11.0 / 1.4 | Schedule: straight distance x RoadFactor / ExpectedSpeed |
Config.Runs.LateSeconds / VeryLateSeconds | 60 / 180 | Grace before "late", and "very late" |
Config.Runs.NearDistance, FarWait, NearWait, ZoneWait | 200 m, 1000/250/100 ms | Client check intervals while a route is active |
Config.Runs.Pay | see file | Account, Base, PerStop, PerPassenger, MissedPenalty, OnTimeBonus, Difficulty multipliers |
Config.Runs.XP | see file | Base, PerStop, PerPassenger, PerKm (long routes), PerRequest (requested stops served), OnTimeBonus, CleanBonus (no missed stop), PerfectBonus (perfect route) |
Config.Runs.CleanRunReputation | 1 | Reputation for a route with no missed stops and at least 80% on time |
Config.Runs.MissedReputation | PerStop = 1, MaxPerRun = 3 | Reputation lost per missed stop, capped per route |
Pay = (Base + PerStop x served + PerPassenger x delivered - MissedPenalty x missed) x route multiplier x difficulty x (1 + OnTimeBonus x on-time ratio), never below 0. delivered counts passengers taken to their stop.
XP = (Base + PerStop x served + PerPassenger x delivered + PerKm x km + PerRequest x requests served) x difficulty x (1 + OnTimeBonus x on-time ratio) + ticket XP + CleanBonus + PerfectBonus + conversation XP. XP never goes down; mistakes cost pay and reputation.
NPC passengers
Passenger models and idle animations live in data/passengers.lua.
| Setting | Default | Meaning |
|---|---|---|
Config.Passengers.Enabled | true | Passengers on routes |
Config.Passengers.InTestRuns | true | Admin test drives get passengers too |
Config.Passengers.TestMaxSeats | 16 | Seat limit for test drives in your own vehicle |
Config.Passengers.Networked | true | Everyone sees the passengers. Set false with sv_entityLockdown strict (then only the driver sees them) |
Config.Passengers.PerStop | { min = 0, max = 4 } | Waiting passengers per stop (the final stop gets none; 0 makes empty stops possible) |
Config.Passengers.MaxWaitingPerStop | 8 | Upper limit after the route type multiplier |
Config.Passengers.RouteTypeMultiplier | see file | More at airports and events, fewer at night |
Config.Passengers.AlightSecondsEach / BoardSecondsEach | 4 / 6 | Time allowed per passenger getting off / on |
Config.Passengers.MaxServiceSeconds | 60 | Hard limit on top of the stop's waiting time |
Config.Passengers.ServiceTickMs | 2000 | Longest gap between server checks while the bus is at a stop |
Config.Passengers.SpawnDistance / DespawnDistance | 150.0 / 250.0 | Waiting passengers appear / disappear by bus distance |
Config.Passengers.ReactDistance | 45.0 | They turn towards the bus when it is this close |
Config.Passengers.BoardTimeoutMs / AlightTimeoutMs | 15000 / 8000 | A stuck passenger is placed in the seat / outside |
Config.Passengers.RemoveAfterMs | 20000 | Passengers who left are removed after this (or once out of sight) |
At a stop, people get off first, then the queue boards into free seats. The doors close when everyone is done and the stop's waiting time has passed. Anyone who does not fit stays at the stop and you get a "no available seats" notice. A stuck NPC never costs you anything: the server settles it when its time runs out.
Capacity is the smaller of the model's capacity and the real number of passenger seats of the bus. To use only some seats, give the model a seats list; to choose which doors open at stops, give it a doors list.
Better stops: capture the waiting area, NPC spawn points, the boarding point (where the queue forms, next to the front door) and the exit point in /busadmin. Without them, passengers stand on the nearest pavement and queue beside the bus.
Stop requests
| Setting | Default | Meaning |
|---|---|---|
Config.StopRequest.Enabled | true | Riders press the stop button before their stop |
Config.StopRequest.SkipEmptyStops | true | A stop where nobody waits and nobody wants off may be driven past without penalty (and without pay for it). The final stop is always served |
Config.StopRequest.RequestDistance | 120 - 320 m | How far before their stop riders press the button |
Config.StopRequest.RandomRequestChance | 0.05 | A rider changes their mind and gets off at the next stop |
Config.StopRequest.CheckMs | 1000 | Server check while riders are about to press (no loop otherwise) |
Config.StopRequest.Sound | 'chime' | 'chime' (the bundled bell nui/sounds/stop.ogg), 'file' (nui/sounds/<SoundFile>), 'frontend' (GTA sound), 'none' |
Config.StopRequest.SoundFile | 'stop.ogg' | Your own sound for 'file': put it in nui/sounds/ |
Config.StopRequest.Volume | 0.6 | 0.0 - 1.0 ('chime' and 'file') |
Config.StopRequest.Frontend | TIMER_STOP | GTA sound name and set for 'frontend' |
Config.StopRequest.RepeatCooldownMs | 4000 | More requests for the same stop chime again at most this often |
Config.StopRequest.Reputation | Missed = 1, MaxPerRun = 2 | Reputation per requested stop you drove past, capped per route (completed routes only) |
The HUD shows what waits at the coming stop: STOP REQUEST with the number of riders (bell icon), the number of people waiting, or "Nobody waiting, no request" when you may drive past. F6 lists the passengers on board, where each gets off and who pressed the button. A rider whose stop you drive past sits down unhappy and gets off at the next stop that is served.
Passengers: personality, conversations, events
Personalities and their effects are in data/passengers.lua, conversations in data/dialogues.lua (texts are locale keys), events in server/events/ (one file each).
| Setting | Default | Meaning |
|---|---|---|
Config.Satisfaction.Late | late = 8, very_late = 20 | Mood lost by everyone on board when you arrive late (x personality lateness) |
Config.Satisfaction.MissedRequest | 15 | Mood lost when you drive past their stop |
Config.Satisfaction.High / Low | 80 / 40 | Route average at or above / at or below gives +Reputation.High / -Reputation.Low |
Config.Social.Enabled | true | Passengers talk to you |
Config.Social.ChancePerLeg | 0.3 | A rider starts talking between two stops (x personality talk) |
Config.Social.DelaySeconds | 6 - 20 | After leaving the stop |
Config.Social.AnswerSeconds | 20 | Unanswered: they give up and lose IgnoredMood |
Config.Social.LateComplaintChance | 0.6 | Someone complains when you arrive late |
Config.Social.ByeChance | 0.5 | Passengers say goodbye (or grumble) when they get off |
Config.Social.AnswerKeys | '1', '2', '3' | Answers while a passenger talks to you: positive, neutral or refusing, no answer (counts like letting the time run out). While the card is up these keys do not select weapons or use the ox_inventory hotbar |
Config.Social.MaxReputationPerRun | 2 | Reputation from conversations and events per route (completed routes only) |
Config.Social.Xp | PerPositive = 10, MaxPerRun = 40 | XP for conversations that went well, capped per route |
Config.Events.Enabled | true | Random passenger events |
Config.Events.ChancePerLeg / ChancePerStop | 0.12 / 0.06 | How often an event happens |
Config.Events.AnswerSeconds / EmergencySeconds | 25 / 30 | Time to answer an event / a sick passenger |
Config.Events.BlockDoorSeconds | 8 | Extra stop time while someone blocks the door |
Config.Events.LostItemFoundChance | 0.6 | Searching finds the lost item |
Config.Events.Weights | see file | How often each event is picked; 0 turns one off |
When a passenger talks to you, their line shows in the HUD and as a subtitle; press F6 to answer. A sick passenger is an emergency: the route waits until you decide (stop the bus, call EMS or drive on to the next stop). Stopping only works when the bus is standing still. On foot near a passenger you can Talk to them or Ask destination with the target eye.
The route report shows the average satisfaction of the passengers you took to their stop.
Police inspections and fines
| Setting | Default | Meaning |
|---|---|---|
Config.Inspection.Enabled | true | Police inspections |
Config.Inspection.InTestRuns | false | Routine inspections in admin test drives (never fines or records) |
Config.Inspection.Networked | true | The officer and the police car are networked so riders and other players see the inspection. false with sv_entityLockdown strict: only the driver sees them |
Config.Inspection.Chance | see file | Chance that a route gets a routine inspection, by route type (city 35%, airport 45%, night 60%, event 50%). It happens when the doors close at one of the stops, spread evenly, so a long line is not inspected more often than a short one |
Config.Inspection.MaxPerRun / CooldownMinutes | 1 / 10 | At most one routine inspection per route, then a break per driver |
Config.Inspection.NpcSeconds | 15 | How long a routine inspection takes without Dialogue |
Config.Inspection.Dialogue | see file | The officer talks to the driver like a passenger (answers 1 / 2 / 3): walks to the bus (ArriveSeconds per kind), greets you, checks the bus for CheckSeconds, tells you the result. Polite both times: driver fine x Cooperative (0.75); silent or short both times: x Uncooperative (1.25). Enabled = false: the timed check without talking |
Config.Inspection.Pullover | see file | Routine inspections on the road: Share of them (0.4) are a police car with its siren on behind the bus, DelaySeconds after leaving a stop. Standing still (StopSpeed m/s) for StopSeconds lets the officer out; not stopping within TimeoutSeconds is fleeing: Fled.Fine, Fled.Reputation and a failed inspection. Vehicle / Officer models. The other routine inspections: the officer waits at the coming stop (the stop's inspection point, else its waiting area) |
Config.Inspection.CheckShare | 1.0 | Share of the riders whose tickets are checked |
Config.Inspection.FareEvasion.PassengerFine | 150 | What fare dodgers pay (shown in the report) |
Config.Inspection.FareEvasion.DriverFine | 300 | Per fare dodger found, before the factors |
Config.Inspection.FareEvasion.DriverPenaltyEnabled | true | Fine the driver at all |
Config.Inspection.FareEvasion.Factors | forced = 0, allowed_without_check = 0.5, accepted = 1 | How much the driver is to blame, by how the dodger got on |
Config.Inspection.FareEvasion.CheckedDiscount | 0.5 | You checked tickets on this route: the fine is halved |
Config.Inspection.FareEvasion.RepeatStep / RepeatMax | 0.25 / 4 | Each earlier failed inspection raises the fine by 25%, counting at most 4 |
Config.Inspection.FareEvasion.MaxDriverFine | 1500 | Highest driver fine |
Config.Inspection.FareEvasion.Account | 'bank' | Taken from here, or from the other account if short |
Config.Inspection.Reputation | Passed = 1, Failed = 2 | Reputation after an inspection |
Config.Inspection.XP | Passed = 75 | XP for the driver when an inspection is passed |
Config.Inspection.Police.Enabled | true | Police players can inspect buses |
Config.Inspection.Police.Jobs / RequireDuty | nil / true | Police jobs (nil uses Config.Dispatch.PoliceJobs), on duty only |
Config.Inspection.Police.Distance / TimeoutSeconds | 10.0 / 180 | The officer must stay close; an unfinished inspection is dropped |
Config.Inspection.Police.CooldownMinutes | 10 | Per driver, between two player inspections |
Config.Inspection.Police.Access | all true | What officers see: driver info, route, history, ticket list |
During an inspection the route waits and goes on where it was afterwards. Fare dodgers found are taken off the bus. The driver is fined only for dodgers they let on: nothing for one who slipped past unnoticed, half when you ignored "Ticket required", in full when you checked and let them in anyway. The amount is always calculated by the server; a police player only decides to issue or waive it.
Assignments and achievements
| Setting | Default | Meaning |
|---|---|---|
Config.Progress.Account | 'bank' | Where money rewards of assignments and achievements go |
Config.Progress.Perfect | MinStops = 4, MinSatisfaction = 75 | A perfect route: no missed stop, every stop on time, no fare dodger got through, no requested stop driven past, at least MinStops stops and (with passengers) this average satisfaction |
Config.Assignments.Enabled | true | Daily and weekly assignments |
Config.Assignments.Daily / Weekly | 3 / 2 | Assignments per driver per day and per week |
Config.Assignments.ResetHour | 6 | Server local time; a new day starts at this hour, a new week on Monday at this hour |
Config.Assignments.AutoClaim | true | Rewards are paid as soon as an assignment is done; false: collected in the career menu |
Config.Assignments.KeepDays | 21 | Old periods are removed from the database after this |
Config.Achievements.Enabled | true | Achievements |
Drivers open the career menu at the depot desk, in the route menu (F6 while on shift) or with /busprofile. It shows level, rank and XP, reputation, the daily and weekly assignments with their progress and time left, achievements and statistics.
Assignments (data/assignments.lua)
Each period a driver gets a pick from this pool, weighted, by level, and only assignments for a route type that exists on the server. The pick is saved and does not change during the period.
{ id = 'd_city_routes', period = 'daily', key = 'asg_city_routes', event = 'route_completed',
filter = { routeType = 'CITY' }, target = 3, reward = { money = 450, xp = 200 } },
{ id = 'w_distance', period = 'weekly', key = 'asg_distance', event = 'route_completed',
amount = 'distance', unit = 'km', target = 500000, reward = { money = 3000, xp = 1200 } },
event:route_completed(completed, paid routes only) orinspectionamount:1(default), a number, or a field of the event:passengers,ticketsChecked,caught,requestsServed,served,distance(meters) ...filter: every field must match: a value (clean = true), a list (routeType = { 'CITY', 'NIGHT' }) or a range (onTimePct = { min = 90 })- route fields:
routeType,difficulty,served,missed,total,onTimePct,passengers,ticketsChecked,caught,evaded,requestsServed,requestsMissed,satisfaction,distance,clean,perfect,inspection; inspection fields:passed,kind,violations minLevel,maxLevel,weightare optionalreward:money,xp,reputation,unlock(an id other resources can check withexports['ns-bus']:HasUnlock)
Never reuse an id for a different task. key is a locale key that gets the target as %d, or use label = 'My text %d'.
Achievements (data/achievements.lua)
{ id = 'routes_100', key = 'ach_routes_100', icon = 'route', stat = 'completed_routes', target = 100,
reward = { xp = 1500, money = 5000 } },
{ id = 'perfect_route', key = 'ach_perfect_route', counter = 'perfect_routes', target = 1 },
{ id = 'master_driver', key = 'ach_master_driver', level = 40, hidden = true },
{ id = 'zero_fare_evasion', key = 'ach_zero_fare_evasion', event = 'route_completed',
filter = { evaded = 0, ticketsChecked = { min = 10 } } },
One condition each: a career statistic (stat), a counter (perfect_routes, clean_routes, stops_served, requests_served, inspections_passed), a level, or a single matching event. The description is the locale key key .. '_desc'. Achievements are checked when a driver loads, so new ones also unlock for drivers who already reached them. Never rename a released id.
Interface (NUI) and statistics
| Setting | Default | Meaning |
|---|---|---|
Config.UI.Dashboard | 'nui' | Driver dashboard at the depot desk, with the route key between routes and with /busprofile; 'menu': the ox_lib career menu and route list |
Config.UI.Hud | 'nui' | Route panel while driving; 'text': the ox_lib text UI |
Config.UI.Reports | 'nui' | Route report, inspection report and ticket card as cards; 'notify': ox_lib notifications |
Config.UI.Admin | 'nui' | /busadmin opens the admin dashboard; 'menu': the ox_lib menus |
Config.UI.HudPosition | 'bottom-right' | 'bottom-right', 'bottom-left', 'top-right', 'top-left', 'right-center' |
Config.UI.Scale | 1.0 | Size of every panel, 0.75 to 1.25 |
Config.UI.ReportSeconds / TicketCardSeconds | 12 / 6 | How long reports and ticket cards stay on screen |
Config.Statistics.RecentRuns / TopRoutes / RecentInspections | 10 / 5 / 5 | List lengths in the dashboard |
The texts of every panel come from locales/; change them there. "Today" starts at midnight server time. The admin dashboard needs the same permission as before (ns-bus.admin or framework admin); the server checks it on every action.
Stops in the admin dashboard: stand where a point belongs (or park the bus on the stopping position) and press Capture here. Hide panel closes it but keeps your draft: walk to the next point and type /busadmin again. Routes: drag stops in the list to reorder them, click a grey stop on the map to add it, click a numbered stop to change its settings.
Tickets and fare dodgers
| Setting | Default | Meaning |
|---|---|---|
Config.Tickets.Enabled | true | false: passengers just get on (no door, no tickets) |
Config.Tickets.Distribution | see file | Weights of what passengers carry: standard, discount and special pass are valid; none, expired and BORROWED_TICKET (someone else's card: the validator accepts it, only the names on the ticket and the ID card differ) are not |
Config.Tickets.SlipChance | 0.5 | Share of fare dodgers who walk past the validator without a word |
Config.Tickets.RetryChance | 0.35 | A refused fare dodger tries once more |
Config.Tickets.DriverDecides | true | Every passenger waits at the door until you let them in or refuse them (checking first is up to you); the stop waits for it too |
Config.Tickets.DriverDecideSeconds | 30 | With DriverDecides: after this they give up waiting and walk in (counted as let in without a check) |
Config.Tickets.TapSeconds | 2 | Without DriverDecides: a passenger validates their ticket and gets on after this, unless you check |
Config.Tickets.DecisionSeconds | 6 | Without DriverDecides: after "Ticket required", time to decide before they walk in anyway |
Config.Tickets.CheckDecisionSeconds | 10 | After a check: time to let them in or refuse |
Config.Tickets.Keys | Accept = 'Y', Refuse = 'N' | Default keys while a passenger is at the door (players can rebind them in GTA settings; they do nothing otherwise) |
Config.Tickets.XP | Check = 0, Caught = 15 | XP per checked ticket (every passenger shows it now) and per stopped fare dodger |
Config.Tickets.Reputation | see file | Caught, Ignored, Knowingly, WrongRefusal, capped at MaxPerRun per route |
While passengers get on, one of them stands at the door at a time. A validator beep means they have a ticket. "Ticket required" means they do not, and they say so. Silence means someone is walking past without paying, and only a spot check catches them. Doing nothing lets the passenger in. Checking shows a ticket card (name, ticket, destination, valid or not).
XP, reputation and statistics for tickets are settled only when the route is completed, never for a cancelled route. Letting someone in without checking after a warning, or letting them in after seeing an invalid ticket, costs reputation; so does refusing a valid passenger. A dodger who slipped past unnoticed costs nothing yet, but police inspections (Phase 10) may find them.
Ticket machine (real players)
Players can ride a bus that is on a route like any passenger. On board they open the ticket machine with K and buy a ticket; the driver opens the same machine, sees the players on board and offers them a ticket. Prices, validity, money and the commission are decided on the server; a ticket is printed in the character's name and stored in ns_bus_tickets, so it survives a relog.
| Setting | Default | Meaning |
|---|---|---|
Config.TicketMachine.Enabled | true | false: no machine, and police checks leave player riders alone |
Config.TicketMachine.Key | 'K' | Default key for the machine (rebindable in GTA settings; it does nothing off a bus on a route) |
Config.TicketMachine.Account | 'cash' | Tickets and fines are paid from this account first, the other one if it is short |
Config.TicketMachine.DriverCommission | 0.15 | Share of every sale on the bus paid to its driver at once (rounded down) |
Config.TicketMachine.CommissionAccount | 'bank' | Where the commission goes |
Config.TicketMachine.OfferSeconds | 30 | How long a ticket the driver offers waits for an answer |
Config.TicketMachine.Keys | Accept = 'Y', Decline = 'N' | Answering an offer (rebindable; only active while an offer is up) |
Config.TicketMachine.PassengerFine | 150 | A player rider without a valid ticket at a police inspection pays this |
Config.TicketMachine.RemoveDodgers | true | They also get off the bus once it stands still |
Config.TicketMachine.KeepDays | 30 | Expired tickets are removed from the database after this many days (at start) |
Config.TicketMachine.Products | single, discount, day | What the machine sells, in this order: id, kind (a valid ticket type: VALID_TICKET, DISCOUNT_TICKET, SPECIAL_TICKET), price, minutes, rideOnly (valid only on the route run it was bought on), optional label (plain text; otherwise the locale key tm_product_<id>), enabled = false to stop selling it |
A rider holds one covering ticket at a time: buying again while a ticket is valid on this bus is refused. The driver cannot buy (drivers ride for free) and can only offer tickets to players on their own bus who have none. The machine works on real routes only, not on admin test drives.
Police inspections (routine and police players) check every player rider. One without a valid ticket pays PassengerFine themselves (an unpaid fine is written to the audit log); the driver is never blamed or fined for them. Tickets sold on a route show on the route report.
Integrations
| Setting | Default | Options |
|---|---|---|
Config.Target | 'auto' | 'ox_target', 'qb-target' |
Config.Inventory | 'auto' | 'ox_inventory', 'qb', 'esx', 'none' |
Config.Fuel | 'auto' | 'ox_fuel', 'LegacyFuel', 'cdn-fuel', 'ps-fuel', 'native' |
Config.Dispatch.Provider | 'auto' | 'qs-dispatch', 'ps-dispatch', 'cd_dispatch', 'custom', 'none' |
Config.Dispatch.PoliceJobs | { 'police', 'sheriff' } | Jobs that receive police alerts |
Config.Dispatch.EmsJobs | { 'ambulance' } | Jobs that receive EMS alerts |
Custom dispatch
With Provider = 'custom', ns-bus fires the client event ns-bus:dispatch with a ready alert (service, code, title, message, coords, blip). Listen to it from your own script, or edit Adapters.custom in bridge/dispatch.lua (that file is not escrowed).
Dispatch export signatures are based on each resource's public documentation. They change between versions, so test alerts with the version you run.
Notifications
| Setting | Default |
|---|---|
Config.Notifications.Position | 'top-right' |
Config.Notifications.Duration | 5000 |