Creating A Delivery Mission
A start location, a destination blip, server-tracked mission state, completion validation, a money reward, skill XP, and a Discord log line — the full shape of wc_corndelivery.
-
Start location — pickup prompt
wc_corndelivery/client/main.lualocal pickup = vector3(2420.0, -2050.0, 28.0) local prompt = wc:CreatePrompt("Load Corn Sacks [G]") wc:WatchPlayerNear(pickup, 3.0, function() wc:SetPromptVisible(prompt, true) end) -
Server creates authoritative mission state
Same principle as the job guide: the client requests, the server decides and remembers.
wc_corndelivery/server/main.lualocal ActiveDeliveries = {} RegisterNetEvent('wc_corndelivery:start', function() local src = source if ActiveDeliveries[src] then return end local ok = wc:AddItem(src, 'corn_sack', 1) if not ok then wc:Notify(src, { variant = 'fail', title = "No room for the sack." }) return end ActiveDeliveries[src] = { reward = 35, startedAt = os.time() } TriggerClientEvent('wc_corndelivery:begin', src) end) -
Destination blip on the client
wc_corndelivery/client/main.luaRegisterNetEvent('wc_corndelivery:begin', function() local drop = vector3(-315.0, 790.0, 120.0) -- Valentine general store local marker = wc:CreateMissionMarker(drop.x, drop.y, drop.z, "Corn Drop-off", { route = true }) wc:WatchPlayerNear(drop, 4.0, function() marker:Clear() TriggerServerEvent('wc_corndelivery:turnIn') end, { timeoutMs = 480000 }) end) -
Completion validation — check the item, pay, XP, log
The server checks the player is still tracked as active and is actually carrying the sack (see Inventory) before doing anything.
wc_corndelivery/server/main.luaRegisterNetEvent('wc_corndelivery:turnIn', function() local src = source local delivery = ActiveDeliveries[src] if not delivery then return end if not wc:HasItem(src, 'corn_sack', 1) then wc:Notify(src, { variant = 'fail', title = "You don't have the sack." }) return end local removed = wc:RemoveItem(src, 'corn_sack', 1) if not removed then return end ActiveDeliveries[src] = nil wc:AddMoney(src, delivery.reward, 0) wc:Notify(src, { title = "Corn delivered — +$35", variant = 'avanced' }) -- optional VORP skill XP (see the Skills page — no-ops safely on RSG) if wc:Framework_Is('vorp') then wc:ApplySkillBonus(src, 'trading', 15, '+15 Trading XP') end -- optional Discord log wc:SendWebhook(src, GetConvar('wc_corndelivery_webhook', ''), "Corn Delivery", "complete", { { "Reward", wc:FormatMoney(delivery.reward) }, }) end)
Common mistakes
- Paying the reward before confirming
RemoveItemactually succeeded. - Never expiring stale
ActiveDeliveriesentries for players who disconnect mid-mission — clear them onplayerDropped(see Safe Resource Cleanup). - Calling
ApplySkillBonusunconditionally without theFramework_Is('vorp')guard — it still no-ops safely on RSG, but the guard keeps intent explicit.