Dialogue (NUI)
Multi-step trust conversations and simple accept/decline prompts rendered in a RedM-styled NUI panel. Extracted and generalized from wc_encounter — uses wcdialogue: message types to avoid collisions when multiple Green Studio resources are running.
RunDialogue(def, npcPed, timeoutSecs, opts) → stringclient
Runs a multi-step trust conversation. Returns "success" (trust ≥ 70), "partial" (trust ≥ 40), or "fail".
| Param | Type | Description |
|---|---|---|
| def.header | string | Panel title. |
| def.npcName | string | NPC display name. |
| def.trustLabel | string | Label on the trust bar. Defaults to "Trust". |
| def.intro | table | { text, mood } — opening NPC line. |
| def.steps | table | Array of step tables, each with an options array of { text, nextStep, trustChange, mood, response }. |
| timeoutSecs | number | nil | Auto-fail timeout in seconds. Defaults to 60. |
| opts.skipFaceEachOther | boolean | Skip the face-each-other setup step. |
| opts.skipNpcGestures | boolean | Suppress NPC conversation gestures. |
| opts.skipPlayerGestures | boolean | Suppress player conversation gestures. |
RunAccept(def, npcPed, opts) → booleanclient
Simple accept/decline prompt. Returns true if the player accepted.
| Param | Type | Description |
|---|---|---|
| def.header | string | Panel title. |
| def.npcName | string | NPC display name. |
| def.intro | table | { text, mood } — NPC opening line. |
| def.options.accept | table | { text } — accept button label. |
| def.options.decline | table | { text } — decline button label. |
| npcPed | number | Entity handle of the NPC. |
| opts | table | nil | Same skip flags as RunDialogue. |
WatchIgnore(guard, npcPed, distance, onIgnored)client
Fires onIgnored() if the player walks more than distance metres from npcPed while guard.done is false. Set guard.done = true when the encounter ends to cleanly stop the thread.
| Param | Type | Description |
|---|---|---|
| guard | table | Shared state table. Set guard.done = true to stop watching. |
| npcPed | number | Entity handle of the NPC to watch. |
| distance | number | nil | Walk-away threshold in metres. Defaults to 80. |
| onIgnored | function | Callback fired when the player walks away. |
example
-- Accept/decline prompt
local accepted = wc:RunAccept({
header = 'Stranger',
npcName = 'Old Pete',
intro = { text = 'I need help haulin\' my wagon. You up for it?', mood = 'hopeful' },
options = {
accept = { text = 'Sure, I can help.' },
decline = { text = 'Not today, friend.' },
},
}, npcPed)
if not accepted then return end
-- Multi-step trust dialogue
local result = WCLib.RunDialogue({
header = 'Negotiation',
npcName = 'Old Pete',
trustLabel = 'Trust',
intro = { text = 'Right then. About the pay...', mood = 'neutral' },
steps = {{
options = {
{ text = 'Fair enough. What\'re you offerin\'?', trustChange = 10, mood = 'friendly', response = 'Fifty cents and no trouble.' },
{ text = 'I want double.', trustChange = -15, mood = 'doubtful', response = 'You sure about that, stranger?' },
}
}}
}, npcPed, 45)
print('Dialogue result:', result) -- "success", "partial", or "fail"
Message type prefix. The NUI panel uses
wcdialogue: prefixed message types. If you previously used wc_encounter's encounter: types directly, update your JS accordingly.
Real Wild County example — trader NPC haggling
wc_trader/client/main.lua
local result = wc:RunDialogue({
header = 'The Fence',
npcName = 'Silas',
intro = { text = "Got somethin' for me?", mood = 'neutral' },
steps = { { options = HaggleOptions } },
}, fencePed, 40)
if result == 'success' then
TriggerServerEvent('wc_trader:sellAtBestPrice')
elseif result == 'partial' then
TriggerServerEvent('wc_trader:sellAtFairPrice')
else
wc:Notify({ variant = 'fail', title = "Silas isn't interested." })
end
Common mistakes
- Calling
WCLib.RunDialogue(...)/WCLib.RunAccept(...)from another resource — always usewc:RunDialogue(...)/wc:RunAccept(...). - Trusting the client-reported dialogue result for anything that pays out money or items — re-validate on the server (e.g. only allow the sale event once, gate by mission state) rather than trusting a client "success" string alone.
- Starting a second dialogue while one is already open — wc_libs only supports one active dialogue at a time; a second call will interfere with the first.
- Forgetting to set
guard.done = truewhen your own encounter ends —WatchIgnore's thread keeps polling until you do.
Troubleshooting
- If the dialogue panel never appears: confirm the NUI files under
nui/dialogue/are declared infxmanifest.luafiles{}and actually shipped with the resource. - If push-to-talk stops working while the dialogue is open: that's unexpected — the module explicitly keeps
INPUT_PUSH_TO_TALKenabled during NUI focus; check you're not overridingSetNuiFocusKeepInputelsewhere. - If the dialogue "fails" instantly: check
def.steps[1].optionsisn't empty — a step with no options has nothing for the player to click. - If it doesn't close properly when your resource restarts: this is handled automatically via
onResourceStopfor the owning resource — if it's still stuck, confirm you passedopts.ownerResourcecorrectly if you called it from a different resource context than expected.