FIELD MANUAL // Police tools

10-8

Documentation from the supplied script archive. Check your included resource files for the version you install.

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:

  1. They have not seen you yet. 1-4 seconds of nothing. They keep driving.
  2. They clock you. Brake lights tap, they come off the throttle.
  3. 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.
  4. They commit and crawl to the curb.
  5. 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.

  1. Ask for license and registration. They dig it out; you take it. You are now physically carrying it, prop and all.
  2. Walk back to your cruiser. Nothing has come back yet.
  3. 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.
  4. 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 the anim@ 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, isPed must be false despite the name, and fixedRot must be true. Set fixedRot false 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:

  1. 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 = 0 uses the body root, which shares your own orientation. Do not change it to a hand bone.
  2. 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. requireSiren forces 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:stopFinished fires with fled = 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 = false they 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.