Stagecoach Job Example
A complete wc_stagecoach resource — buy a ticket from a clerk NPC, follow a GPS route, get paid on arrival. Server-authoritative from start to finish. See the Creating a Job guide for the step-by-step walkthrough this is built from.
fxmanifest.lua
wc_stagecoach/fxmanifest.lua
fx_version 'adamant'
game 'rdr3'
lua54 'yes'
name 'wc_stagecoach'
dependency 'wc_libs'
shared_scripts { '@wc_libs/init.lua', 'config.lua' }
client_scripts { 'client.lua' }
server_scripts { 'server.lua' }
config.lua
wc_stagecoach/config.lua
Config = {}
Config.ClerkPos = vector4(-290.5, 800.2, 118.9, 160.0)
Config.Destination = vector3(273.5, -1258.0, 67.0) -- Valentine
Config.Reward = 50
Config.RunTimeoutMs = 600000 -- 10 minutes
client.lua
wc_stagecoach/client.lua
local bag = wc:CreateCleanupBag()
CreateThread(function()
local hash = wc:LoadModel("a_m_m_hillfolk_01")
local clerkPed = wc:SpawnPed(hash, Config.ClerkPos.x, Config.ClerkPos.y, Config.ClerkPos.z, Config.ClerkPos.w, true, true)
wc:CreateBlip(Config.ClerkPos.x, Config.ClerkPos.y, Config.ClerkPos.z, "Stagecoach Station")
local prompt = wc:CreatePrompt("Buy Ticket [G]")
wc:WatchPrompt(prompt, clerkPed, 2.5, function()
TriggerServerEvent('wc_stagecoach:start')
end)
end)
RegisterNetEvent('wc_stagecoach:begin', function()
bag:AddBlip(wc:CreateMissionMarker(Config.Destination.x, Config.Destination.y, Config.Destination.z, "Delivery Point", { route = true }))
bag:AddGPS()
wc:TopNotify('Stagecoach Run', 'Deliver the mail sack to Valentine')
wc:WatchPlayerNear(Config.Destination, 5.0, function()
bag:Clean()
TriggerServerEvent('wc_stagecoach:complete')
end, {
timeoutMs = Config.RunTimeoutMs,
onCancel = function() bag:Clean() end,
})
end)
AddEventHandler('onResourceStop', function(resource)
if resource == GetCurrentResourceName() then bag:Clean() end
end)
server.lua
wc_stagecoach/server.lua
local ActiveRuns = {}
RegisterNetEvent('wc_stagecoach:start', function()
local src = source
if ActiveRuns[src] then
wc:Notify(src, { variant = 'fail', title = "You already have a run active." })
return
end
ActiveRuns[src] = { reward = Config.Reward, startedAt = os.time() }
TriggerClientEvent('wc_stagecoach:begin', src)
end)
RegisterNetEvent('wc_stagecoach:complete', function()
local src = source
local run = ActiveRuns[src]
if not run then return end
ActiveRuns[src] = nil
wc:AddMoney(src, run.reward, 0)
wc:Notify(src, { title = ("Stagecoach Complete: +$%d"):format(run.reward), variant = 'avanced' })
end)
AddEventHandler('playerDropped', function()
ActiveRuns[source] = nil
end)
Runs identically on VORP or RSG — AddMoney, Notify, and every prompt/blip/watcher call already normalize the framework difference for you.