gta6/prompts
heists
Freeintermediateheists

Bank Vault Heist — Multi-Stage Drill & Loot

ESX bank vault heist with three sequential drill stages and a server-validated loot payout.

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
~84 loc
Claude
Claude Opus 4.x / Sonnet 4.x
Validation
syntax-validated
Updated
2026-06-25

Description

A working ESX bank vault heist: the player stands at the vault and presses E to run three sequential stages (breach panel, drill lock, crack safe), each with a progress bar, before the vault opens and pays out. This is the flagship multi-stage heist loop a roleplay server sells as endgame content.

Prompt Template

You are writing a FiveM resource for es_extended (stable, exports getSharedObject).
Produce TWO files (client.lua + server.lua) for a multi-stage bank vault heist.

Framework: ESX via exports['es_extended']:getSharedObject().

Client:
- A vault at [VAULT_COORDS] with an ordered Stages table [breach_panel, drill_lock,
  crack_safe]. Track currentStage locally for RENDERING only.
- When the player is within 2.5m and presses E (control 38), run the next stage:
  RequestAnimDict('anim@heists@fleeca_bank@drilling') + a `while not HasAnimDictLoaded`
  loop, TaskPlayAnim the drill, run a ~10s progress loop sending SendNUIMessage
  drillProgress, then ClearPedTasks and TriggerServerEvent('bankheist:completeStage', index).
- RegisterNetEvent('bankheist:stageAck') to advance the rendered stage / show vaultOpen.

Server:
- Track the real stage per player in a table keyed by source. Reject any
  completeStage whose index != expected (DropPlayer on desync).
- Only on the final stage, call xPlayer.addMoney([LOOT_REWARD]).
- Clean the table on playerDropped.

Use PlayerPedId(), never GetPlayerPed(-1). The SERVER owns stage order and payout.
Return only Lua, separated by "-- ===== client.lua =====" and "-- ===== server.lua =====".

Expected Output

The reference Lua lives at content/expected-outputs/heists/01-bank-vault-drill-loot.lua. It implements the proximity drill loop with NUI progress, a server-side per-player stage counter that rejects out-of-order completions, and a one-time payout on the final stage. The fxmanifest splits it into a client_script (drill + NUI) and a server_script (stage validation + money).

01-bank-vault-drill-loot.lua85 lines
-- Resource: bank-vault-heist (ESX)
-- Multi-stage vault drill + loot. client.lua drives the drill stages and props;
-- server.lua validates stage order and pays out loot.

-- ===== client.lua =====
local ESX = exports['es_extended']:getSharedObject()

local VaultCoords = vector3(253.4, 226.1, 101.8)
local Stages = { 'breach_panel', 'drill_lock', 'crack_safe' }
local currentStage = 0
local drilling = false

local function playDrillAnim()
    local dict = 'anim@heists@fleeca_bank@drilling'
    RequestAnimDict(dict)
    while not HasAnimDictLoaded(dict) do Wait(0) end
    TaskPlayAnim(PlayerPedId(), dict, 'drill_straight_idle', 8.0, -8.0, -1, 1, 0, false, false, false)
end

local function runStage(index)
    if drilling then return end
    drilling = true
    playDrillAnim()
    local duration = 10000
    local started = GetGameTimer()
    while GetGameTimer() - started < duration do
        local pct = math.floor(((GetGameTimer() - started) / duration) * 100)
        SendNUIMessage({ action = 'drillProgress', stage = Stages[index], pct = pct })
        Wait(250)
    end
    ClearPedTasks(PlayerPedId())
    drilling = false
    TriggerServerEvent('bankheist:completeStage', index)
end

RegisterNetEvent('bankheist:stageAck')
AddEventHandler('bankheist:stageAck', function(nextIndex)
    currentStage = nextIndex
    if nextIndex > #Stages then
        SendNUIMessage({ action = 'vaultOpen' })
    end
end)

CreateThread(function()
    while true do
        local sleep = 1000
        local ped = PlayerPedId()
        local dist = #(GetEntityCoords(ped) - VaultCoords)
        if dist < 2.5 and currentStage < #Stages then
            sleep = 0
            if IsControlJustReleased(0, 38) then
                runStage(currentStage + 1)
            end
        end
        Wait(sleep)
    end
end)

-- ===== server.lua =====
local ESX = exports['es_extended']:getSharedObject()
local heistStage = {}
local LootReward = 25000

RegisterNetEvent('bankheist:completeStage')
AddEventHandler('bankheist:completeStage', function(claimedIndex)
    local src = source
    local xPlayer = ESX.GetPlayerFromId(src)
    if not xPlayer then return end
    local expected = (heistStage[src] or 0) + 1
    if claimedIndex ~= expected then
        DropPlayer(src, 'Heist stage desync')
        return
    end
    heistStage[src] = expected
    if expected >= 3 then
        xPlayer.addMoney(LootReward)
        heistStage[src] = 0
    end
    TriggerClientEvent('bankheist:stageAck', src, expected + 1)
end)

AddEventHandler('playerDropped', function()
    heistStage[source] = nil
end)

Known Failure Modes

  • Client-trusted stages — Claude tracks the stage and pays loot client-side. Keep the authoritative counter server-side and reject any claimedIndex ~= expected.
  • Anim before loadTaskPlayAnim no-ops if the dict is not loaded; always RequestAnimDict + while not HasAnimDictLoaded(dict) do Wait(0) end.
  • One-sided net event — defining the event on only one side breaks the round-trip; both client and server use RegisterNetEvent + AddEventHandler.
  • No desync guard — without a stage-order check a packet can jump straight to payout; drop or ignore mismatched indices.

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. Pair the SendNUIMessage calls with a simple nui/index.html progress bar, or swap them for an ESX progressbar export.
  • Test on a dev server: stand at the vault, run all three stages in order, confirm money lands only after stage 3 and that replaying a stage out of order is rejected.

Profit Potential

$450–$6500/mo on Tebex (expected ~$1700). [INFERRED] priced inside the $50-389 FiveM script band against a hot endgame-heist niche; corpus median seller $11.85K/mo (signal-scraper tebex_snapshot n=100), scaled up for the flagship multi-stage resource.

Trend Signal

🔥 hot — [INFERRED] multi-stage heists are flagship endgame content servers pay up for.

Sales Angle

Position as the flagship endgame heist every serious RP server needs — a server-authoritative multi-stage bank vault that can't be packet-skipped to the payout. Recommended Tebex price $349.

Difficulty & Ship Time

intermediate · ships in 4-6h.