gta6/prompts
jobs
Freeintermediatejobs

Police Job — Cuff, Arrest & Jail Timer

ESX police job that cuffs a nearby player, sends them to jail, and runs a server-authoritative release timer.

NON-TESTÉ — the reference Lua is syntax-validated, not run in a live FiveM server. Adapt and test on your own dev server before shipping.
Est. Lua
~95 loc
Claude
Claude Opus 4.x / Sonnet 4.x
Validation
syntax-validated
Updated
2026-06-25

Description

A working ESX police job: an officer aims at the nearest player and runs /cuff to toggle handcuffs, then jails them for a set number of minutes. The jail teleports and freezes the convict, counts down, and releases them at the police station. This is the core law-enforcement loop every roleplay server needs.

Prompt Template

You are writing a FiveM resource for es_extended (stable, exports getSharedObject).
Produce TWO files (client.lua + server.lua) for a police job:

Client:
- A getClosestPlayer(maxDist) helper that iterates GetActivePlayers(), skips the
  local PlayerPedId(), and returns the closest player index within range.
- RegisterCommand("cuff") that finds the nearest player and fires
  TriggerServerEvent("police:toggleCuff", GetPlayerServerId(pid)). Do NOT apply
  cuffs locally — wait for the server's reply.
- RegisterNetEvent("police:setCuffState", state) that plays/clears the
  mp_arresting anim (RequestAnimDict + a `while not HasAnimDictLoaded` loop).
- RegisterNetEvent("police:sendToJail", seconds) that teleports, FreezeEntityPosition,
  counts down with Wait(1000) in a CreateThread, then unfreezes and releases.

Server:
- Validate the caller is job.name == "police" via ESX.GetPlayerFromId(source).
- Validate the target exists before toggling/jailing.
- police:jail takes minutes; the SERVER computes seconds and owns the timer.

Use PlayerPedId(), never GetPlayerPed(-1). Return only Lua, separated by
"-- ===== client.lua =====" and "-- ===== server.lua =====" banners.

Expected Output

The reference Lua lives at content/expected-outputs/jobs/02-police-arrest-cuff-jail.lua. It implements the closest-player scan, server-validated cuff toggle, and a server-driven jail timer. The fxmanifest splits the file into a client_script (commands, anims, teleport) and a server_script (job check, cuff state map, jail dispatch).

02-police-arrest-cuff-jail.lua95 lines
-- 02-police-arrest-cuff-jail.lua
-- ESX police job: cuff/uncuff a nearby player, arrest, and a server-tracked jail timer.
-- This file holds BOTH sides; deploy as client.lua + server.lua per the banners.

-- ===== client.lua =====
local ESX = exports["es_extended"]:getSharedObject()
local isCuffed = false

-- Find the closest player ped within range of the local player.
local function getClosestPlayer(maxDist)
    local myPed = PlayerPedId()
    local myCoords = GetEntityCoords(myPed)
    local closest, closestDist = -1, maxDist or 2.5

    for _, pid in ipairs(GetActivePlayers()) do
        local target = GetPlayerPed(pid)
        if target ~= myPed then
            local d = #(myCoords - GetEntityCoords(target))
            if d < closestDist then
                closest, closestDist = pid, d
            end
        end
    end
    return closest
end

-- /cuff toggles cuffs on the nearest player (police only enforced server-side).
RegisterCommand("cuff", function()
    local pid = getClosestPlayer(2.5)
    if pid == -1 then
        ESX.ShowNotification("No one nearby to cuff.")
        return
    end
    TriggerServerEvent("police:toggleCuff", GetPlayerServerId(pid))
end, false)

-- Apply or clear the cuff animation/state on this client.
RegisterNetEvent("police:setCuffState", function(state)
    isCuffed = state
    local ped = PlayerPedId()
    if state then
        RequestAnimDict("mp_arresting")
        while not HasAnimDictLoaded("mp_arresting") do Wait(0) end
        TaskPlayAnim(ped, "mp_arresting", "idle", 8.0, -8.0, -1, 49, 0, false, false, false)
        SetEnableHandcuffs(ped, true)
    else
        ClearPedTasks(ped)
        SetEnableHandcuffs(ped, false)
    end
end)

-- Jail teleport + freeze for the duration the server dictates.
RegisterNetEvent("police:sendToJail", function(seconds)
    local ped = PlayerPedId()
    SetEntityCoords(ped, 1641.8, 2570.3, 45.5, false, false, false, false)
    FreezeEntityPosition(ped, true)
    CreateThread(function()
        local remaining = seconds
        while remaining > 0 do
            Wait(1000)
            remaining = remaining - 1
        end
        FreezeEntityPosition(ped, false)
        SetEntityCoords(ped, 441.0, -982.0, 30.7, false, false, false, false)
        ESX.ShowNotification("You have been released.")
    end)
end)

-- ===== server.lua =====
local ESX = exports["es_extended"]:getSharedObject()
local cuffedPlayers = {}

local function isCop(xPlayer)
    return xPlayer and xPlayer.job and xPlayer.job.name == "police"
end

RegisterNetEvent("police:toggleCuff", function(targetId)
    local copPlayer = ESX.GetPlayerFromId(source)
    local target = ESX.GetPlayerFromId(targetId)
    if not isCop(copPlayer) or not target then return end

    local newState = not cuffedPlayers[targetId]
    cuffedPlayers[targetId] = newState or nil
    TriggerClientEvent("police:setCuffState", targetId, newState)
end)

RegisterNetEvent("police:jail", function(targetId, minutes)
    local copPlayer = ESX.GetPlayerFromId(source)
    local target = ESX.GetPlayerFromId(targetId)
    if not isCop(copPlayer) or not target then return end

    local seconds = (tonumber(minutes) or 5) * 60
    TriggerClientEvent("police:sendToJail", targetId, seconds)
end)

Known Failure Modes

  • Client-trusted authority — Claude often applies cuffs/jail directly client-side. Force the cuff toggle and jail dispatch through the server with an xPlayer.job.name == "police" check and target validation.
  • Wrong ped/player scanGetPlayerPed(-1) is deprecated; iterate GetActivePlayers() and use GetPlayerPed(pid) + GetPlayerServerId(pid).
  • Client-side timer — a countdown on the client can be edited away. Keep the duration and release logic authoritative; the client only renders the freeze.
  • Missing anim loadTaskPlayAnim before the dict loads silently no-ops; always RequestAnimDict + while not HasAnimDictLoaded do Wait(0) end.

Integration Notes

  • Split the banners into client.lua and server.lua; list both in fxmanifest.lua (client_script 'client.lua', server_script 'server.lua').
  • Requires es_extended started first, plus a police job seeded in your jobs table.
  • Test on a dev server with two connected clients: one set to police, then /cuff and /jail the other and confirm the release teleport fires after the timer.

Profit Potential

$500–$7000/mo on Tebex (expected ~$1800). [INFERRED] priced inside the $50-389 script band against the signal-scraper tebex_snapshot corpus (median seller $11.85K/mo, n=100), scaled for a hot job-systems niche at intermediate difficulty — police is a top-demand category every RP server buys.

Trend Signal

🔥 hot — custom FiveM job systems = niche-selection LAUNCH #1 (4.75).

Sales Angle

Position as the anti-cheat police core: server-validated cuffing, target checks, and a server-owned jail timer no modded client can edit. Law enforcement is a must-have on every RP server — list at $189.

Difficulty & Ship Time

intermediate · ships in 1 day.