10-8
Traffic stops for FiveM: pull over ambient vehicles, run the plate, check ID, search the driver and the car, write citations, and take people into custody.
Standalone. Only hard dependency is ox_lib.
Pairs with teneight_callouts but does not need it.
Install
ensure ox_lib
ensure ox_target # optional
ensure teneight
Two things to do before it will work
1. Tell it your job names. Nobody has access until their job matches a
department. Open config/agencies.lua and put your server's job names in the
jobs list for each of the three departments:
lspd = {
job = 'lspd',
jobs = { 'lspd', 'police', 'lspd_officer' }, -- add yours here
The common names are already listed, so most servers work untouched. If yours
does not, /stops whoami prints exactly what the server thinks your job is and
where it read it from.
Config.DefaultAgency is false on purpose: this is the only access check in
the resource, and setting it to a department means every player passes as
police. Leave it off once your job names are right.
2. Import the SQL, if you want records to survive a restart:
sql/teneight.sql
Without it, people, vehicles, citations and arrests last until the server
restarts. That is a supported way to run it — set
Config.Database.enabled = false and it will stop trying.
If something is not working
| Command | What it tells you |
|---|---|
/stops doctor |
Checks the whole install: dependencies, your job, the database, and whether the configured vehicle and prop models exist on this server |
/stops whoami |
What the server thinks your job and department are |
/stops why |
Why you can or cannot work the stop in front of you |
/stops tip |
Why no intelligence tips are coming through. Reports every gate, then raises one and says what came back |
/stops outfit |
Prints what you are wearing, ready to paste into an agency |
/stops eye |
Lists everything this resource puts on the third eye, which is where conflicts with other scripts show up |
/stops version |
Which build is running — the first thing to check against a report |
Vehicle and prop models are configured as lists — the first one that exists on the server is used. That is why an addon car can be first and a base-game car second, and the resource still works on a server that has neither of your liveried vehicles.
ox_target is optional but recommended. When it is running, everything on a
stop is done by looking at the thing you mean — the driver, the car, your own
cruiser. Without it you get an E prompt at the window and the same actions in a
menu. It is detected automatically; Config.Interaction.mode forces either.
Optional — restrict who can run stops:
add_ace group.police teneight.police allow
then set Config.Permission = 'teneight.police' in config/config.lua.
Controls
Keys do the work; commands are the fallback.
| Key | Does |
|---|---|
| Q | Signal the vehicle ahead to pull over |
| Spacebar | Stop them where they are, while they are yielding |
| X | Take down the person in front of you |
| G | Grab or release a cuffed detainee |
| HOME | Open the terminal — only from a driver's seat |
| hold E | The call-for-services wheel: backup, EMS, coroner, flatbed |
| E | Contextual prompts — place a detainee in a car, and the window prompt without ox_target |
With ox_target, look at the driver for everything at the window, at their car to search it, and at your own cruiser to run documents.
Walking up gives you one option: speak to them. Everything else stays hidden until first contact, so you are not handed a wall of choices before you have said a word. Speaking opens the full menu; from then on the individual actions appear on the target as they become relevant, and once the driver is out of the car you can work entirely from targeting.
A ped inside a vehicle cannot be targeted. The ray hits the car, not the person in it. So anything to do with somebody who is seated is registered on the vehicle instead, and gated to appear only while they are actually in it:
- Driver still in their car — look at the window for everything: speak, ask for documents, run the plate, ask them to step out.
- Detainee in the back of your cruiser — look at your car to speak to them or take them out again.
Once they are stood outside, the same options move to the ped where you would expect them.
All three are rebindable in FiveM's key settings, and configurable in
Config.Keys. Q is the default: it is only bound to Cover on foot, so it is
free while you are driving, which is the only time signalling means anything.
Change it to whatever suits your server.
If you have run an earlier version, FiveM will have stored the old key against the binding and does not revisit a default it has already seen. Signalling is registered under its own internal name for exactly that reason, so this change takes effect — but if a binding of yours ever appears stuck on an old key, that is why, and FiveM's key settings will move it.
| Command | Does |
|---|---|
/stops |
Signal the vehicle ahead. Same as Q. |
/stops ped |
Speak to the person in front of you |
/stops whoami |
Show which department the server thinks you are in |
/stops assist |
Attach to another unit's stop near you |
/stops off |
Step off a stop you are assisting with |
/stops menu |
Open the contact menu without walking to the window |
/stops docs |
Ask for license and registration |
/stops run |
Run the documents you are holding, from your vehicle |
/stops tow |
Call a flatbed to recover the vehicle |
/stops backup |
Request a unit to back you up |
/stops ems |
Call medics to the scene |
/stops coroner |
Call the coroner to recover a body |
/stops escort |
Toggle escorting a detainee |
/stops seat |
Put a detainee in the nearest free rear seat |
/stops duty |
Go on or off AI duty — on, off, or nothing to switch. Also on the terminal |
/stops end |
Release the driver and send them on their way |
/stops dismiss |
Send your backup unit home |
/stops cancel <unit> |
Stand a unit down: backup, ems, coroner or tow |
/stops follow |
Toggle the driver walking along with you, uncuffed |
/stops wheel |
Open the dispatch wheel without the key |
/stops division |
Choose what you are assigned to |
/stops intel |
Read the intelligence briefing |
/stops why |
Explain why you can or cannot work the stop in front of you |
/stops help |
Show all of the above, plus the keys |
Escort and seating are also options in the contact menu, so you never have to remember a command mid-stop.
The authoring tools
Three more exist for setting a server up, and they are off by default —
Config.DevCommands = false. While off they are not in /stops help, and
typing one says the same thing as typing nonsense.
| Command | Does |
|---|---|
/stops anim <key> |
Play an animation so you can see it. Takes a key from config/animations.lua, or a raw <dict> <clip> |
/stops prop <name> |
Put a prop in your hand and tune its offsets live |
/stops propoff |
Put it down again |
Turn them on while you are pointing the resource at your own animations and ped
models, and off before you open the doors — an officer who finds them mid-shift
can stand in the road playing animations at people. Every pose in this resource
was chosen with /stops anim.
How a stop runs
Get behind a vehicle and press Q.
It signals on the way on, not on the way off. The light bar and the signal
key end up being the same key on a lot of setups, so the press that turns the
lights off fires the signal too and opens a stop you never asked for. The key
waits lightsGrace and then asks whether the lights are actually on: on the way
on they are, on the way off they are not. Config.Target.lightsOnly = false
turns the check off — do that if you signal without lights, or if your light bar
is run by another resource that does not set the vehicle's own siren state.
Typing /stops never checks: you are not pressing a light key when you type it.
What follows is deliberately not instant:
- They have not seen you yet. 1-4 seconds of nothing. They keep driving.
- They clock you. Brake lights tap, they come off the throttle.
- Indicator on. 2-4.5 seconds of coasting while they hunt for somewhere to put it. This is the beat that makes a stop feel like a stop.
- They commit and crawl to the curb.
- Stopped. Hazards on, and the engine dies a beat later - not with it.
The whole thing takes about three seconds before they commit — long enough to read as hesitation, short enough not to be a wait.
You drive them
Once they indicate, their car comes off the AI and onto your steering.
A mark sits off your hood at leadDistance. Their car steers for it every
frame and matches your speed. Because the mark is fixed in your frame of
reference, turning the wheel sweeps it — and takes them with you. Turn right and
they go right. Slow and they slow. It is closer to towing them than to being
followed.
This is deliberately not an AI driving task. Task-driven steering is planned on the road graph and lags your inputs by a second or more, which is exactly why it never felt like you were in control.
So you pick the spot: shoulder, pull-off, off an intersection, across a parking lot. They go where you put them.
You have the wheel from the moment you signal. The brake tap and indicator
still happen, they just happen while you are already steering rather than making
you wait. Set instantControl = false for the old staged version where they
notice and hesitate first.
Their turn rate is capped in degrees per second (maxTurnRate), which is what
stops a big steering error snapping round in a few frames. A blend factor alone
cannot do that — no car turns like a cursor.
When you stop and they come to rest they are held completely still: no steering, no speed writes, velocity zeroed. That is what stops the sideways creep at the curb.
Wheels that roll, and turn. The mirror moves the car by writing its velocity
every frame. SetEntityVelocity on its own translates the body and tells the
wheels nothing, so the game treats them as locked — which is where the skid marks
trailing up the road behind a stop came from.
SetVehicleForwardSpeed drives the car properly and spins them, but it zeroes
the Z component, which cancels gravity and leaves the car riding high. Doing both
in that order — forward speed, then put the original Z back — gets rolling wheels
and keeps gravity.
The other half is steering. The mirror turns by rotating the whole body, so the
front wheels sat dead ahead while the car pivoted underneath them.
SetVehicleSteerBias points them into the turn and straightens them when the
car is going straight. It is cosmetic and changes nothing about where the car
goes, but it is the difference between a car driving and a car being dragged.
Neither of those stops the tires marking the road, and nothing will: driving a
car by writing its kinematics makes the tires slip against the asphalt whichever
native does the writing, and every one of those lays a decal. There is no switch
for it. So the marks are removed as they appear, in a small radius around the
car (skidClearRadius), which takes any other decal in that circle with it —
worth it against a stop that leaves fifty meters of black lines. clearSkidMarks
= false puts them back.
The brake tap is a flicker, not a hold. When they notice you, a braking temp
action takes the car off its own driver — ClearPedTasks alone does not reliably
end a vehicle driving task, and an AI still steering fights the mirror badly
enough that they never pull over at all.
The catch is the duration. A braking action holds the pedal down, and the mirror
drives by writing velocity: hold the brake while that is happening and the wheels
lock under a car that is still moving, so it slides on its tires. With
instantControl on you have the wheel immediately, so it is cut to a flicker —
long enough to replace the driving task, too short to lock anything. Without it
there is a beat of hesitation before the mirror engages, so it stays long enough
to read as a brake tap.
Spacebar stops them where they are. Normally they park when you stop and
stay stopped for settleTime, which is right when you are walking them to a
curb — but sometimes you want them down now, mid-road, and waiting a second and
a half for it is a second and a half too long. Held down, it rolls them to a
stop from whatever speed they were doing rather than dropping them dead.
It reads the handbrake control, which is what spacebar is while you are driving.
stopOnHandbrake = false turns it off, and stopControls takes any control ids
you would rather use.
| Setting | Does |
|---|---|
instantControl |
Take the wheel the moment you signal (default on) |
maxTurnRate |
Hard ceiling on their turn rate, degrees per second |
notice |
How long before they see you — ignored when instantControl |
acknowledge |
Hesitation before committing — ignored when instantControl |
leadDistance |
Where the mark sits off your hood |
minGap |
Safety gap — inside this they must outrun you |
mirrorSteering |
How tightly they copy your wheel, 0–1 per frame |
mirrorSmoothing |
How quickly their speed eases toward yours, 0–1 per frame |
mirrorCatchup |
How hard they close a gap, m/s per meter of error |
mirrorCatchupMax |
Ceiling on that correction |
minFollowSpeed |
Slowest they will roll while still moving |
stoppedSpeed |
Below this, you count as stopped |
settleTime |
How long you must be stopped before they park |
yieldTimeout |
Safety net: they settle regardless after this |
mirrorSmoothing and mirrorSteering are the two feel dials, and they fix
different kinds of fake:
- Twitchy, answers your throttle with no weight → lower
mirrorSmoothing. Writing a speed directly every frame turns the car into a cursor; easing toward it gives them inertia. - Snaps round corners, or turns on the spot → lower
mirrorSteering. Steering authority is already scaled by their own speed, so a stopped car cannot pirouette, but a heavy hand still reads as rails. - Wanders wide and loses your line → raise
mirrorSteering. - You keep driving into the back of them → raise
minGap. Inside that distance they are forced to run faster than you no matter what the station correction worked out to, so you cannot close on them while steering them over.
On spinning: the mark is never allowed to sit behind their bumper. They run
faster than you by design, so a mark fixed at leadDistance gets overshot — and
a mark behind the car is a 180 degree steering error, which reads as the car
pirouetting on the spot. The mark is projected onto your heading and pushed to
at least 6m ahead of wherever they actually are, and steering is additionally
refused whenever the mark is not in front of the car.
When you settle, they roll to a stop rather than dropping dead on the spot —
and they stay exactly where you put them. Nothing nudges the car afterwards:
SetVehicleHalt slides a vehicle to rest over a distance and a heading tweak
rotates it, so neither is used. The velocity is killed and the handbrake goes
on, and that is all.
Speaking to a seated driver winds their window down.
On the car riding high while you steer it: SetVehicleForwardSpeed is the
obvious native for pushing a car along its own axis, and it is the wrong one —
it zeroes the vertical component, so calling it every frame cancels gravity and
the car floats over bumps instead of sitting on its suspension. The mirror sets
the velocity vector directly and preserves the existing Z.
Set Config.LogLevel = 'debug' and the console prints mirror engaged,
mirror settling and mirror released with the stop id — the quickest way to
tell whether it is actually running.
Not everyone parks well. Each stop rolls a quality: tight to the curb, left sticking out, at an angle, or stopped short. It is called out in the notification, and it changes where you have to stand.
All of the timings are in Config.Pullover.
Three departments
LSPD in the city, LSSD in the county, SAST across the state. Which one you are in comes from your job name, and that is also the access check — a job that is none of the three gets nothing.
| Patch | Callsign | Terminal | |
|---|---|---|---|
| LSPD | Los Santos | 1-ADAM-n |
Blue |
| LSSD | Blaine County | 2-LINCOLN-n |
Sheriff tan |
| SAST | Everywhere | 3-MARY-n |
Trooper green |
Set Config.DefaultAgency = false once your jobs are configured; until then it
falls back to LSPD rather than locking everybody out.
The terminal is your department's
Each agency gets its own theme: the header carries its name, the shield carries its short code, and the whole palette shifts. A sheriff's terminal does not claim to belong to the city police.
A department is two colors. accent and trim — everything tinted is
derived from them in CSS rather than listed, because a theme that redefined
twenty variables would drift the moment one was forgotten. Status codes keep
their own colors throughout, since 10-7 means out of service in every
department and red is the reason you can read it at a glance.
Units on duty and BOLOs show which agency they belong to, so a full dispatch board reads as three departments sharing a channel.
Your own department turns out
Backup arrives in your agency's vehicle, with its livery and its uniform. Ask for backup as a deputy and a sheriff's car comes.
Jurisdiction
Only Blaine County is enumerated, in Config.Counties. Everything else is the
city, and state units are at home in both — one list to keep right rather than
two that can disagree about somewhere.
Opening a stop off your patch tells you so and does nothing else. That is
deliberate: officers cross county lines constantly, and a resource that refused
to let them is a resource people turn off. Config.Jurisdiction.enforce = true
makes it a wall; notify = false makes the three departments simply share the
map with different liveries.
Booking works at any station by default. Config.Jail.ownStationsOnly = true
restricts each department to its own — realistic, but it means a deputy driving
past a police station with somebody in the back.
Reading a job
It tries qbx_core, qb-core, ox_core and ESX, in that order, skipping any that is not running. Each is a separate attempt on purpose — they disagree about their own export names between versions, so one guess is one silent point of failure.
/stops whoami when it goes wrong, and it is worth knowing why it needs to
exist. When the job read fails, the fallback agency takes over — and a resource
that has quietly decided everybody is city police looks exactly like a working
one. That command shows the job string it read, which framework answered, the
agency it resolved to, and the job names it is actually looking for.
If none of the four fit, replace Config.ResolveJob with a function taking a
server id and returning a job name. One function to change rather than a guess
baked through the resource.
The department is re-checked, not read once at spawn. People connect off duty and take a job afterwards, and it is asked again every minute and every time you open the terminal.
Police on the map
Other officers show as blue blips. The default player blip is the same white arrow as every civilian, and officers are the one thing you want to find on a map without having to ask.
The list comes from the job, not from a duty toggle this resource does not own: if the framework says they are police, they are police. Only real job matches count — the fallback agency would otherwise put a blip on every player on the server.
Blips attach to the ped, so they follow. The list is refreshed every interval
seconds to catch people joining, leaving and streaming in.
perAgency = true colors them by department instead of all blue, so a glance
says who as well as where.
Working somebody else's stop
A stop belongs to whoever opened it — that is who its paperwork is filed under —
but anybody who walks up can attach to it and work it. Third eye anyone at the
scene and pick Assist this stop, or /stops assist. /stops off steps back off
one.
Everything is gated on being attached, not on being the owner. Once you are on it you can run the plate, take documents, search the car, cuff the driver, write the citation, book them — the same options the owner has, because they are the same options. There is no reduced passenger-seat version to keep in step.
The owner publishes how far along the stop is, and that turned out to be the whole thing. Whether the driver has stepped out, whether they are in cuffs, what has already been searched — all of that lives on the owner's client, so without it an assisting officer's menu is the menu of a stop that has not started: no cuffs, no escort, nothing that waits on the driver being out of the car. It is pushed the moment it changes rather than only on the tick, so cuffs going on appear on their partner's screen immediately.
Only the keys that mean something are accepted, so a client cannot post arbitrary fields onto a record everybody else reads.
The one thing you cannot do is end it. That option becomes Step off this stop instead, and ending it stays with the owner.
Entities are the whole problem this solves. A vehicle handle is per-client and means nothing to anybody else, which is exactly why a second officer could not see your stop before. The server now keeps the network ids for the car and the driver, and a joining officer resolves them into handles of their own.
Joining is range checked against where the stop actually is, not against
where a client says it is. The owner reports the position of their scene every
reportEvery seconds, and that is what joinDistance is measured against — so
attaching is something you do by walking up to a scene.
State changes go to everybody attached. An assisting officer whose menus still think the driver is sat in the car is worse than no assist at all.
Leaving is handled at both ends. Step off and the owner is told. Disconnect and you come off the list. The owner disconnecting or ending closes the stop, and everybody attached is told to let go rather than being left holding a stop that no longer exists.
Records that survive a restart
Import sql/teneight.sql once and set Config.Database.enabled = true.
oxmysql is the assumption; any resource exporting execute and query_async
will do.
The in-memory tables stay the source of truth. Searching people and plates already happens there and it is fast, so the database's only job is to make sure that layer is still there tomorrow. It hydrates at trunk and writes changes back.
Records go in as JSON blobs with a few indexed columns beside them. Adding a field to a person never needs a migration that way, and the indexed columns — record number, name, plate, incident — are enough to query by hand when you want to. It is a deliberate trade: you lose SQL-side reporting, you gain never having to migrate a schema you are still changing.
People and vehicles are batched every saveInterval, because points, notes
and priors change constantly and a write per change would be a lot of writes for
no benefit. Citations, arrests, BOLOs and field interviews are one-off events and
go in immediately. A clean shutdown flushes whatever is still pending.
Counters persist too. Without that, the next person generated after a restart is P-1000 again, sitting alongside the P-1000 who already exists, and yesterday's incident numbers come back around. Incident numbers are written the moment they are handed out rather than on the timer — one issued twice is worse than one that skips a few after a hard restart.
It degrades rather than breaks. With the database off, or oxmysql not
running, everything works exactly as before — people, plates, citations, BOLOs.
They just last until the next restart, which is where this resource was until
now. It waits waitFor seconds for oxmysql to start before deciding, because it
often starts after this one does.
Running, and catching them
A field contact with somewhere to be will take it. The roll happens the first time you put pressure on them — asking for a name, reaching for cuffs — and is remembered, so a runner is a runner rather than somebody who gets a fresh chance to bolt every time you open a menu.
fleeChance is by disposition: a runner goes two times in three, a compliant
subject almost never. The stop stays open when they go. Catch them again and it
carries on — same person, same record, and now a charge for running.
Catching them is a third option. Backup will chase somebody down and shooting
them is always available, but neither is what you want for a man who ran from a
name check. Get within reach and press X: he goes on the floor for
downTime, long enough to close in and start the cuffs, not so long that it is a
free arrest.
You go down too, briefly. A takedown where only one party hits the floor reads as
a shove, and closing the gap should cost something. officerFalls = false if you
disagree.
Warrants
They were previously a scary label and nothing else. Now:
- WARRANTS in the terminal lists everybody currently wanted, with the reason, when it was issued, and their prior arrests. Click one to open their record.
- Booking somebody clears theirs, which it always did.
- CLEAR on a row clears one by hand, for a warrant served somewhere this terminal never saw. It writes a note onto the record saying who cleared it and why, so it is not a silent edit.
The list is built from the person records rather than kept as its own table. Two lists that can disagree about who is wanted is exactly the bug you do not want in this.
Surrender at gunpoint
Point a gun or a taser at somebody and they go down on their knees with their hands on their head. Free-aim only — this reads what you are actually aiming at, not what happens to be in front of you.
They face you first. Kneeling side-on to a gun reads as ignoring it.
They stay down while you are aiming and for holdTime after you stop, which is
long enough to holster and walk up to them without them getting straight back
up. Then they get up and go back to their own life — unless they belong to a
stop, in which case whoever owns that stop decides what happens to them, and
anybody in cuffs stays exactly where they were put.
A fist is not a reason to kneel. Anything the game counts as a firearm
qualifies, plus whatever is named in weapons — the stun gun is not in the
firearm group and has to be listed, which it is, because it is the one you most
want this to work with.
Somebody with a gun of their own gets a choice. Decided once, the first time
you point something at them, so aiming at the same man twice does not re-roll
his nerve. On a stop the odds come from their disposition — a compliant driver
gives up nine times in ten, a hostile one draws four times in five — and a
stranger in the street gets a flat armedComplyDefault.
Drawing puts a stop subject down the hostile path that already existed: they go for a pistol, backup engages rather than watching, and the weapon is still recoverable from the body afterwards. Nothing about that changed; this is just a new way into it.
On their knees, there are two things worth offering: talk to them, or put them in cuffs. Everything else is hidden while they are down — searches, ID checks, backup orders, the lot. They are already doing what they were told.
Cuffing a stranger opens a field contact behind it, so you never end up with somebody in handcuffs that nothing has a record of. The moment the cuffs go on they are in custody rather than kneeling, and the full set of options comes back.
Your own people are exempt. Backup officers, paramedics, tow drivers: none of them end up on their knees because you swept past them.
| Setting | Does |
|---|---|
distance |
How far the aim reaches |
weapons |
Named weapons that count beyond the firearm group |
holdTime |
How long they stay down after you stop aiming |
armedComply |
Chance an armed person gives up, by disposition |
armedComplyDefault |
The same, for somebody not on a stop |
Config.Surrender.enabled = false turns it off.
Field contacts
Walk up to anybody in the street, third eye them, Speak to them — or /stops
ped if you are not running ox_target.
It is the same stop record everything else is built on, with no vehicle attached. That is the whole design: custody, searches, citations, backup, booking and the terminal all work on a person stood on a sidewalk without any of them knowing the difference. There is no parallel system to keep in step.
The menu drops what has no meaning on foot — no plate to run, nobody to ask to step out, no car to search, no flatbed. What is left reads for a person rather than a driver: Ask for identification instead of license and registration.
You still walk back to the cruiser. Asking gets you their ID card in your hand, not the answer — the same physical handover, the same walk, the same run at the terminal as a license and registration. That walk is the point of the whole loop, and a field contact is not exempt from it.
A hostile subject refuses to hand anything over, and a nervous one often does too. A refusal ends nothing: they are still stood in front of you, and you can search them, cuff them, or ask again.
They get a real record, the same generated person a plate would produce — warrants, license status, priors, notes — keyed so that stopping the same person twice gets the same record rather than inventing a new stranger.
Everything after that is the machinery you already know. Pat them down, cuff them, walk them to your car, drive them to a station and book them. Call backup and have them make the arrest. The stop ends with Send them on their way, and they go back to wandering.
Config.FieldContact.enabled = false turns it off, and reach is how far in
front of you /stops ped will look.
Passengers
A car is not always one person. Everybody else in it is tracked with their own state — which seat, whether they are out, searched, in cuffs.
While they are seated, target the vehicle and pick Speak to the occupants for a list. Several people in seats give a target ray nothing to distinguish between, so they are chosen from a menu rather than aimed at. Once somebody is out of the car they are an ordinary ped and can be targeted directly.
Passengers get everything the driver gets: identification, step out, pat down, cuffs, taking hold of them, putting them in a car, a citation, and being sent on their way — back into their own seat if it is still free, on foot if the car has gone. It is the same code either way — escorting is written around whoever you currently have hold of rather than around the driver, so a passenger you have just cuffed walks exactly like the person behind the wheel. One at a time; taking hold of somebody lets go of anyone else.
Each passenger has their own record, generated per seat, with its own license
status and warrants. Passenger searches roll separately and once each, cached by seat, so
searching the same person twice does not conjure a second haul — they carry less
often than the driver, since they are not the one who chose to drive around with
it (Config.Search.passengerContrabandChance).
Anything found on a passenger still pre-ticks the citation.
The escort key follows your hands, not the driver. Press it while walking a passenger and you let go of the passenger — it does not drop them and pick the driver up instead. With your hands empty it takes whoever is in cuffs and nearest, driver or passenger. Anything that cuffs somebody writes it to the record they belong to, so the menus agree with what you are actually holding.
Somebody who has stepped out stays on the list. The occupants menu is rebuilt every time you open it, and rebuilding it purely from who is sitting in a seat used to drop everybody standing at the curb — so a passenger you had asked to step out vanished and could not be searched, cuffed or run.
Putting somebody in a car uses the door you are stood at. The free rear seat nearest you is the one they take, so nobody walks round to the far side.
Two silent things stop a cuffed person boarding a car, and both of them made Place in vehicle look broken:
SetEnableHandcuffs(ped, true)blocks vehicle entry outright, so a cuffed detainee stands at the door doing nothing until a timeout warps them in.- a car locked against peds by the first detainee going into it stays locked, so the second one can never board it at all.
Boarding now goes through one place that takes the cuffs off for the walk-in, unlocks the doors, puts both back afterwards, and only warps as a last resort. It is the same path for you and for backup.
Documents and the terminal
Asking for a license gets you the license, not the answer.
- Ask for license and registration. They dig it out; you take it. You are now physically carrying it, prop and all.
- Walk back to your cruiser. Nothing has come back yet.
- Get in your cruiser and press HOME. The terminal is in the car, so that is where lookups happen — you cannot run a record stood in the road.
- Decide. Citation, cuffs, or hand it back and send them on.
The terminal
Seven sections down the left, click between them.
DISPATCH. Your status bar and the current stop. Ten-code buttons set your unit status — 10-8 available, 10-6 busy, 10-76 en route, 10-97 on scene, 10-15 one in custody, 10-7 out of service — and the pill in the header follows the color you set. The active stop shows the plate, what state it is in, and whether you are holding documents, have somebody in custody, or have had the car recovered. Below that, every unit on duty with their status and how long they have held it. Yours is marked.
PERSONS. Search by name or record number, or arrive here from a license you ran. The record carries the physical description, address, license status, penalty points, insurance, and the count of prior arrests and citations. Flags sit above everything else, because they are the reason you ran the name: active warrants, violent offender, known to resist, medical alert, gang affiliation, armed and dangerous. Below is the criminal history with dates and dispositions, and officer notes — free text you can add yourself, stamped with your callsign and visible to everyone who pulls that record afterwards.
VEHICLES. QUERY runs an exact plate and creates the record if the plate has never been seen. SEARCH matches part of a plate or an owner's name against vehicles already on file, which is how you work from half a plate. The record shows make, color, year, registration, insurance, how many times the car has been stopped, and whether it is reported stolen. OPEN RECORD jumps to the registered owner.
WARRANTS. Everybody currently wanted, with the reason, when it was issued and their prior arrests. Click one to open their record; booking somebody clears theirs automatically.
BOLOS. Post one for a person or a vehicle and it goes out to every officer on the server immediately, with a sound and a notification. Clear it when it is done. This is shared state, not a local list.
REPORTS. Citations and arrests, yours or the whole department's — a terminal
that can only show you your own work belongs to you rather than to a department.
Each one carries its own CAD incident number in the shape real ones take —
26-252-0004, being year, day of year, sequence. Field interviews are filed
from here too: name, location, circumstances. Charges filed on an arrest become
priors on that person's record.
CODES. The ten-code reference. Nothing looks them up for you.
The nav dots tell you which sections have something in them, and go red when what they hold is flagged.
Plate query works with no stop running — it is a database search, and it returns
vehicle registration only, never somebody's record. It answers to the same rule
as a name, though: plates are run from the car. A plate you read off a
bumper still has to be typed into something, and the terminal is in the
cruiser. Config.Documents.plateInVehicleOnly = false relaxes it.
A record pushed to the terminal stays up wherever you are. Only the terminal proper is tied to the driver's seat; a lookup you run at the roadside, such as a passenger's identification, has to be readable while stood in the road. Closing it the instant you were not in a car made those lookups look broken.
Config.Documents.lookupInVehicleOnly = false relaxes it back to working beside
a car rather than in one.
Releasing somebody while still holding their license asks you to confirm.
The server enforces the order: it will not return a record for documents you were never handed. Running the plate is separate and passive — it tells you about the car, needs no cooperation, and opens the same terminal.
Set Config.Documents.enabled = false to skip the whole loop and get the record
straight from the window. Config.MDT.agency is the name across the top.
From the contact menu you can:
- Run the plate — make, color, year, registration, insurance, registered owner, whether it is reported stolen, and how many times it has been stopped before. Passive; works whether or not the driver cooperates.
- Request license and documents — name, DOB, license status, penalty points, prior arrests, and any outstanding warrant. A hostile driver may refuse.
- Ask the driver to step out — required before you can pat them down or cuff them.
- Search the driver / search the vehicle — consent is asked for first.
- Issue a citation — multi-select from the offense catalog.
- Detain in cuffs → Book into custody.
- Release and end the stop — the driver gets back in and drives away.
Dispositions
Every stop rolls a hidden disposition that drives the whole encounter:
| Odds | Behavior | |
|---|---|---|
| Compliant | ~60% | Pulls over, answers, consents to a search |
| Nervous | 26% | Pulls over, evasive, usually refuses consent, may resist |
| Runner | 10% | Half never stop at all; the rest pull in and bolt as you approach |
| Hostile | 4% | Stops, then turns on you when you ask them out or try to cuff |
A driver who is already wanted, or driving suspended, is more likely to be nervous — so a warrant hit tends to feel like one before you run the check.
Odds are in Config.Dispositions.
Identity and records
Identities are generated per plate and cached server-side, so:
- Two officers on the same stop see the same person.
- Pulling the same car over next week gets the same driver, carrying whatever points you gave them last time.
- Penalty points accumulate. Cross
Config.SuspendAt(default 12) and the license flips to suspended on its own.
What the client is told, and when
- Disposition is sent when the stop opens — the officer's client owns the driver AI, so it has to know whether this one runs.
- Identity only after the check is actually run.
- Contraband only what a completed search turns up, item by item.
That last one is the part worth protecting: without it a client could read ahead and know which cars are worth searching.
Calling for things
Hold E to bring up the wheel. Flick toward what you want and release. Release in the middle, or press Escape, to cancel.
It works with or without a stop running — it just has less on it. Backup is something you want whether or not you have somebody pulled over.
| Shows when | |
|---|---|
| Backup | Always |
| EMS | The subject is hurt or dead |
| Coroner | The subject is dead |
| Flatbed | There is still a vehicle to recover |
A tap of E still does whatever it did before — place a detainee in a car, speak to the driver. Hold is the wheel, tap is the action.
Ending the stop is on the wheel too. End the stop sits second from last, above Close — it is the one thing you might need from the far side of a scene, or with nobody left to target.
Anything already on its way turns into its own cancel. Call EMS and the EMS
wedge becomes Cancel EMS in red; stand them down and it goes back. The wheel
never grows a second row, and there is never a separate cancel entry to hunt
for. /stops cancel <backup | ems | coroner | tow> does the same by hand.
A prisoner already in the back of your own cruiser is fetched out of it before the officer starts walking — his door was locked against peds, so he would reach the car, find he could not open it, and stand there. And the walk over watches progress rather than the clock: re-issuing a walk task on a timer throws away a path that was working, which is what left him circling.
A unit that has already finished its job cannot be canceled, because there is nothing left to stand down — a car already on the flatbed has gone.
Selection follows the angle of your pointer from the center rather than needing an exact hit on a wedge: at speed you flick in a direction, you do not aim at a shape.
Config.Radial.holdTime is how long the hold has to be (260ms by default) —
long enough that a normal tap never triggers it, short enough not to feel
sticky. Config.Radial.enabled = false turns it off.
Booking
Nobody is booked at the roadside. Config.Jail.locations lists where somebody
can be booked, and the nearest one wins. Mission Row is set up out of the box;
add your own stations to the list.
An entry is either a plain point, or a table:
{
label = 'Mission Row',
at = vector3(407.11, -981.3, 28.27),
}
A unit drives to the booking point itself — where the blip is, and where you
would go looking for them. arriveDistance is deliberately wider than
distance, because a booking point is usually inside a building and a car can
only ever reach the road outside it; too tight and the run never finishes, with
the driver sat at the curb waiting to reach a spot no car can reach.
Add driveTo = vector3(...) to an entry only if you want a car sent somewhere
other than the blip.
Each one gets a handcuffs blip on the map, so the rule is something you can see
rather than something you find out by being refused. Short range by default, so
it shows when you are near it rather than sitting there from across the city.
blip = false removes them, and blipSprite is the icon.
Backup drives the run with lights off. A prisoner in the back is not an emergency, so they drive it in like any other car on the road — with two exceptions.
They do not queue at red lights — and neither does any other responding
unit, on the way to you or away from you. DRIVE_STYLE_NORMAL has the
stop-at-traffic-lights bit (128) taken out of it, and the lights are overridden
green for that car on top of that.
Both, because neither is reliable alone: the style flag tells the driver not to wait, the override tells the world the light is green for him. The override also gets cleared as a vehicle moves between intersections, so it is re-applied while they drive rather than set once at the curb — set once, they queue at the third red instead of the first.
They announce themselves crossing an intersection. One blip of the light bar and
straight back to dark, not a light bar left running: they look flashAhead up
the road for a set of lights, and flashCooldown keeps the same intersection from
being announced twice.
Two ways in:
- Drive them yourself. Cuff them, put them in the back of your car, drive to a station. You get a word when you arrive, and Book into custody works from there.
- Let backup do it. Have backup arrest them and they load them, drive them in, and book them at the far end. The stop closes when they get there.
The rule is enforced on the server rather than only in the menu, because it is
the one thing that decides whether an arrest counts. The exception is the backup
run: a unit drives them in while you are still stood at the scene, so your own
position says nothing, and that path is trusted the same way the charge list
already is — this resource is not a security boundary. Config.Jail.enforce =
false turns the whole rule off and books work anywhere, as they used to.
There is no prisoner transport van. A unit that can arrest somebody can drive them in as well, and one unit doing a whole job beats two doing half each.
Backup
The one service with no job that finishes: they turn out, get out, and take up
post. A closing stop sends them home, or /stops dismiss does it by hand.
One officer, and he walks with you. A call for backup on a traffic stop gets you a single unit, and one officer who stays with you reads better than two who split up. Walking with you is what he does by default — you have to pick a directive to have him do anything else.
He holds a meter and a half off your shoulder — close enough to be with you rather than trailing after you — and keeps holding it as you turn, because the offset is read in your frame rather than the world's. Get back in your car and he goes back to the cruiser rather than chasing your bumper; get out again and he falls back in beside you.
Set crew to a ped model and you get two instead: the driver takes the car and
the second man walks with you.
He faces the subject when you stop. Walk up to the window and he plants himself and turns onto whoever you are dealing with, rather than standing there scanning the street. It tracks: a driver who wanders along the shoulder does not end up behind his shoulder. Whoever is out of the car outranks the ones still sat in it, and with nobody left to watch he watches the car they came out of. Start walking again and he falls back in behind you.
postSpeed is the speed below which you count as stopped, follow = false
puts both of them on the car instead. Hold this position on either officer
stops him walking with you; Walk with me starts it again.
They decide how hard to go on their own. A watcher sits over the scene the
whole time backup is there, sweeping every 400ms out to watchRadius:
| What the subject does | What backup does |
|---|---|
| Fists, or resists hands-on | Taser |
| Bat, knife, anything melee | Sidearm (lethalOnMelee = false tasers instead) |
| Draws a firearm at the stop | Sidearm |
| Nothing | Back on post |
Drawing a firearm at a stop counts as hostile on its own — nobody pulls one to hand over a license. Somebody armed twenty meters away who has not turned on anybody does not, so a passer-by carrying is not a reason for your backup to open fire. Worst case on scene wins: a gun in play outranks a fist fight happening at the same time.
They hold the line for settle after it goes quiet rather than holstering the
instant somebody hits the floor, and a subject already ragdolled stops counting
as a threat until they get up. force.enabled = false turns the whole thing off
and leaves them standing there.
They join in when it goes wrong. The moment your subject runs, backup reacts on its own:
| Situation | What they do |
|---|---|
| Suspect drives off | Everyone back in the car, siren on, vehicle pursuit |
| Suspect on foot | Out of the car and after them |
| Suspect turns armed | Draw and engage |
Both officers go, not just the driver — a passenger left standing in the road while the car drives off is the usual bug here.
Target them to give orders: Traffic control, Go after them, Hold this position, Walk with me, Send them home.
Hand them the work. Delegation sits on the subject rather than on the officer — you point at the person you want dealt with, not at the man you want to deal with them:
| Target | Option |
|---|---|
| Driver or passenger | Have backup arrest them |
| Driver or passenger | Have backup search them |
| The stopped vehicle | Have backup search the vehicle |
He walks over, does it, and falls back in beside you. An arrest does not end at
the cuffs: he then walks them to his own car and puts them in the back, the same
way he would drive them in, and comes back to you rather than getting in
(arrestToCar = false stops at the cuffs). His walk back to the car is pathed
and watched for progress: a straight-line walk stops dead at a curb or a parked
car, and re-issuing it on a timer throws away the progress already made — that
pair is what made the walk look like a struggle. And a responding unit's own car
is never locked against peds, because a crew locked out of its own cruiser
stands beside it forever.
He walks to the ground beside the door they are going in, not to the door itself: the bone sits inside the car's collision, and a navmesh route to a point inside a vehicle never resolves.
Ped avoidance is off for the whole escort. This is the one that mattered, and it is specific to NPCs. An officer sent at a person is told to walk to them and, at the same time, to keep out of their way — so he arcs round instead of arriving. Once he has hold of them it is worse: the detainee is attached to his hand but is still a ped standing in the world, so his avoidance sees somebody right on top of him and steers away, which moves the detainee with him, so he steers again. Walk a few feet, turn, walk a few feet, turn. A player never does this because a player does not run avoidance, which is why the same escort looks fine in your own hands.
So SetPedSteersAroundPeds and SetPedSteersAroundObjects go off for the
duration and back on at the end, and the two of them are explicitly told not to
collide with each other.
The route itself is pathed and issued once — the straight line from a subject to a car parked behind your cruiser usually runs through one or both of those cars, and walking into bodywork ends the task and turns him round to retry. Two retries at most, only after four full seconds of standing still.
Nothing else is allowed to take him off the walk either. The use-of-force watcher used to: engaging clears his tasks, so a threat that read true for a single tick stomped the escort. While he is walking a prisoner, only a drawn firearm pulls him off it — a scuffle is not worth dropping a man in cuffs for.
Sending backup home books whoever is in their car. A detainee sat in a car that is about to drive off has to go somewhere, and booked is the only answer that is not deleted or left standing in the road. The charges are whatever the stop turned up — the same list the booking dialog would have offered you — and they leave with the car. If it was your driver, the stop closes with them.
What he finds lands on the stop exactly as your own searches do, so contraband he turns up still pre-ticks the citation, and somebody he cuffs is in custody as far as the rest of the resource is concerned. One job at a time: the options gray out while he is busy.
They walk back to the car when sent home, rather than jogging, and the driver walks back too — he was left standing on the sidewalk while the car tried to drive off without him.
Traffic control
The zone opens the moment the stop settles, not when backup arrives. Your cruiser parked in a live lane is what everything queues behind. That was the argument for opening a zone on every stop, and it did not survive contact with a highway: 35 meters of road capped at walking pace with one lane held backs up in seconds behind a truck, and a taillight check should not close a lane.
So it is off by default. Directing traffic is something you ask for - order your
backup unit onto it from the eye and they work the lane while you work the car.
autoOnStop = true brings the automatic zone back if you work quiet streets and
would rather not think about it.
There are no cones: what goes out is a speed zone over the scene and, once somebody is free, an officer waving cars past.
The zone only touches traffic that is actually coming past you. A car has to
be facing the scene — approachDot, a dot product where 1.0 is dead-on and 0.0
is side-on — before its speed is capped at all. Without that test the zone is a
sphere round your cruiser, and it slows the opposite lane, the cross
street and everything sat in the intersection behind you to walking pace. That is
how you cause a jam rather than prevent one.
Cars that get stuck are sent onward, the way they were already pointing.
Ambient traffic queues rather than overtakes — one car stops behind you and
everything behind it stops too, and none of them works out that the lane
alongside is empty. Anything that sits still inside the zone for stuckAfter is
aimed at a point exitDistance ahead of itself, snapped to the nearest road
node, and checked to make sure that node is genuinely in front of it.
Along its own heading, and never to a shared point off your hood. A common destination is what produces U-turns: half the traffic gets sent to a spot behind itself or across a median, and the AI's answer to "drive to a point you cannot reach directly" is to turn round and go the long way. If you ever see the street start doing three-point turns around you, that is the shape of the bug.
Once they clear the zone they go back to wandering, so nobody parks at the far end of the run.
Three things happen at once. A road-node speed zone means cars are already
slowing before they reach you rather than braking on top of you. Every AI driver
inside radius then has its top speed capped at speed, its aggression dropped
to zero, and the steer-around flags set with a driving style that avoids stopped
vehicles — that last part is what makes them swing wide of the scene instead of
threading the gap. And somebody is visibly directing.
radius = 45.0 with speed = 6.0 reads as traffic crawling past a scene. Drop
the speed further and they stop dead, which looks worse than being ignored.
Cars are released the moment they leave the zone, and every one still inside it is released when the zone closes — on dismissing backup, closing the stop, backup leaving for a pursuit, or the resource stopping. That matters: a capped car nobody released crawls for the rest of its life.
closeRoad = true additionally suppresses the road nodes, so the AI reroutes
around the block entirely. It is off by default, because that is a road closure
rather than traffic control and the street goes noticeably dead.
Standing an officer in the road is a directive, not something that happens to him. Traffic control on a backup officer toggles the man, not the zone — the zone is already up. With one officer on scene, putting him on the lane means giving up the one walking with you, so it is a choice you make rather than something he does the moment he arrives. Select it again and he comes back to your shoulder.
Turn the whole thing off with Config.TrafficControl.enabled = false. It is
already limited to scenes you ask for - autoOnStop is off - which on a busy
intersection is the right answer: nothing happens until you want it to.
Lights, no siren, and not until they are parked. siren = 'lights' runs
the light bar with the audible siren muted; sirenWhen = 'arrival' holds it
back entirely until they have stopped, so backup drives in dark and only lights
up once it is actually on scene with you.
They unmute for a pursuit and go back to lights-only when it ends. siren = true
gives you both, and dropping sirenWhen lights them from the moment they spawn.
Note that muting is the only way to keep a light bar without the noise —
SetVehicleSiren(false) takes the lights with it. Which is exactly what is
wanted on the way out: the lights go off the moment a unit is released,
before anybody starts walking back to the car, so ending the stop takes the
scene dark instead of leaving a lit cruiser wandering the map. That applies to
every responding unit, not just backup.
They park behind you, and back off until there is room. parkBehind is the
first gap tried; if something is already within parkClearance of that spot, or
it is not on a road, they try parkStep further back, up to parkTries times.
One fixed gap is the whole problem on a busy scene — the space behind you is
already the car you pulled over or your partner's cruiser, and a unit told to
park in an occupied space either shunts it or gives up. Assisting somebody with
your own car parked elsewhere, they measure from the stopped vehicle instead.
parkBehind is the gap in meters,
center to center — 7.5 leaves roughly eight feet between the bumpers. They take
your heading rather than the road's, so a cruiser stopped at an angle gets
backup square behind it.
They spawn up the road behind you, not in front. A unit that comes from ahead has to turn around before it can pull in behind you, and that turn is the part that reads as staged. If there is no road behind you it falls back to a scan rather than refusing to come.
They creep onto the spot rather than parking near it. TaskVehiclePark is
the obvious native for this and the wrong one: its pull-over mode makes the AI
seek its own idea of the curb, so a unit that drove in on the running lane
parks on the running lane no matter how carefully the mark was placed. Driving
to the coordinate at walking pace keeps them on your line instead of the road's.
Nothing is ever snapped into position. If the AI parks badly, that is where it parked. Teleporting a car into line is more obviously wrong than a bad park is, and it is the thing that makes a unit read as a script.
The livery is forced on every spawn, so a unit never turns up in a plain car. Addon vehicles expose liveries through one of two systems and it is not always obvious which, so both are set — the wrong one is simply ignored.
Officers escort properly. A backup officer sent to make an arrest walks over, takes physical hold of the subject, walks them to his own car and puts them in, using the same attach and cuffed walk you do.
Three things that matter if you touch that code:
- Clear the officer's tasks before giving them a new one. Two live walk tasks fight, each re-pathing around the other's destination, and the NPC circles instead of going anywhere.
- Re-issue the walk rather than setting one long task. A single go-to that gets blocked never recovers; the officer just stands there.
- A seated prisoner needs pinning. Blocked events, a kept task, no dragging out, and locked doors — otherwise they climb straight back out and wander off.
- Lock last, and only once. Lock state 2 stops any ped using the doors, crew included, so a van locked the moment the prisoner is in strands its own officer outside. The lock waits until everybody who belongs in the vehicle is in it, and exempts you explicitly or it would shut you out of your own cruiser.
The same pinning applies when you put somebody in a car yourself.
If the wheel misbehaves
The key release is detected by the page, not by the game. That is not an
implementation detail you can ignore if you change this: SetNuiFocus stops
game controls registering entirely, so the instant the wheel opens
IsControlPressed reads false. A release check on the Lua side fires
immediately and the menu flashes open and shut rather than staying up.
There is a 12 second safety timeout in case the page never reports a release — you will never be left with a menu on screen and no game input.
When it goes wrong
A stop does not end because it went badly. Running, being tazed, dying, or having the car towed all leave the record open, because all of them are things that happen during a stop rather than instead of one.
They ran. The stop stays open and moves to fleeing. Chase them, taze them,
put them on the ground — the target options stay on the ped the whole time, so
you can still cuff and book them a street later. The evading charge is sticky:
once they have run it stands even if you catch them.
They got hurt, or died. EMS can be called for either — somebody you tazed off a wall does not have to be a corpse to need a medic. An ambulance drives in and the crew work on them:
- Injured — treated and put back on their feet.
- Dead — resurrected, then treated.
Either way they come back in a proper standing state, and a revived subject is still under arrest, so the stop carries on.
Call the coroner instead if you would rather they stayed dead: a wagon drives in, the body is recovered, and the stop closes.
Working the scene. Somebody who pulled a weapon and got shot for it leaves both behind. Target the body for Recover the firearm and Collect evidence — the weapon they drew is remembered on the stop, so it is still recoverable even if the ped dropped it on the way down. Both fire an event for your inventory or property system:
AddEventHandler('teneight:evidenceCollected', function(d)
-- d.kind is 'weapon' or 'scene'
-- d.weapon, d.items, d.plate, d.stopId
end)
Those two options are available without speaking to anybody first, since a body you never got to talk to still has a weapon on it.
They are in custody. Nobody is booked at the roadside. Either put them in the back of your car and drive them to a station yourself, or have backup arrest them — backup then drives them in and books them there, and the prisoner leaves with the car rather than being deleted where they stand.
The car got towed. Nothing changes about the stop — you may still have the driver in front of you or in the back of your cruiser.
Everybody is gone but the car is still there. Arrested and driven off, recovered by the coroner, or simply lost — the stop stays open on the vehicle alone so you can still have it recovered. It only closes once there is nothing left to deal with.
AddEventHandler('teneight:subjectRevived', function(d) end)
AddEventHandler('teneight:bodyRecovered', function(d) end)
AddEventHandler('teneight:detaineeTransported', function(d) end)
Models and timings are in Config.Services. Every unit spawns on a road out of
sight and drives in under its own power; nothing teleports to you.
Crew models and uniforms
Police units use mp_m_freemode_01 and are dressed from
Config.Services.copOutfit, with a random face each so a two-officer unit is
not two copies of the same person.
Hair comes from an explicit list of base-game indices (hairStyles), not a
blind random over the whole range — custom hair packs append themselves after
the base entries, so a plain random lands on addon hair. Eyebrows are set from
eyebrowStyles and colored to match the hair, as is any facial hair. Hair
color stays in the natural end of the palette; the greens and pinks live
higher up.
Component slot numbers are not guessable, so they are named in the config:
| Slot | |
|---|---|
| 4 | legs |
| 5 | bags and parachute |
| 6 | shoes |
| 7 | scarves and chains |
| 8 | shirt |
| 9 | body armor |
| 11 | jackets |
Replace Config.Services.dressPed wholesale if your server has its own
appearance system:
Config.Services.dressPed = function(ped, model)
exports.illenium_appearance:setPedAppearance(ped, myCopOutfit)
end
s_m_y_cop_01 is the ready-made alternative if you would rather not dress them
at all. EMS and the coroner keep their own dedicated models.
They always spawn on usable road. The nearest road node is not automatically somewhere a car can sit — it can be under a bridge, inside scenery, or already occupied by traffic, and a unit spawned there is stuck before it has moved. Candidate nodes are checked for being genuinely on road and clear of vehicles, walking out through the nearest eight before giving up, and the vehicle is seated on the surface once created.
If a unit does not turn up, it will now tell you why rather than sitting there — a model that would not load, a driver that could not be created, or no route to the scene. A unit whose driver fails takes its vehicle with it, so you never get an abandoned van. AI drivers occasionally give up on a route and stop; the drive task is re-issued if it stalls for four seconds.
Crew creation retries three times, falls back to spawning them beside the vehicle and warping them in, and finally verifies somebody is actually in the driver seat. A car with a driver that is not in the seat is exactly what an empty unit that never moves looks like, so that case now fails loudly instead.
Vehicle recovery
Target the stopped car and pick Call a flatbed, or /stops tow.
A truck spawns on a road about 140m out and drives in under its own power — it is not teleported to you. When it arrives it pulls up, spends a few seconds loading, puts the car on the bed and leaves. The whole thing is called in and acknowledged over the radio.
The truck, its driver and the recovered car are tracked and cleaned up on any exit path, including the truck failing to reach you and the resource being stopped mid-recovery.
AddEventHandler('teneight:vehicleTowed', function(data)
-- data.plate, data.stopId
end)
Everything is in Config.Tow: models, spawn distance, drive speed, how long
loading takes, and bedOffset if the car sits wrong on your flatbed.
Radio and audio
Every beat of a stop goes out over the radio: a squelch, the line, a squelch. Transmissions queue rather than overlap.
1-ADAM-12 Control, 1-ADAM-12, traffic stop. Plate 46EEK572, Vinewood Blvd.
DISPATCH 1-ADAM-12, copy your stop at Vinewood Blvd. Show you out.
DISPATCH 1-ADAM-12, be advised, 46EEK572 is flagged. No insurance on file.
DISPATCH 1-ADAM-12, Marcus Deering is showing an active warrant. Unpaid citations.
1-ADAM-12 Control, 1-ADAM-12, one in custody at Vinewood Blvd.
Street names are read from where you actually are, and your own transmissions
key the shoulder mic — an upper-body clip, so you can keep walking or driving
while you call it in. Dispatch talking back does not move your arm.
Config.Audio.radioEmote = false turns that off.
It works with no audio files. Every sound defaults to a built-in GTA one - the squelch pair is the game's own CB radio keying, which is what gives the traffic its shape.
To use your own: drop files in web/sounds/ and name them in
config/audio.lua:
radio_up = { file = 'radio_up.ogg' },
file overrides the native fallback on the same entry, so you can replace
sounds one at a time without deleting anything.
To reword the radio, edit Config.Radio.lines. Tokens available:
{unit} {plate} {street} {name} {reason} {count}. Set your callsign
with Config.Radio.unit.
If you record voice lines later, add sound = 'key' to a line and it plays over
the text.
Offenses
config/offenses.lua is a flat catalog — id, label, fine, penalty points,
category. Add or reprice freely:
{ id = 'speed_minor', label = 'Speeding, 1-15 over', fine = 150, points = 2, category = 'Moving' },
Ids sent from a client are validated against this catalog before anything is recorded, so a crafted request cannot invent a charge or a fine.
Searches pre-tick the offenses they justify — find a pistol, and Unlawful carry of a firearm is already selected when you open the citation dialog.
Picking the offenses is not the end of it: you then write the thing for
Config.Tickets.writeTime. Cancel partway and nothing is issued.
By default that plays your own /e notepad emote — Config.Tickets.emote.
Handing off to your emote resource is the reliable way to match an emote
exactly: copying its dictionary, clip and prop offsets means guessing at values
that differ between builds and forks, whereas asking the resource to play it
cannot be wrong. emoteCommand and emoteCancel are the rpemotes and dpemotes
defaults if yours uses different ones.
Set emote = false to use the built-in animation and props instead, which
needs no emote resource at all. A stop that ends mid-citation takes the pad out
of your hand either way.
Contraband
config/identities.lua holds the item tables. on decides where an item can
hide:
{ id = 'shotgun', label = 'Sawn-off shotgun', type = Stops.Contraband.WEAPON,
on = 'vehicle', weight = 5, offense = 'weapon' },
both rolls a side per stop. Clean searches still return innocuous items so a
search reads as a search rather than a null result.
Searching a car
A car is searched a part at a time, not in one go. Search the vehicle on the driver lists the trunk, the hood and every door the car actually has; third eyeing a part goes straight to that one, because each option is pinned to its own bone.
For each part the door is opened, the officer crouches into it, works through
it for partDuration, and the door is shut again. You have to be stood at the
part — partDistance — and the crouch faces the car rather than playing side-on
to it.
Anything in a car is in one particular part of it, decided when the stop opens. Searching the trunk finds what is in the trunk; it does not find the bag under the back seat. That is the whole point of searching a car a part at a time, and it means a lazy search misses things.
Which parts a car has is decided by bone rather than by door index: a coupe will answer a rear-door index perfectly happily and then open nothing, so the menu would carry two entries that do nothing. A car with no recognisable parts at all falls back to the old single whole-car search.
Backup does the same job the same way — Have backup search the vehicle walks him round it, part by part, opening and crouching at each one, and he reports once at the end rather than a dialog per door.
| Setting | Does |
|---|---|
parts |
The parts, their door index, bone, standing offset and crouch |
partDuration |
How long one part takes |
partDistance |
How close you must be to work on one |
trailerDuration / trailerDistance |
The back of a truck, below |
Searching a trailer
A trailer is searched as one thing rather than a part at a time. It has no doors the parts list would recognize and nothing useful to divide it into: you open the back and look at the load. Third eye the trailer itself for Search the trailer — the reach is generous, because a trailer is thirteen meters long and "stand at the trailer" should not mean one particular corner of it.
What comes back is freight: pallets, straps, roll cages, drums of cooking oil. All of it legitimate, which is the point — a trailer is full of things and almost none of them are yours to care about. Three of the interdiction compartments live back there, so it is also where a load turns up on a truck.
The options only ever appear when the stopped vehicle is actually pulling something.
Why it does not fight the car
Three things the mirror deliberately does not do, because each of them was a visible bug:
It does not write a speed onto the car every frame. Inside
speedDeadband — a quarter of a metre per second — the car is left alone.
Writing a velocity sixty times a second never lets the suspension settle: the
springs spend every frame answering a body velocity they did not produce, which
shows up as the nose pushed down, the tail lifted, and wheels that slide instead
of rolling. Between corrections the car is simply a car.
It does not throw away the vertical component on a slope.
SetVehicleForwardSpeed drives along the vehicle's own forward vector, which
already follows whatever the car is sat on — so on a hill the vertical part is
correct and free. The only time it is carried across by hand is when the car is
genuinely off the ground, or the fall gets cancelled mid-air.
It does not use SetEntityHeading. That writes an orientation with no pitch
or roll in it — on level ground the car had none anyway, but on a hill it is a
car suddenly sitting level on a slope, slammed back onto its springs every
frame. Steering sets the yaw and keeps the pitch and roll the car already has.
Stopping something longer than a car
The mirror numbers are a car's numbers. A tractor unit with a box trailer is about nineteen meters end to end, so a mark twelve meters off your hood puts the tractor where you asked for it and the trailer straight through your cruiser — and a heading blend a car answers smoothly folds a semi at the fifth wheel.
Anything at or over Config.Pullover.rig.longAt meters gets its own set, worked
out from what the vehicle measures rather than from a list of model names, so
an addon truck nobody told the resource about is handled the same as a phantom:
| Setting | Does |
|---|---|
longAt |
Length, in meters, at which a vehicle stops being treated as a car |
clearance |
Added to its own length for the mark it holds station on |
steering / turnRate |
Gentler hands. The turn rate is the one that decides whether a rig follows or jack-knifes |
smoothing |
How quickly it answers your speed |
decel |
Nearer 1.0 is a longer roll to a stop |
Settling stops the trailer too. Stopping only the tractor leaves the trailer with all its momentum and a joint to spend it through, which folds the rig across both lanes a beat after it looked like it had parked.
Highway interdiction
Most stops are somebody with a brake light out and a joint in the ashtray. A few are a courier moving weight down the interstate, and those do not look any different from the window.
This is the teneight_interdiction addon, not the core: install it beside
teneight and it attaches itself. Its settings are in that resource's
config/interdiction.lua. enabled = false there, or not starting the addon at
all, turns the whole thing off and stops behave exactly as they did before.
Walking the car
Third eye the car itself for Walk the vehicle. That puts four more options on it, one on each of the hood, both front doors and the trunk, and you work them in whatever order you like.
Third eye one and the officer crouches and looks over that part of the car. If there is something there he says it, then takes the board out and writes it down — clipboard in the off hand, pencil in the writing hand, the same pose the citations are written in. Then you move to the next one.
What he finds is a mix. Some of it means something:
The rear sits low on its springs, and the car is empty. Fresh undercoating along the rear arch. The rest of the underside is twenty years old.
Some of it is a car:
The trunk is full of tools and the rear sits low because of it. Curbed wheels, a cracked light lens, and a dent somebody has lived with.
A clean car has things to say too, which is the point — if only dirty cars had observations, the presence of any observation at all would be the tell and nobody would read them. Walking round a car and deciding it is clean is the correct outcome most of the time.
Observations that point at a panel come up more than twice as often when the load is actually behind that panel, so the walk is worth doing rather than decoration.
Each point has its own pose, because looking under a hood and looking along a rocker panel are not the same shape: head into the engine bay at the front, down on one knee at the rocker panels and the trunk, stood up with the flashlight at the trailer.
The flashlight is out for all of it. Half of interdiction is looking into places
that have no light in them, and an officer doing that empty-handed in daylight
is what gives away that none of it is real. It draws an actual beam, not a prop
glow — Config.Interdiction.flashlight sets the color, throw and how far below the
eyeline it points, and light = false keeps the prop and drops the beam.
When the last point is done the board comes up on its own: a sheet of paper on a clipboard with the whole walk written on it in biro, not the resource's usual dark card. Finish the walk-around stops early and hands you whatever you had by then, and the sheet says it was not completed.
One walk per stop, by anybody on it. A second officer asking gets the same board rather than a second walk.
A car missing one of the four bones — no trunk on a van, for instance — simply has fewer points, and anything that would have been noticed there moves to a point that exists rather than becoming unreachable.
The compartment
Searching the right panel does not hand you the load. It hands you this:
The carpet in the wheel arch is held by four screws and two of them are new. Behind it the panel sounds solid where it should sound hollow.
That puts Take the quarter panel apart on that door and no other. It takes
twenty-four seconds, you have to stay at the panel, and the officer uses the
right tool for it — a screwdriver on trim screws, a wrench on a bolted tray, a
bar on a riveted bulkhead — in the pose that suits it, with the progress bar
naming what is actually being done. Each compartment says which tool it needs in
Config.Interdiction.Traps, and Config.Interdiction.Tools says what that tool
is. A tool can require an ox_inventory item to be carried; none do by default.
What comes out of it is weight — kilo bricks, bales, bundled cash, pistols in grease paper — which banks through the same evidence and charging path as anything else you find. It charges as trafficking rather than possession.
Everybody on the stop is told which panel came back wrong, so an assisting officer can take it apart too.
Intelligence
One in seven is a grind. What turns a grind into a shift worth working is knowing something.
A tip names a corridor, a window of ten to twenty minutes, and one to three
things about a vehicle. It arrives over the radio from dispatch and goes on the
board; /stops intel reads the board back on the same clipboard the walk-around
is written on, because it is the same thing — a sheet somebody handed you.
INT-1004 · Graded B2 · 14 min left A source at the freight yard, within the hour. Carrying: a semi or a box truck, pulling a trailer. Corridor: SANDY.
A tip is about one real vehicle. It names the make and the full plate,
and marks a stretch of highway on the map of everybody who gets it. A BOLO for
the plate goes on the terminal at the same time, and comes down when the tip
closes. Nothing is on the road yet: the first officer into the marked area
starts a clock, and somewhere between Run.delay.min and Run.delay.max
seconds later the vehicle comes up the road from out of sight and drives
through. Stop that plate anywhere - in the area or ten miles down the road -
and it answers the tip. Add your own stretches with /tippoint, which prints a
line to paste into Config.Interdiction.Run.points.
Config.Interdiction.Run.enabled = false brings back the older kind of tip
described below, where nothing is spawned and the first matching stop answers.
Everything a description tip names can be checked from behind the vehicle without stopping anybody — what sort of vehicle it is, whether it is pulling anything, and the first three characters of the plate. That is the whole test for what is allowed in a tip. A tip you can only act on by pulling over every car on the highway is not a tip, it is a lottery with extra steps.
How much you get told depends on the source:
| Grade | Tells you | Chance a matching stop is the load |
|---|---|---|
| A1 — reliable, corroborated | three things | 80% |
| B2 — reliable, uncorroborated | two things | 55% |
| C3 — untested source | one thing | 32% |
A stop that answers a live tip is played on the tip's odds rather than the corridor's, and the load is a consignment — a little over twice to three and a half times an ordinary run. That is the difference between a bust and the one the shift was for. The tip closes either way: stop the right vehicle and find nothing, and you are not told to go and stop it again.
Where tips come from
Three places. A timer, every twenty to forty minutes, and only while somebody is on duty to act on one. The terminal, when a plate keeps turning up. And best of all — the last courier you took.
Once a compartment is open and the driver is in cuffs, third eye him for Ask who he is running for. Whether he talks is the sort of man he is: a compliant driver gives it up better than half the time, a hostile one almost never. What he gives up is a tip like any other, and a good one — he knows the run because he was on it.
That is the loop the whole feature is built around: a bust feeds the next bust.
| Setting | Does |
|---|---|
interval / window |
How often one comes in, and how long it is good for |
maxLive |
How many at once. More than a few and the sheet stops being a briefing |
grades |
How much each grade of source tells you, and how often it is right |
consignment |
What a tipped load is worth, as a multiplier |
flip.chance |
How likely each disposition is to give up the next run |
sources |
Who is talking. Prose, rewrite it for your server |
Seeing it without waiting for it
One in seven on a corridor is the right number to play against and the wrong number to test against — you cannot check a chain of three mechanics by running traffic stops until one happens to be dirty.
/stops interdiction
arms your next stop as a courier run: it overrides the odds and the disposition, belongs to you and nobody else, and is spent the moment it is used. Run it again to turn it off. A player who is not allowed to run stops cannot get anything out of it, because opening one is already refused for them.
Tuning
| Setting | Does |
|---|---|
corridors |
Zone codes where a courier run can roll. Zone codes, the same ones Config.Counties uses |
chance.corridor / chance.elsewhere |
How often. 0.14 and 0.02 — low on purpose |
dispositions |
Who can be carrying. compliant is in there deliberately: a courier is calm, and without it the feature collapses into "search the nervous ones" |
walk.distance |
How close to the car you must be to start |
walk.inspect / walk.note |
How long looking at one point takes, and how long writing one line takes |
probe.duration |
How long the panel takes. Long is most of what makes finding it feel like something |
probe.requiresItem |
An inventory item the officer must be carrying. false for none |
Indicators |
What the car says. courier entries only appear on a carrying car, noise on any |
Traps |
The compartments, their zone, and the prose for each |
Loads |
What is inside. Same shape as Config.ContrabandItems |
A trap whose zone no part in Config.Search.parts covers can never be
reached. Rather than leave an unfindable load in the car, the stop reverts to a
clean one and says so in the console.
Getting trucks onto the road
Interdiction needs commercial traffic, and the game does not really supply it.
phantom, hauler and packer are in the ambient pool but weighted low and
gated per zone by popcycle.dat — and the part that actually matters, ambient
traffic hardly ever puts a trailer on one. A bare tractor unit is not what
anybody pictures when they think of working a highway.
The density natives are no use for it: SetVehicleDensityMultiplierThisFrame
and friends scale the whole pool, so you get more cars in the same mix. The only
two real options are editing popgroups.ymt and popcycle.dat — server-wide
and fiddly — or putting them on the road from script. Config.Interdiction.Traffic
is the second.
It tops up rather than spawns. Every seven seconds it counts the trucks
already near you — whoever put them there, the game included — and adds one only
if there are fewer than target. So it does not fight ambient traffic, and two
officers working the same stretch do not end up with six each: the second counts
the first's.
| Setting | Does |
|---|---|
enabled |
Independent of the rest of interdiction. More trucks and no compartments is a valid combination, and so is the reverse |
corridorsOnly |
Only on the corridors above. Semis in Vinewood would be worse than none |
target / radius |
How many to keep within how far |
spawnMin / spawnMax |
How far ahead one comes in. Out of sight, on your road |
despawn |
How far away one has to get before it is taken back |
pauseOnStop |
Stops seeding while you are on a stop, so traffic does not pile onto your scene |
tractors / boxTrucks / trailers |
What goes out |
semiChance / trailerChance |
How often it is a semi, and how often that semi has anything on the back |
drivers |
Ped models. First that exists is used |
speed |
m/s. 24 is about 54mph |
Everything it puts out it takes back — a seeded truck is a mission entity so the game will not delete it out from under a stop, which means the game will never tidy it up either and the resource has to. A truck that is the subject of a stop is never removed however far away it gets, and a trailer goes with whatever is pulling it rather than on its own distance.
What the client is told, and when
Nothing about a compartment crosses to a client before it has been earned. The walk-around returns observations only once it is asked for; the tell only from the exact panel the load is behind; the load only after the panel is off. Asking to open a panel you have not been told about is refused, and refused with the same silence as asking about a clean car — so a client cannot find the compartment by asking.
Integrating
Nothing here moves money. Both records fire an event carrying everything you need:
AddEventHandler('teneight:ticketIssued', function(citation)
-- citation.plate, .name, .offenses, .total, .points, .officer
end)
AddEventHandler('teneight:arrestBooked', function(arrest)
-- arrest.plate, .name, .charges, .fines, .officer
end)
AddEventHandler('teneight:stopFinished', function(data)
-- data.disposition, .fled, .resisted, .duration
end)
Server exports
exports.teneight:GetIdentity(plate)
exports.teneight:AddPoints(plate, points)
exports.teneight:ClearWarrant(plate)
exports.teneight:FlagVehicle(plate, 'Wanted in connection with a robbery')
exports.teneight:GetOfficerStop(source)
exports.teneight:GetCitations()
exports.teneight:GetArrests()
Client exports
exports.teneight:IsOnStop()
exports.teneight:GetStop()
exports.teneight:OpenMenu()
exports.teneight:EndStop('message')
With teneight_callouts
Set Config.RequireDuty = true to require police duty from teneight_callouts.
FlagVehicle is the interesting one — a callout can plant a wanted plate, and
the officer discovers it organically by running it on a stop:
exports.teneight:FlagVehicle('46EEK572', 'Suspect vehicle, armed robbery')
Props
Hand offsets are in meters from the bone, so they are small numbers — anything
much over 0.1 puts a prop visibly in front of the hand rather than in it,
which is what floating looks like.
They cannot be judged without standing in front of them, so there is a live tuner rather than a config-edit-and-restart loop:
/stops prop list the props
/stops prop documents put it in your hand as configured
/stops prop documents 0.06 0.02 0 -95 40 0 try these numbers instead
/stops propoff put it down
Every call re-attaches immediately and prints the finished config line to your
F8 console, ready to paste over the entry in config/animations.lua.
Animations
Every clip is in config/animations.lua, in one table. Clip names are the
fiddliest thing in FiveM to get right and the only way to judge one is to stand
in front of it, so when something reads wrong you change one line rather than
hunting through five files.
The subject is turned away from you and the officer steps in behind before a pat down or a cuffing, because that is both how it is done and what the animations assume.
Escorting: G to take hold, G again to let go. Also on the ped once they are cuffed.
escortMode = 'attach' is the default: they are pinned to your hand and a
cuffed walk clip is driven on them, which is how police scripts normally do it.
Two details make or break this, and both are easy to get wrong:
- The anim dict is
anim@move_m@prisoner_cuffed. Without theanim@prefix you get the movement clipset, which will not load as an anim dict — the clip silently never plays and the detainee slides along with dead legs. - On the attach call,
isPedmust befalsedespite the name, andfixedRotmust betrue. SetfixedRotfalse and they rotate with the hand bone, which lays them out flat in mid-air.
The clip is re-asserted a few times a second, and the stride speed is matched to your pace so they do not moonwalk. Releasing stops the clip explicitly rather than only clearing tasks.
Two alternatives if you want them:
escortMode |
Behavior |
|---|---|
drag |
They walk under their own power, re-aimed at your shoulder several times a second |
follow |
A plain follow task; animates fine but paths, so it trails on corners |
attach degrades to drag on its own if the walk clip cannot be loaded, and
says so in the console.
Letting go. While you are holding somebody an on-screen prompt shows the escort key, and beside a car it offers both that and putting them in it.
That prompt is not just convenience: an attached detainee is carried with
collision off, and a target ray passes straight through an entity that has no
collision — so you cannot look at them to release them. The key always works.
Config.Custody.escortCollision = true makes them targetable while held, at the
risk of the pair of you shoving each other around doorways.
If a detainee ever floats horizontally, there are only two causes and both are handled here:
- Attached to a limb bone. An attached entity inherits the bone's
orientation, and the hand bone points off at an angle, which lays them out
flat.
Config.Custody.escortBone = 0uses the body root, which shares your own orientation. Do not change it to a hand bone. - No animation playing. An attached ped with no clip holds whatever pose the skeleton was last left in, which reads as a plank. The cuffed idle is re-asserted every second for as long as you are holding them.
escortOffset is where they stand relative to you (right, forward, up), and
escortRotation is there if they still sit crooked on your model.
If their legs float while you walk, the cause is the animation flag. An
attached ped is not walking under its own power, so nothing animates it unless
you drive the clip by hand — and flag 49 is LOOP + UPPERBODY + SECONDARY,
which leaves the legs out of the clip entirely. Three separate clips handle
this:
| Clip | Flag | When |
|---|---|---|
subject_cuffed |
49 | Sitting in a vehicle — upper body only, so the cuffed arms lay over the game's own sitting pose |
subject_cuffed_stand |
1 | Standing, full body |
subject_cuffed_walk |
1 | Being walked, full body, playback rate matched to your pace |
The swap between standing and walking is automatic, and the stride speeds up when you jog so they do not moonwalk.
Config.Custody.escortMode = 'follow' swaps to them walking behind you under
their own steam instead, if you prefer that.
Tuning
config/config.lua:
Config.Target— how far and how far off-axis a vehicle can be and still be signaled.requireSirenforces lights on first.Config.Pullover— reaction time, yield distance and speed, timeout, whether hazards come on and the engine cuts.Config.Dispositions— the odds above.Config.Search— durations, consent odds, contraband odds per disposition.Config.Custody— cuff time, escort distance, resist odds.
Set Config.LogLevel = 'debug' to see dispositions, contraband rolls, and state
transitions in the server console.
Known limits
- Drives AI vehicles only. Real players are skipped by the target scan; player-on-player stops are a different problem and want their own consent flow.
- The runner path hands off to normal GTA flee behavior. There is no pursuit
system here —
teneight:stopFinishedfires withfled = true, which is the hook to build one on. - Records survive a restart only with the database on — see Records that
survive a restart. With
Config.Database.enabled = falsethey last until the server restarts.
Evidence items
Evidence items
What a search turns up can be bagged and carried off as a real inventory item. This folder is the part your inventory needs. Two steps, once.
1. The items
Open ox_inventory_items.lua. Everything inside the return { ... } goes into
the table in ox_inventory/data/items.lua.
52 items, all prefixed ev_ so nothing collides with what your server already
has. If you change Config.Evidence.prefix, change these names to match — an
item the inventory has never heard of is silently not given, and the officer
gets told the seizure was refused.
2. The pictures
Copy everything in inventory-images/ into ox_inventory/web/images/.
One PNG per item, 128×128 with a transparent background, named to match the item. They are drawn for this resource and are not lifted from anywhere, so they ship with it and you are free to edit or replace them.
Then
Restart both resources. Search a car, find something, and the stop menu gains Bag the evidence — or the third eye on the car does, if you use ox_target.
The item carries what it was, which vehicle it came out of, who seized it and when, in its tooltip. The plate rather than the driver's name, deliberately: the name may never have been run on that stop, and an item tooltip is a poor place to hand somebody an identity they did not earn.
Running something other than ox_inventory
Set Config.Evidence.inventory = 'event'. The server then fires
AddEventHandler('teneight:giveEvidence', function(src, name, count, meta)
-- name 'ev_cannabis'
-- count 1
-- meta description, evidence, seizedFor, plate, officer, stop, at
end)
for each item, and everything else — the bar, the charges, the paperwork, the card listing what went into the bag — works as it does with ox_inventory.
Config.Evidence.enabled = false turns the whole thing off. Searches still
list what they found and still suggest the charges; nothing enters anybody's
pockets.
The catalog
Config.ContrabandItems in config/identities.lua is the one source of truth:
what can be found, how likely it is, which part of a car it can be hidden in,
which charge it suggests, and which item it becomes.
Add your own freely — nothing in the resource refers to a find by name. An
entry with no item is found and charged as normal and simply never bagged,
which is also the honest way to add a find you have no artwork for yet.