Robberies & police
[FREE] Dynamic NPC Robberies & Police Chases | MINIGAME | Legacy & Enhanced comaptible
Original Lua/JavaScript resource developed independently by WebMerc. Requires FiveM (GTA V). No Rockstar game files are included.
Independent third-party developer. Not affiliated with, endorsed, sponsored or approved by Rockstar Games, Take-Two Interactive, Cfx.re or FiveM. GTA, Grand Theft Auto and FiveM are trademarks of their respective owners.
Read it before you buy
These are the same files that come in the download, updated with every release.
lastresort_wheelman
A man in a mask, a bag in his hand, and no ride. That is your cue.
lastresort_wheelman is a getaway-driver street contract for FiveM. It is not a job menu and it is
not a menu-driven minigame: it is an ambient world event that places itself, runs on its own clock,
and is validated end to end on the server.
Author: LastResort Development — intelnpc.com Version: 1.0.3
Open source. No escrow, no license check, no subscription, no obfuscation. You get every line and you can change every line.
What it is, and what it is not
A robber is standing at a street corner with a bag. He needs a car. You pull up, you hold your horn, he gets in, and the police are two seconds behind you. Lose them, and he tells you where to drop him. Get shot at along the way and he might not survive the trip — in which case the bag is still on the pavement, and it spends just as well.
It is an ambient world event: it places itself, it expires on its own, any player can take it, and only one player can take each one.
It is not a heist script, a robbery script or a police script. It does not rob stores, it does not touch your dispatch's own alerts beyond firing one, it does not manage jail, and it never assumes your server has police online. It creates one ped and, on a bad day, one bag.
Gameplay flow
director places a contract -> robber spawns, blip + 25 m area blip + alarm
player drives within 100 m -> help text: honk, or the reason you cannot
player holds the horn -> server reserves the contract for exactly one driver
robber gets the enter task -> he runs to the car and takes a passenger seat
2 s later -> two stars, doors lock, dispatch alert goes out
pursuit -> heat 0-100, fed by player police, NPC police and proximity
heat reaches zero -> wanted cleared, drop-off revealed and routed
arrive and stop -> he gets out and walks the last 1.5 m
server validates -> payout, XP, cleanup
The other ending:
robber is killed -> bag drops where he fell, blipped
walk up and press E -> grab animation, one wanted star
get the heat back to zero -> server validates, smaller payout, cleanup
The robber
He carries the bag from the moment the contract is placed — it is a real prop on his hand, with a carry walk to match, and it is the same model that drops if he is killed. The bag rides in the car with him and leaves with him at the drop.
He also talks. 169 lines across 32 situations, in English and Hungarian, spoken as a 3D speech
bubble over his head. With lastresort_chat running they are real chat bubbles; without it the
resource draws its own. He reacts to what is happening rather than to the state machine alone —
being told to hurry when the police are close reads differently from a quiet stretch, a driver who
has stopped dead gets told about it, and so does one who just wrote the car off. Every line lives in
locales/, so any of them can be replaced without touching Lua. Config.Bark.Enabled = false
silences him.
Whether he fights back depends on who he is. Each of the seven robber profiles maps to a combat style:
| Style | Who | Behaviour |
|---|---|---|
quiet |
the corner kid, the downtown runner, the man in the suit | Never draws in the car |
nervous |
the Balla, the Lost MC rider | Fires in bursts, poor aim, short range |
shooter |
the hired gun | Aims properly, holds fire past 65 m |
veteran |
the last one | Full auto, 85 m, and he does not miss much |
Only a passenger fires — the driver's seat is checked, so a robber who somehow ends up driving never shoots. He stands down the moment the wanted level clears, and again before he gets out at the drop.
Difficulty tiers
Contracts are graded, and the grade is what the robber, the heat and the payout are built from. Higher reputation unlocks the harder tiers rather than simply paying more for the same job.
| Tier | Unlocks at | Robber | Heat | Payout | XP |
|---|---|---|---|---|---|
| Street job | Rookie | 160 hp, pistol, poor aim | 55 | ×0.85 | ×0.8 |
| Armed robbery | Rookie | 200 hp, pistol | 70 | ×1.00 | ×1.0 |
| Professional crew | Wheelman | 260 hp, 50 armour, SMG | 85 | ×1.35 | ×1.3 |
| No-questions contract | Ghost | 320 hp, 100 armour, rifle | 100 | ×1.75 | ×1.6 |
Seven robber profiles back these — a corner kid, a downtown runner, a Balla, a Lost MC rider, a man in a suit, a hired gun and something worse — and each tier draws from its own subset, so the person you pick up looks like the job you took. The director grades against the highest-ranked player online, so a server of rookies never sees the top tier.
The gate is enforced where it matters, not only where it is displayed: the server checks the
claiming driver's own rank against the tier's requiresRank inside the claim, so a rookie who walks
up to a ghost contract placed for someone else is refused with a message rather than paid for it.
Turn the whole thing off with Config.Difficulty.Enabled = false and every contract runs at
Config.Difficulty.Default.
Getaway crew
Off by default. With Config.Crew.Enabled = true, a player standing near a driver who is already on
a job can run /wheelmancrew to ride along. The server picks the nearest joinable contract itself —
the client never names one — and refuses if the crew is full, the player is already working, the
player is already riding with another crew, the contract is in a different routing bucket, or the
contract has moved past Config.Crew.JoinBefore. Riding along counts as working: a crew member
cannot claim a contract of his own while he holds a share of someone else's.
Crew members get Config.Crew.ShareFraction of the driver's payout each. The driver's own cut is not
reduced, so the share is minted on top — which is why Config.Crew.TotalShareCap bounds what the
whole crew can draw between them, defaulting to one driver payout however many join. They optionally
take a scaled share of the XP and are dropped from the crew on disconnect.
Outcomes
| Outcome | Meaning | Pays |
|---|---|---|
DELIVERED |
The robber reached the drop-off alive | Full |
PACKAGE_STOLEN |
He died, you took the bag and got clear | Reduced |
ROBBER_KILLED |
He died and the bag was never collected | No |
BUSTED |
You were arrested | No |
PLAYER_DEAD |
You died | No |
ABANDONED |
You left him more than 250 m behind | No |
VEHICLE_LOST |
Your car was destroyed or you stopped driving it | No |
TIMEOUT |
The pursuit or the contract ran out of time | No |
ENTITY_LOST |
The robber or the bag went missing | No |
EXPIRED |
Nobody took the contract before it aged out | No |
CANCELLED |
An admin or another resource cancelled it | No |
RELEASED |
Internal: you were released before the pickup, the contract is offered again | No |
Install
Full instructions are in INSTALLATION.md. The short version:
set onesync on
ensure oxmysql # optional
ensure es_extended # or qb-core / qbx_core, or neither
ensure lastresort_notify # optional
ensure lastresort_chat # optional
ensure lastresort_wheelman
OneSync is required. Contract entities are created on the server, which is what makes a contract impossible to duplicate from a client.
Dependencies
Nothing is mandatory.
| Resource | If present | If absent |
|---|---|---|
es_extended / qb-core / qbx_core |
Identity, job, wallet | Standalone mode, license identifier, ConfigServer.Rewards.CustomPayout |
oxmysql |
SQL persistence | JSON file persistence |
lastresort_notify |
Rich notifications, the objective tracker, the wanted flash, money and rank cards, HUD gauges and the dispatch surface | ox_lib → framework → native feed for toasts; hand-drawn bars for the gauges; nothing lost |
lastresort_chat |
Admin replies, the 911 dispatch card with a map pin, optional contract chatter | chat:addMessage, which the default FiveM chat renders |
ox_inventory |
ConfigServer.Rewards.Item payouts |
Framework inventory, then skipped |
ps-dispatch / cd_dispatch / qs-dispatch / core_dispatch / linden_outlawalert |
Police alert | Plain notification to on-duty police |
Sibling resources
Both of your own resources are used natively when they are running, and every one of these degrades to something that still works when they are not.
lastresort_notify
| What | Export used | Without it |
|---|---|---|
| The contract objective list, stepped as the state machine advances | Objectives / ObjectiveStep / ObjectivesComplete |
The existing notifications carry the same beats |
| Robber health and heat as real HUD gauges | RegisterHudWidget / SetHudStats / UnregisterHudWidget |
The hand-drawn bars in client/hud.lua |
| The wanted flash when the chase starts | Wanted |
Nothing; the stars speak for themselves |
| The payout card | Money |
A success notification with the figure |
| The rank-up card | Rank |
A success notification |
| The drop-off name when it is revealed | Location |
An inform notification |
| Police dispatch | DispatchAlert |
The next dispatch provider in the chain |
| Toasts | Notify |
ox_lib, then the framework, then the native feed |
Gauges and rich surfaces are separately switchable, so you can keep the HUD widgets and drop the objective tracker or vice versa:
Config.Integrations = {
Notify = 'auto', -- 'auto' | 'off'
RichSurfaces = true, -- objectives, wanted, money, rank, location
HudWidgets = true, -- the two HUD gauges
HealthWidget = { id = 'wheelmanRobber', icon = 'user', colour = '#FF7B54', warn = 25 },
HeatWidget = { id = 'wheelmanHeat', icon = 'siren', colour = '#FF453A', warn = 60 },
}
lastresort_chat
| What | Export used | Without it |
|---|---|---|
| Admin command replies | Send to ConfigServer.Chat.Channel |
chat:addMessage to that player |
| The police alert as a dispatch card with a map pin | SendCard to the 911 channel |
The dispatch provider chain, then a plain notification |
| Optional "a job is going down in X" chatter | Send |
chat:addMessage |
ConfigServer.Chat = {
Enabled = true, -- false silences every chat path, including the fallback
Handler = 'auto', -- 'auto' | 'off' — 'off' forces the chat:addMessage fallback
Channel = 'system',
PoliceChannel = '911',
Prefix = 'Wheelman',
Colour = { 255, 123, 84 },
CardIcon = 'siren',
CardAccent = '#FF453A',
CardExpiry = 300,
AnnounceContracts = false, -- chatter when a contract is placed; off by default
}
The card is the one thing with no equivalent: without lastresort_chat the police still get the
alert, just as a dispatch notification rather than a card with a pin. Everything else has a real
fallback, and Enabled = false turns the whole chat surface off without touching anything else.
Framework
bridge/client.lua and bridge/server.lua are the only files that know a framework exists. Gameplay
code calls LRW.Bridge.* and nothing else.
Config.Framework = 'auto' -- 'auto' | 'standalone' | 'esx' | 'qb' | 'qbox'
Server bridge surface: getIdentifier, getName, getJob, isPolice, isStaff, pay,
giveItem, sqlAvailable.
Standalone servers have no wallet, so supply one:
ConfigServer.Rewards.CustomPayout = function(src, amount, account, reason)
exports.my_banking:AddMoney(src, amount)
return true
end
Return true and the payout is considered done. Return anything else and it is logged and skipped —
never silently doubled.
Database
ConfigServer.Persistence.Mode is auto by default: oxmysql when it is started, a JSON file
otherwise. The table is created at runtime with CREATE TABLE IF NOT EXISTS; sql/lastresort_wheelman.sql is
provided for anyone who prefers to import it by hand.
wheelman_drivers (identifier, name, xp, completed, failed, stolen_packages, earnings, updated_at)
One row per character. Nothing else is persisted — contracts are in-memory and deliberately do not survive a restart.
Reputation
Five ranks, thresholds in shared/reputation.lua:
| Rank | XP | Payout multiplier |
|---|---|---|
| Rookie | 0 | 1.00 |
| Driver | 400 | 1.08 |
| Wheelman | 1200 | 1.16 |
| Professional | 2800 | 1.24 |
| Ghost | 6000 | 1.32 |
The multiplier is 1 + (rankIndex - 1) * Config.Reputation.PayoutStep and is applied on the
server, after the reward is calculated, never from anything the client sent.
Dispatch
Detected automatically from the started resources. Every event name and export signature below was taken from the resource it belongs to — none are invented.
| Provider | Call |
|---|---|
| ps-dispatch | exports['ps-dispatch']:CustomAlert(payload) |
| cd_dispatch | TriggerEvent('cd_dispatch:AddNotification', payload) |
| qs-dispatch | exports['qs-dispatch']:VehicleTheft(payload) |
| core_dispatch | TriggerEvent('core_dispatch:addCall', ...) |
| linden_outlawalert | TriggerEvent('wf-alerts:svNotify', payload) |
| none | TriggerClientEvent notification to each on-duty police source |
Force one with ConfigServer.Dispatch.Provider. Alerts are rate-limited per contract by
ConfigServer.Dispatch.RepeatInterval.
Configuration reference
config/config.lua — shared, client-visible
| Block | Key | Default | Meaning |
|---|---|---|---|
Locale |
'auto' |
Locale pack; locales/*.lua is globbed. 'auto' picks it — lr:locale convar, then locale / ox:locale / esx:locale / txAdmin-locale, then the player's game language on the client, then en (see Localisation) |
|
Debug |
false |
Enables LRW.log, /wheelmandebug, /wheelmanwaypoint |
|
Framework |
'auto' |
Framework override | |
Director |
Enabled |
true |
Master switch for automatic placement |
MaxConcurrent |
2 |
Live contracts at once. One per player is fixed, not tunable | |
SpawnAttemptInterval |
45000 |
How often a placement is rolled | |
SpawnChance |
0.35 |
Probability per roll | |
MinPlayers |
1 |
Players online before anything is placed | |
LocationCooldown |
1800000 |
Per-location cooldown after use | |
PlayerCooldown |
600000 |
Per-character cooldown after a contract ends | |
OfferLifetime |
600000 |
How long an unclaimed contract survives | |
MinDistanceFromPlayer |
120.0 |
Never spawn on top of someone | |
MaxDistanceFromPlayer |
700.0 |
Never spawn where nobody will see it | |
ActiveHours / HourStart / HourEnd |
false / 18 / 5 |
Optional night-only window | |
Robber |
Models |
five street peds | One is picked per contract |
Weapon / Armed |
WEAPON_PISTOL / true |
What he draws when the police arrive | |
Health |
200 |
Max health; the on-screen bar offsets it by HealthBarOffset |
|
Accuracy |
35 |
SetPedAccuracy |
|
EventRadius |
25.0 |
Area blip radius, and the radius inside which the help text shows and the horn is armed | |
BoardDistance |
3.5 |
"He is at the door" threshold: inside it the contract reports boarding and a standing robber is not a stall | |
ApproachTimeout |
30000 |
Give up on the run | |
DressTimeout |
10000 |
How long to keep asking for control to configure a freshly streamed robber | |
BoardTimeout |
20000 |
Timeout handed to each TASK_ENTER_VEHICLE; once he is at the door the retry budget is this times ApproachRetries |
|
ApproachRetries |
3 |
Attempts before the warp fallback | |
WarpFallback |
true |
Last resort only, and only while the car is stopped | |
ShootsAtPursuit |
true |
He fires from the passenger seat when you are wanted | |
StreamRadius |
350.0 |
Blip and alarm activation radius | |
FleeDistance |
500.0 |
Flee distance when he gives up on you | |
Carry.* |
bag prop, bone 36029, move_m@clipboard |
The holdall he carries, attached to his hand with a matching walk. Enabled = false removes it |
|
PacifyInterval |
1500 |
How often the client that owns an unclaimed robber re-settles him if he has wandered | |
StallRetask |
4000 |
How long an enter task may be dead, or a running robber motionless away from the door, before it is re-issued; also the floor between two re-issues | |
Combat.Profiles / Styles / Weapons |
four styles | Which robber fights back and how hard — see below | |
AlarmSound / AlarmSoundset / AlarmSounds / AlarmBanks |
true / ALARMS_SOUNDSET / … |
Location-keyed alarm bell | |
Package |
Model |
ch_prop_ch_duffelbag_01x |
The bag he carries and drops — one model for both. Any object model; swap it freely |
PickupDistance |
1.5 |
Grab radius | |
DespawnDistance |
1000.0 |
Bag is abandoned past this | |
Lifetime |
300000 |
Bag lifetime before the contract closes | |
BlockInWater / WaterDepthLimit |
true / 0.5 |
No fishing it out of the sea | |
RequireOnFoot |
true |
Get out of the car to take it | |
WantedOnPickup |
1 |
Stars granted on collection | |
GrabAnimDict / GrabAnimClip / GrabAnimPhase / GrabDuration |
Pickup animation | ||
Vehicle |
AllowBikes |
false |
Bikes have no passenger seat worth the name |
RequireDriveable |
true |
IsVehicleDriveable |
|
RequireEmptyPassengerSeats |
true |
He will not ride with other passengers aboard | |
PreferredSeats |
{0,1,2} |
Seat preference order | |
BlockedModels / AllowedModels |
{} |
Your own overrides; allow wins over block | |
AbandonGracePeriod |
20000 |
How long you may be out of the car pre-pickup | |
LockDoorsWithRobber / ExitLockout |
true / 10000 |
The ten-second lock-in | |
Signal |
HornHoldTime / HornReleaseGrace |
250 / 120 |
Hold, do not tap; the grace keeps a held horn latched across dropped frames |
BlipDistanceFoot / BlipDistanceVehicle |
50.0 / 100.0 |
Blip and area blip range | |
ResendCooldown / RetryDelay / ClaimTimeout |
3000 / 2000 / 8000 |
Client-side signal throttling; a rejected claim reopens the horn after RetryDelay, which must stay above the server's SignalRateLimit (1500) |
|
NoticeCooldown |
2500 |
Floor between two toasts about the same signal problem | |
Pursuit |
UseGameWanted |
'auto' |
'auto' uses the vanilla wanted system until another resource is proven to be suppressing it, then drives the chase off the heat model alone. true forces the vanilla stars, false never touches them |
WantedLevel |
2 |
Stars applied when the run starts | |
StartDelay |
2000 |
Delay from the run starting to the stars | |
HeatStart / HeatMax |
70 / 100 |
Custom heat scale | |
HeatDecayPerSecond / HeatGainPerSecond |
2.2 / 4.0 |
Cooling and heating rates | |
PoliceSightRange / PoliceCloseRange |
90.0 / 45.0 |
Player-police pressure bands | |
DecayGraceTime / DecayGracePerStar |
8000 / 4000 |
Quiet time before heat falls, extended by the stars last reported | |
CorroborationRange / UncorroboratedDecayFactor |
250.0 / 0.5 |
How near a real officer must be to corroborate a calm report, and how much slower heat falls without one | |
NpcPoliceFallback / NpcPoliceRange / NpcPoliceRequireSiren |
true / 120.0 / true |
NPC police contribution; only responding units with a siren count | |
ReportInterval |
1000 |
Client observation cadence | |
MinPursuitTime / MaxPursuitTime |
30000 / 480000 |
Floor and ceiling on the chase | |
EvadedGraceTime / CleanDecayMultiplier |
4000 / 3.0 |
Once the stars are genuinely gone and no NPC police are near, heat drains at triple rate after a short beat, so the post-chase wait does not drag | |
RaiseMaxWanted |
true |
Raise the game's wanted ceiling for the chase and restore it afterwards. Needed on any server that sets SetMaxWantedLevel(0) |
|
ForceDispatch / DispatchRestore |
true / 'disable' |
QBCore and QBox disable the game's police dispatch services at startup, so stars alone bring nobody; this re-enables them for the chase and puts them back after. Skipped automatically when lastresort_intel_npc owns the police |
|
EvasionGrace / HoldTime |
15000 / 300000 |
The stars release only after the engine calls you evaded for this long with no responding police nearby, and unconditionally after HoldTime |
|
StompWindow / StompStrikes |
1000 / 3 |
How the resource decides another script is wiping the wanted level: this many wipes inside this window of a fresh write | |
FightHeat |
45 |
With the vanilla wanted system off, the robber fights and hurries you above this heat | |
Bark |
Enabled / Backend |
true / 'auto' |
The robber's speech. 'native' forces the built-in renderer instead of lastresort_chat |
Duration / MinGap / DefaultCooldown |
4500 / 6000 / 30000 |
How long a line stays up, the floor between any two lines, and the per-situation repeat guard | |
NativeRange |
25.0 |
Draw distance for the built-in renderer | |
ChatEcho |
true |
Also print his lines into the chat panel — Robber: …, with shouting and muttering labelled |
|
SolicitRadius |
30.0 |
He calls out to any player inside this radius while unclaimed — on foot or in a car | |
SlowSpeed / LongPursuit / CleanRun |
8.0 / 120000 / 45000 |
Thresholds behind the "you are crawling", "this is dragging on" and "you are good at this" lines | |
Cooldowns |
per situation | Overrides DefaultCooldown for the chatty ones |
|
Delivery |
ArriveDistance |
15.0 |
Where you may stop |
RobberArriveRadius |
2.5 |
Where he must reach | |
StopSpeedThreshold |
3.0 |
You must actually stop | |
RevealDelay |
3000 |
Beat before the drop-off message | |
RegionMatched |
true |
Pick a drop-off in the robbery's own region | |
OriginalDestinationsOnly |
false |
true restricts to the three primary drop-offs |
|
MinSafehouseDistance / MaxSafehouseDistance |
500.0 / 6000.0 |
Distance band | |
AbandonDistance / AbandonGrace |
250.0 / 20000 |
Leave him this far behind for this long and the job is off | |
Exit |
'flee' |
What he does once he is out at the drop: 'flee' sprints him away from the car, 'walk' walks him to the door instead |
|
PayoutBeat |
4000 |
The pause between him getting out at the drop and the money landing, so the farewell plays before the payout shard | |
DismountTimeout / DismountAttempts |
8000 / 3 |
Wait for the robber to be out of the car before he leaves; retries if he is not | |
Blips |
Robber / Package / Safehouse / Area |
sprite 143 / 143 / 40, colours 57 / 57 / 12 | Sprite and colour per blip |
Hud |
ShowHealthBar / ShowHeatBar + geometry |
Two bars, drawn only while a contract is live | |
Integrations |
ObjectivesPosition |
'tr' |
Which corner the objective tracker docks to; moved off the chat's corner by default |
Notify |
Handler / Fallback / Duration / Position / Custom |
'auto' / 'builtin' |
Notification routing |
Reputation |
XpDelivered / XpStolen / XpFailed / PayoutStep |
120 / 70 / 0 / 0.08 |
Progression |
config/config_server.lua — server-only, never sent to a client
| Block | Key | Default | Meaning |
|---|---|---|---|
Rewards |
Account |
'cash' |
'cash' or 'bank' |
Delivered |
9000–15000 |
Base payout band | |
PackageStolen |
4000–7000 |
Reduced band | |
DistanceBonusPerKm / DistanceBonusCap |
400 / 4000 |
Server-measured distance only | |
PursuitBonusPerMinute / PursuitBonusCap |
500 / 2500 |
Server-clocked pursuit length; heat never enters the payout | |
CleanRunBonus |
2000 |
The robber was never hit | |
IntactVehicleBonus / IntactVehicleThreshold |
1500 / 850 |
The getaway car arrived above that health | |
RobberSurvivalBonus / RobberSurvivalThreshold |
1200 / 0.75 |
He arrived above that fraction of his health | |
FastCompletionBonus / FastCompletionTime |
2000 / 240000 |
Whole job inside four minutes | |
Item / ItemCount |
nil / 1 |
Optional item payout | |
CustomPayout |
nil |
Standalone wallet hook | |
Police |
Jobs / Aces / RequireOnDuty |
Who counts as police | |
MinimumOnline |
0 |
Suppress dispatch below this many police | |
Dispatch |
Enabled / Provider / Title / Message / Code / blip fields / RepeatInterval |
Alert content | |
Persistence |
Mode / Table / File / SaveInterval |
'auto' / wheelman_drivers / data/drivers.json / 60000 |
Storage |
Limits |
ContractLifetime |
1800000 |
Hard ceiling on a claimed contract |
SignalRateLimit / ObserveRateLimit |
1500 / 750 |
Per-source event throttles | |
MaxSignalDistance |
40.0 |
Server-checked claim distance | |
MaxDeliveryDistance |
25.0 |
Server-checked delivery distance | |
MaxPackageDistance |
6.0 |
Server-checked pickup distance | |
MaxPositionDrift / MaxSpeed |
250.0 / 90.0 |
Teleport rejection for the distance bonus, in metres and metres per second | |
MinEscortDuration / MinEscortDistance |
12000 / 250.0 |
The robber must have been in your car this long, over this far, before a delivery counts | |
MaxEscortStep |
3000 |
Longest gap between two escort samples that still counts towards it | |
MaxSeatScan |
8 |
Seats scanned when looking for the robber | |
OutroCeiling / FailureOutroCeiling |
75000 / 45000 |
How long the server waits for the client's ending before it cleans up itself | |
SilentDriverTimeout |
45000 |
A claimed contract with no observations for this long is handed back | |
FailConfirmWindow |
5000 |
A client-reported failure must still be true after this long | |
MaxReportedWanted / MaxReportedNpcPolice |
5 / 12 |
Ceilings on what a client may claim in an observation | |
MinEvadedClaim |
5000 |
A client must have been in pursuit this long before it may claim it shook them | |
MinContractSweep |
250 |
Floor on the sweep cadence | |
BoardingDeadline |
120000 |
How long a contract may sit unboarded before the server hands it back | |
MaxReleases |
2 |
Times one contract may be handed back before it is cancelled | |
ReputationRateLimit |
5000 |
Throttle on the stats query | |
StaleContractSweep |
30000 |
Sweep cadence | |
Preflight |
Enabled |
true |
Scan the started resources once at boot for anything that turns the police off |
Delay |
15000 |
How long after this resource starts the scan runs, so late resources are included | |
Files |
five common config paths | Tried in every resource on top of its own non-globbed script list | |
MaxFiles |
600 |
Hard ceiling on files read by the scan | |
Skip |
{} |
Resources the scan ignores | |
StopOnConflict |
false |
Stop this resource instead of printing the notice. See below before turning it on | |
Admin |
Aces |
wheelman.admin, command, group.admin |
Who may run the admin command |
Logging |
Enabled / Console / Webhook / WebhookName / WebhookColour / LogPayouts / LogRejections |
Console and Discord logging |
Commands
| Command | Who | What |
|---|---|---|
/wheelman |
anyone | Your rank, XP, completions, bags taken, failures and lifetime earnings |
/wheelmanadmin list |
admin | Every live contract with its state and driver |
/wheelmanadmin spawn [locationId] |
admin | Place one; no id means the director picks |
/wheelmanadmin tp [id] |
admin | Teleport yourself to a contract, a robbery id or a drop-off id. No argument goes to your own drop-off if you are on a job, otherwise to the nearest offer. Your car comes with you |
/wheelmanadmin clear [contractId] |
admin | Cancel one, or all |
/wheelmanadmin state [id] |
admin | Full dump of one contract: state, bucket, driver, crew, net ids, escort samples, travel, heat, safehouse |
/wheelmanadmin cleanup <id> |
admin | Run CleanupContract on it and report what was removed — the centralised teardown, testable in isolation |
/wheelmanadmin difficulty [tier] |
admin | List the tiers, or force a contract at one |
/wheelmanadmin reset |
admin | Clear location and player cooldowns |
/wheelmancrew |
anyone | Join the nearest getaway as crew, when crews are enabled |
/wheelmandebug |
Config.Debug only |
On-screen state panel: contract id, state, heat, robber ped and health, package, nearest offer, whether your car passes the gate and why not, wanted level |
/wheelmanwaypoint |
Config.Debug only |
Waypoint to the nearest offer, at any distance — the blip itself only draws when you are close |
Admin access is ACE-gated through ConfigServer.Admin.Aces — wheelman.admin, command or
group.admin by default:
add_ace group.admin wheelman.admin allow
Every action except tp also runs from the server console, which skips the ace check.
The two /wheelman… debug commands need Config.Debug = true in config/config.lua and a
restart lastresort_wheelman; it ships false. With it off they say so rather than doing nothing.
Testing a contract end to end
The director will not place a contract closer than MinDistanceFromPlayer (120 m), so for a test
you want the forced path:
/wheelmanadmin reset -- drop the location and player cooldowns
/wheelmanadmin spawn getaway_03 -- force one at Strawberry, ignoring cooldown and concurrency
/wheelmanadmin tp getaway_03 -- go there, in whatever you are driving
Then pull up next to the robber and hold the horn. /wheelmanadmin tp with no argument will take you
to the drop-off once the contract reveals it.
Exports
Server
exports.lastresort_wheelman:GetActiveContracts() -- array of contract summaries
exports.lastresort_wheelman:GetContract(contractId) -- one summary, or nil
exports.lastresort_wheelman:GetPlayerContract(src) -- that player's contract, or nil
exports.lastresort_wheelman:IsPlayerOnContract(src) -- boolean
exports.lastresort_wheelman:CreateContract(locationId, force) -- summary, or nil + reason
exports.lastresort_wheelman:CancelContract(contractId) -- boolean
exports.lastresort_wheelman:GetDriverStats(src) -- xp, rank, completed, failed, stolenPackages, earnings
-- reads the warm cache; nil until the player has been seen
exports.lastresort_wheelman:GetLeaderboard() -- top 25 by xp, snapshot refreshed every two minutes
exports.lastresort_wheelman:GetLocations() -- copy of the robbery location table
exports.lastresort_wheelman:GetSafehouses() -- copy of the drop-off table
exports.lastresort_wheelman:SetDirectorEnabled(bool) -- pause or resume automatic placement
Client
exports.lastresort_wheelman:GetContractState() -- current state string, or nil
exports.lastresort_wheelman:IsOnContract() -- boolean
exports.lastresort_wheelman:IsGetawayVehicleValid() -- boolean, reason
exports.lastresort_wheelman:RequestDriverStats() -- asks the server to push your stats
Events
Fired server-side for other resources to listen to. All are TriggerEvent, not net events.
'lastresort_wheelman:contractOffered' -- (contractId, locationId)
'lastresort_wheelman:contractStarted' -- (contractId, src)
'lastresort_wheelman:contractResolved' -- (contractId, outcome, src, payout)
'lastresort_wheelman:packageCollected' -- (contractId, src)
'lastresort_wheelman:reputationChanged' -- (src, identifier, rankId, xp)
Player state bag
While a player holds a contract the server sets a state bag on them, so other resources can see it without an export call:
Player(src).state['lastresort_wheelman:contract'] -- the contract id, or nil
It is set on claim and cleared on release, resolution, disconnect and cleanup.
Security model
The server owns ActiveContracts, PlayerContracts, LocationCooldowns and PlayerCooldowns. The
client owns nothing but its own presentation.
- The client never names a reward.
LRW.Net.delivercarries a contract id and nothing else. The amount is computed on the server from a config band, a server-measured travelled distance, the server's own peak-heat record and the server's copy of your reputation. - There is no "mission complete" event. Delivery is validated: correct state, you are the
contract's driver, you are within
MaxDeliveryDistanceof the drop-off the server chose, the robber entity still exists, he is alive, and he is within 25 m of you. - Claiming is a test-and-set.
LRW.Contracts.claim()checks state, checks that nobody else holds the contract, checks your cooldown and checks your distance, then writes the driver — all inside one server tick, so two simultaneous honks cannot both win. - State transitions are checked against a table.
LRW.canTransition(from, to)inshared/states.luais the only way a contract changes state. A client asking for an illegal jump is dropped silently. - Entity ids are resolved, not trusted. Every net id a client sends is turned into a real server
entity with
NetworkGetEntityFromNetworkIdandDoesEntityExistbefore it is used, and the escape transition additionally verifies that you are in seat-1of the vehicle you named. - Package pickup cannot be repeated. State must be
DOWNED, the bag must exist, you must be withinMaxPackageDistance, andpackageClaimedis set before the object is deleted. - Distance travelled is measured server-side, each sample is clamped to what
ConfigServer.Limits.MaxSpeedallows in the elapsed interval, and the running total is capped by wall-clock time since the claim. A teleport cannot inflate the distance bonus. - The pursuit bonus is on the server's clock, not on client-reported heat, so a client that claims five stars gains nothing from saying so.
- Delivery requires the robber to have actually been carried. The server samples
GetVehiclePedIsIn(robber) == yourVehicleon every observation and adds up the time he was aboard and the ground the car covered while he was. A delivery underMinEscortDurationorMinEscortDistanceis refused, and a jump longer thanMaxEscortStepadds nothing to either, so teleporting the ped to the drop-off does not pay. - Boarding is not proximity. The server resolves the vehicle from the net id, confirms the caller is in its driver seat, checks the model and the vehicle type, and requires the robber to be in one of that vehicle's passenger seats. Standing next to a car is not getting into it.
- Delivery re-checks all three parties: the driver in the bound car's driver seat, the robber a
passenger in that same car, and the car itself within
MaxDeliveryDistanceof the drop. - Leaving him behind is measured on the server. Every observation and every sweep compares the
driver's ped to the robber entity; past
AbandonDistanceforAbandonGracethe contract ends asABANDONEDwhatever the client reports, and a client-reported abandonment is still verified against the same distance before it is accepted. - A missing robber is
ENTITY_LOST, never a death. A vanished entity or net id cannot be reported as a kill and cannot produce a package. - Package pickup needs the real object. The entity must exist, be the configured model, be in the contract's routing bucket, and its position is read from the entity — the cached coordinate is never a fallback. On-foot is enforced when configured.
- Heat only falls when nothing contradicts it. Cooling needs a grace window of clean reports, and
no player officer inside
PoliceSightRangewhatever the client claims. The grace is extended byDecayGracePerStarfor every star the client itself last reported, so a client that suddenly announces its four-star chase evaporated waits the longest. Corroboration is positional, not a head-count: only an officer insideCorroborationRangeof the contract counts, and without one the rate is cut byUncorroboratedDecayFactor. A client reportingwanted = 0cannot cool itself down on its own word. - The package branch cannot skip the pursuit. Taking the bag starts the heat clock if it was not
already running, and cashing out goes through the same
LRW.Heat.isCoolgate as a delivery. - Handing a contract back is bounded by
MaxReleases, so claim-and-release cannot hold a location's slot open indefinitely, and the server hands one back on its own afterBoardingDeadlineif the client stops making progress before the pickup. - Failure costs a cooldown only when it was yours. An admin cancellation, an expiry or a lost entity leaves you free to take the next contract immediately.
- Every net event is rate-limited per source and type-checks its arguments before doing anything.
- Contract entities are created on the server, so a client cannot spawn a second robber or a second bag.
- Routing buckets are respected. A contract carries the bucket it was created in; offers are sent only to players in that bucket, and a claim — or a crew join — from another bucket is refused, so an event in one instance cannot be taken, seen or ridden along with from another.
- A driver can only be paid once per contract. Holding a crew share counts as being busy, so a passenger cannot take a contract of his own alongside it, and he cannot ride with two crews.
- The difficulty gate is checked at the claim. The tier's
requiresRankis compared against the claiming player's own reputation, not against whoever the contract was graded for. - Condition bonuses use the worst reading, not the last one. The server keeps a running minimum of the car's and the robber's health across the whole run, so repairing the car or healing the ped in the last hundred metres does not buy the intact-vehicle, survival or clean-run bonus.
- Vehicle types are an allowlist.
GetVehicleTypeis checked server-side against the road vehicles the job accepts; boats, aircraft, blimps, bicycles, trains, trailers, submarines and anything a future game build introduces are refused rather than tolerated. - No export yields.
GetDriverStatsreads the warm cache andGetLeaderboardreads a snapshot refreshed every two minutes, so a cross-resource call never blocks on a database round trip.
Cleanup
LRW.Contracts.cleanup(contractId, reason) is the single teardown path. It deletes the robber, the
bag, the driver mapping and the broadcast, in that order, and runs on:
success, failure, timeout, expiry, entity loss, admin cancellation, playerDropped and
onResourceStop.
Client-side, LRW.Blips.clear(), LRW.Alarm.stopAll(), LRW.Pursuit.release() and
LRW.Vehicle.endLockdown() all fire on onClientResourceStop, so a restart cannot leave a locked
car, a stuck wanted level, a route line or an orphan blip behind. The client never creates a
networked entity, so it has none to delete — it only ever releases its hold with
SetEntityAsNoLongerNeeded.
Disconnecting mid-contract before the pickup releases the contract back to OFFERED for someone
else; disconnecting after the pickup resolves it as ABANDONED.
Performance
There is no unbounded Wait(0) anywhere. Every loop is state-gated and picks its own sleep:
| Loop | Sleep |
|---|---|
client/director.lua discovery |
1500 ms, tightening to 250 ms only within three times EventRadius |
client/director.lua horn prompt |
500 ms, dropping to 0 only while the help text is actually on screen |
client/director.lua blips |
1000 ms |
client/contract.lua |
1000 ms idle; 250–750 ms per state while a contract is live |
client/hud.lua |
500 ms, dropping to 0 only while a bar is drawn |
client/vehicle.lua lockdown |
500 ms, dropping to 0 only while you are sitting in the locked car |
client/debug.lua |
500 ms, and 0 only when the panel is open and Config.Debug is set |
Server-side there are two timers: the contract sweep (StaleContractSweep, 30 s) and the persistence
flush (SaveInterval, 60 s). Heat is recalculated on the client's observation event, not on a
server tick.
Expected resmon: 0.00 ms idle for a player with no contract and none nearby; roughly
0.02–0.06 ms while a contract is running, which is the two HUD bars.
These figures are the design target derived from the structure above, not a live measurement: this build was produced without a FiveM server and game client to record
resmonagainst. Confirm them on your own server before publishing them as your own numbers.
OneSync
Required, and used deliberately.
- The robber and the bag are created with the server-side
CreatePed/CreateObject, so they exist once, globally, and no client race can duplicate them. - The driver's client takes control before every task with
LRW.Entities.requestControl(), which loopsNetworkRequestControlOfEntityuntilNetworkHasControlOfEntityreturns true or the attempt budget runs out. Every task call is guarded by it. - Net ids are re-resolved on every use rather than cached, so a migrated entity is picked up again instead of going stale.
- If the robber or the bag stops resolving, the contract ends as
ENTITY_LOSTrather than hanging. - Entities the client did not create are released with
SetEntityAsNoLongerNeeded, never deleted — deletion is the server's job.
Tests
The resource ships its offline test suites. They boot the whole server half against stubbed FiveM natives and drive it — the happy path, the package path, the claim race, every illegal state transition, delivery and pickup validation, payout idempotency, disconnects, rate limiting, hostile payloads, routing-bucket isolation, the server-side abandonment rule and a final entity-leak sweep — plus a static pass over the manifest, both locales, config parity, the location data and the startup police check.
tests/boarding.test.lua boots the whole client half against a stubbed engine with coroutine-driven
threads and drives the boarding state machine: the enter task on the first tick after the horn, the
blocked-door seat choice, re-tasking and the stopped-car warp, the mid-run re-board in ESCAPE and
DROPOFF, the abandon bail from a robber who has streamed out, the driver on foot, the wreck, and
the drop-off outro as the one legitimate exit.
473 assertions across five files, no server required:
cd lastresort_wheelman
lua5.4 tests/boarding.test.lua
lua5.4 tests/contracts.test.lua
lua5.4 tests/integrity.test.lua
lua5.4 tests/preflight.test.lua
lua5.4 tests/vehicle.test.lua
They do not exercise GTA's ped AI, the wanted system or entity ownership; nothing here runs a game
client. What the harness therefore cannot prove, and what a first in-game run should watch
[NEEDS IN-GAME TEST]: that SCRIPT_TASK_ENTER_VEHICLE is the hash GET_SCRIPT_TASK_STATUS reports
the enter task under (the name follows the SCRIPT_TASK_<NATIVE> convention of every other entry and
is backed by GET_VEHICLE_PED_IS_TRYING_TO_ENTER either way); that the stay-in flags hold him through
a hard frontal crash and a wreck; that IS_PED_GETTING_INTO_A_VEHICLE turns true at the door and
not before; and that the ten-second lock-in (SET_VEHICLE_DOORS_LOCKED state 3, player-only lockout)
never stops his enter task, which is why the lock is dropped the moment he is found outside anyway.
Localisation
Twelve complete language packs ship in locales/, with 275 strings each, including robber dialogue:
| Language | Code |
|---|---|
| English | en |
| Hungarian | hu |
| German | de |
| French | fr |
| Spanish | es |
| Brazilian Portuguese | pt-br |
| Polish | pl |
| Turkish | tr |
| Russian | ru |
| Italian | it |
| Czech | cs (also accepts cz) |
| Slovak | sk |
Every player-facing string is a locale key; nothing is hardcoded in Lua.
For example, use setr lr:locale cs for Czech or setr lr:locale sk for Slovak
in server.cfg, then restart the resource. Explicit settings such as
Config.Locale = 'cz' and regional codes such as cs_CZ / sk_SK are also normalised.
To add a language, copy locales/en.lua to locales/de.lua, change Locales['en'] to
Locales['de'], translate, and set Config.Locale = 'de' — or leave it on 'auto' and set
setr lr:locale de in server.cfg. The manifest already globs locales/*.lua.
Config.Locale = 'auto' resolves once at start, on the server and on each client, and takes the first source that names a language with a pack in locales/:
- the
lr:localeconvar — one line inserver.cfgsets every LastResort resource at once:setr lr:locale hu - the
locale,ox:locale,esx:localeandtxAdmin-localeconvars, in that order - on the client only, the player's own GTA language setting
en
Codes are normalised before matching (hu_HU → hu, en-US → en, pt_BR → pt-br), and a code with no pack is skipped rather than failing. The server console names the result once at boot — [lastresort_wheelman] locale: hu (lr:locale convar) / [lastresort_wheelman] locale: en (default); the client prints nothing. Use setr, not set, for lr:locale: a client resolves its own copy, a set convar never reaches it, and the client would fall through to the player's game language while the server stayed on its own result. An explicit code such as 'hu' skips detection entirely; an explicit code with no pack falls back to English as before.
Turning the vanilla wanted level on or off
Config.Pursuit.UseGameWanted has three settings:
| Value | Behaviour |
|---|---|
'auto' (default) |
Uses the game's stars, and watches them. If another resource keeps clearing or clamping them (three wipes within a second of a fresh write), the driver is told once, the server console prints once which problem it is, and that client finishes the chase on the heat model alone. |
true |
Always writes the stars and keeps re-asserting them for the length of the pursuit. Never switches itself off; the console diagnostic still prints so you know what is fighting it. |
false |
Never touches the wanted system. The whole chase runs on the 0-100 heat model, NPC police pressure and real player police. Pick this on any server whose framework owns the wanted level. |
On QBCore and QBox servers the suppressor is the smallresources pack, and it does two separate
things at startup: Config.AIResponse.wantedLevels = false runs SetMaxWantedLevel(0) (this
resource raises the clamp back for the chase on its own), and Config.AIResponse.dispatchServices
turns off the game's police dispatch — which is why stars alone bring nobody. ForceDispatch
re-enables dispatch for the length of the chase and DispatchRestore puts it back after, so the
no-police world outside contracts is untouched. To make stars global instead, set
wantedLevels = true and the dispatch entries in the qb-smallresources / qbx_smallresources
config.lua, and leave ForceDispatch on anyway — it costs nothing when dispatch is already up.
Either way the contract flow is unaffected: heat is what reveals the drop-off, and heat never depended on the stars.
The startup police check
Fifteen seconds after the resource starts, the server reads every started resource once and looks
for the handful of calls and settings that turn the police off. It reads each resource's own
non-globbed shared_scripts and client_scripts entries plus five common config paths, and it
stops after ConfigServer.Preflight.MaxFiles files. Nothing is written, nothing is patched, and the
scan never runs again until the next server start.
What it looks for, and what each one means:
| Found | What it does | What to do |
|---|---|---|
SetCreateRandomCops(false) |
Ambient police stop spawning anywhere on the map | Nothing here puts them back. Player police and the heat model still run the chase |
SetCreateRandomCopsNotOnScenarios(false) |
Police stop spawning outside scenario points | As above |
SetMaxWantedLevel(0) |
The wanted ceiling is zero, so no stars can appear | This resource raises the clamp back for the length of a contract |
wantedLevels = false |
The QBCore/Qbox smallresources form of the same clamp | As above |
SetPoliceIgnorePlayer(player, true) |
Police ignore that player entirely | Config.Pursuit.ForceDispatch undoes it at the start of every chase |
SetDispatchCopsForPlayer(player, false) |
The game dispatches nobody to that player | As above |
The output names the resource, the file and the setting, then the fix on their side and the knob on this side:
[lastresort_wheelman] preflight: another started resource turns off part of the police response
[lastresort_wheelman] qb-smallresources -> config.lua: wantedLevels = false
[lastresort_wheelman] effect: the wanted ceiling is zero, so no stars can appear for anyone
[lastresort_wheelman] there: set it to true
[lastresort_wheelman] here: the clamp is raised back for the length of a contract, or set Config.Pursuit.UseGameWanted = false
It is a notice, not a fault. Every line above is survivable: a contract still places, the horn
still claims it, the robber still boards, fights and talks, heat still rises from player police,
the drop-off still reveals and the payout still lands. That is why
ConfigServer.Preflight.StopOnConflict is false. Turning it on makes the resource stop itself
whenever the scan finds anything — it is there if you want a hard gate on your own box, but on a
live server it turns a config note into an outage.
A resource whose files are escrowed cannot be read, so it is skipped in silence; so is anything
listed in ConfigServer.Preflight.Skip, anything not started, and this resource itself. Comments
are stripped before the scan, so a native named in a note is not a finding. A match never crosses a
line break, and a conditional such as
SetMaxWantedLevel(Config.AIResponse.wantedLevels and 5 or 0) is not reported as a clamp to zero —
that case is caught by the config setting behind it instead.
lastresort_intel_npc is the one resource the scan answers for. Its police module suppresses
ambient police and dispatch on purpose while its own police system is on, and a chase here already
stands down for it — ForceDispatch asks its IsPoliceSystemEnabled export first and leaves the
world alone if the answer is yes. The scan says that in one line instead of listing its calls as
faults.
Troubleshooting
Nothing ever spawns. Check Config.Director.Enabled, then MinPlayers, then ActiveHours. The
director only places a contract between MinDistanceFromPlayer and MaxDistanceFromPlayer of
someone, so on an empty map nothing will appear. Force one with /wheelmanadmin spawn.
The robber spawns but there is no help text. You are outside PromptDistanceVehicle, or the
contract is already claimed. Turn on Config.Debug and run /wheelmandebug — the panel shows the
nearest offer and the exact eligibility reason.
The horn does nothing. The help text tells you why: wrong seat, law vehicle, passengers on board,
unsuitable vehicle, airborne, or you already have a wanted level. Hold the horn for
Config.Signal.HornHoldTime; a tap will not register.
He runs to the car but will not get in. Almost always a vehicle with no reachable entry point, or
one parked against geometry. He is sent to the first seat whose door is clear, re-tasked every
StallRetask while the engine refuses, and warped in after ApproachRetries attempts if
Config.Robber.WarpFallback is on — but only once the car has stopped, so stop. Set Config.Debug
and watch the boarding row in the panel: it shows whether the enter task is live and the attempt count.
He got out of the car by himself. He should not: the windscreen, wreck, panic and jack exits are all
switched off on him. If it still happens, stop — he is re-tasked into the car within a tick and the
notification says so. Driving on without him ends the job as ABANDONED after AbandonGrace.
"payout … went unbanked" in the console. No framework wallet was found. Either set
Config.Framework explicitly or supply ConfigServer.Rewards.CustomPayout.
Persistence says json when you wanted SQL. oxmysql was not started when lastresort_wheelman started. Put
ensure oxmysql above ensure lastresort_wheelman.
The drop-off never appears. Heat has not reached Config.Pursuit.ClearThreshold, or
Config.Pursuit.MinPursuitTime has not elapsed. If the console prints that another resource is
clearing or clamping the wanted level, that is the built-in detector: with UseGameWanted = 'auto'
the resource has already switched that client to the heat-model-only chase, and the print tells you
which framework script to look at. Set UseGameWanted = false to make that the permanent behaviour,
or stop the other script from touching drivers who hold a contract. With UseGameWanted = false and
no police near you at all, heat decays on its own once DecayGraceTime has passed — at half rate,
because nothing corroborates the calm — so the drop-off shows up about a minute after the pickup.
The console says another resource turns off part of the police response. That is the startup check above. It names the resource, the file and the setting; fix it there, or take the knob it offers on this side. Nothing is broken by the notice itself.
Police never get an alert. Check ConfigServer.Police.Jobs matches your job names, that
RequireOnDuty matches how your framework reports duty, and that MinimumOnline is not above the
number of police online.
Support
intelnpc.com — LastResort Development.
Installation
- Copy
lastresort_wheelmaninto theresourcesdirectory. - install sql file
- Add this after your framework, optional
oxmysql, and LastResort integrations inserver.cfg:
set onesync on
ensure lastresort_wheelman
- Configure police jobs, rewards, and the dispatch provider.
- Restart the server, then test as an admin with
/wheelmanadmin spawn.
Configure
- Leave persistence and framework detection on
autounless your stack needs a fixed provider. - Set police job names, duty requirements, dispatch, payout account, contract timing, and spawn chance.
- In JSON mode, ensure the resource can write to its
datadirectory. - Optional
lastresort_notifyandlastresort_chatintegrations must start before Wheelman.
Verify
- Run
/wheelmanadmin spawnand drive to the generated contract. - Hold the horn near the robber and confirm only one player can claim the job, and that he starts for the car at once.
- Park with the passenger side against a wall and confirm he goes round to another door; hit something on the way and confirm he stays in.
- Complete a delivery and verify payout and reputation persistence.
- Run
/wheelmanadmin clearand confirm all contract entities are removed.
Important
- OneSync is required.
- In JSON mode, the
datadirectory must be writable. Back it up before updating. - In standalone mode, provide a custom payout function or rewards will only be logged.
- Review police-conflict warnings and disable debug features before going live.
Changelog
2026-09-04
- added support for lastresort_chat and lastresort_notify scripts
- initial release
What you get
- The full source code. Every Lua, config and UI file, readable and editable. Rename it, restyle it or hook your own systems into it.
- No escrow, no license checks. No key and nothing that phones home. It keeps running as long as your server does.
- Free updates. New versions land in your account for as long as we maintain the script. Download the latest one whenever you like.
- One community per purchase. Use it on that community's live, development and test servers. Reselling or re-uploading the code is not allowed.
Get [FREE] Dynamic NPC Robberies & Police Chases | MINIGAME | Legacy & Enhanced comaptible
It is free. Log in and download it from your account.
