NPC Chat documentation

Install & integrate

Everything you need to drop lrr_npc_chat into a FiveM server — OneSync requirement, add-a-character flow, the complete NPC schema, the server hook contract, the Bridge API, runtime exports and lifecycle events.

← Back to documentation hub

Install

  1. Drop the resource. Place lrr_npc_chat into your resources/ folder.
  2. Ensure dependencies. Append to server.cfg.
  3. Restart. Seven demo characters spawn across the map (bar, alley, mechanic, strip club, park bench, gang corner, boardwalk hot-dog cart).
set onesync on            # REQUIRED (server-side CreatePed + state bags)
ensure ox_lib
ensure ox_target          # recommended (otherwise E-key prompt is used)
ensure lrr_npc_chat
OneSync is required

If onesync is off the server-side spawn refuses to run and logs a clear error. Turn it on with set onesync on (or legacy, or infinity).

Add a character

Drop a file into config/characters/, e.g. dani_taxi.lua:

RegisterNpc({
    id     = 'dani_taxi',
    model  = 'a_m_m_business_01',
    coords = vec4(900.5, -178.2, 73.1, 200.0),
    scenario = 'WORLD_HUMAN_SMOKING',

    speaker       = 'Dani',
    tag           = 'Taxi Dispatcher',
    mood          = 'casual',
    body_language = 'leaning on the cab',

    target = { label = 'Talk to Dani', icon = 'fa-taxi' },
    blip   = { sprite = 198, color = 5, scale = 0.7, label = 'Dispatch' },

    dialogue = {
        greeting = { "Need a ride?", "Where to, friend?" },
        topics = {
            {
                id = 'ride', label = 'Quick ride ($50)',
                text = "Take me to Vespucci.",
                tone = 'bribe', icon = 'fa-route', cost = 50,
                response = "Hop in. Five minutes.",
                fail_response = "Show me the fifty first.",
                server = function(src)
                    if not Bridge.RemoveMoney(src, 'cash', 50) then return false end
                    Bridge.AddItem(src, 'taxi_voucher', 1)
                end,
            },
            { id = 'leave', label = "Not right now.", tone = 'leave', response = "Catch you later.", terminal = true },
        },
    },
})

Add the file to shared_scripts in fxmanifest.lua. Restart. Done.

NPC schema

Spawn

FieldTypeNotes
idstringRequired, unique. Used by exports, state bags, dialogue routing.
modelstringPed model name (a_m_m_business_01) or hash.
coordsvec4vec4(x, y, z, heading)
scenariostringIdle scenario (default WORLD_HUMAN_STAND_MOBILE).
freezeboolFreeze in place. Default false.
bliptable{ sprite, color, scale, label }. Optional.

Identity (NUI header)

FieldTypeNotes
speakerstringDisplay name in the chat header.
tagstringSubtitle under the name (job / location / nickname).
moodstringfriendly, neutral, grumpy, suspicious, casual, cooperative, cautious, cornered, alert, evasive, nostalgic, curious, annoyed, open, distracted, flirty.
body_languagestringShort phrase under the mood pill.
trusttable{ current, max, label, hostile }. Off by default.
gta_voice_namestringOptional speech voice (e.g. GENERIC_HI).

Target / interaction

FieldTypeNotes
target.labelstringox_target button label.
target.iconstringFontAwesome class (fa-comment).
target.distancenumbermeters (default 2.0).
target.e_key_textstringDrawText3D prompt for the E-key fallback path.

Dialogue

dialogue = {
    greeting              = 'string' or { 'pool', 'of', 'strings' },
    greeting_highlights   = { { word = 'foo', cls = 'hl-offer' } },
    opening_system_message= { type = 'default', icon = 'fa-info', text = '…' },
    topics                = { ... },     -- numbered (1-9) menu
    topic_bar             = { ... },     -- always-on T-key chip pool
}

Topic schema

Used inside topics, followups, and topic_bar.

FieldTypeNotes
idstringunique within its parent menu
labelstringchoice button label
textstringwhat the player says — echoed in chat
subtitlestringsmall text under the choice
tonestringdialogue, bribe, tip, investigate, threaten, flirt, rob, mug, pickpocket, help_up, friendly, leave, back, provoke, beg
iconstringFontAwesome class
costnumber$ pill (cosmetic — your server hook does the charge)
skillstringskill chip (cosmetic)
disabledboolshows as locked
disabled_labelstringtext on the lock chip
italicboolitalicise the player echo
previewlisttooltip rows: { { type='good'|'bad'|'info', icon, text } }
responsestring / listNPC reply, or pool
fail_responsestringNPC reply when server returns false without a message
highlightslistcolour words in the NPC reply — classes: hl-offer, hl-threat, hl-name, hl-location, hl-warning
player_highlightslistsame shape, applied to the player echo
system_messagetable{ type, icon, text } — types: default, warning, success, error
terminalboolclose the chat after the response
topic_barboolforce into the chip bar (when used inside topics)
followupslist of topicsnested sub-menu
serverfunction(src, ctx)server-side hook (see below)
clientfunction(ctx)client-side hook (anims, custom notify, ptfx)

Server hook contract

server = function(src, ctx)
    -- ctx = { npc_id, topic_path, player = { src, name, job } }
    --
    -- Return values:
    --   nil / true                          → continue normally
    --   false                                → cancel, reply with topic.fail_response
    --   false, 'short message'               → cancel, reply with that message
    --   { response='…', followups={…}, terminal=bool, highlights={…}, system_message={…} }
    --                                        → fully override the reply + next menu
end

The Bridge API

Bridge.* is in scope inside any server hook. Auto-detects framework + inventory on first call.

-- MONEY
Bridge.GetMoney(src, account)
Bridge.AddMoney(src, account, amount, reason)
Bridge.RemoveMoney(src, account, amount, reason)
-- accounts: 'cash' | 'bank' | 'black_money'

-- INVENTORY
Bridge.HasItem(src, item, count)
Bridge.AddItem(src, item, count, metadata)
Bridge.RemoveItem(src, item, count)
Bridge.GetItemCount(src, item)

-- IDENTITY
Bridge.GetPlayerName(src)
Bridge.GetJob(src)              -- { name, label, grade }

-- NOTIFY
Bridge.Notify(src, { title, description, type, duration })

-- Detect (mostly for debug)
Bridge.DetectFramework()        -- 'qbx' | 'qb' | 'esx' | 'standalone'
Bridge.DetectInventory()        -- 'ox' | 'qb' | 'esx' | 'standalone'

OneSync, persistence, state bags

The server creates every NPC via CreatePed(pedType, hash, x, y, z, h, true, true) and immediately calls SetEntityOrphanMode(ped, 2) so the engine doesn't garbage-collect the entity when no player is in scope. Each ped's lrr_npc_chat:id state bag carries its npc.id to every client that ever streams the entity. Clients listen via AddStateBagChangeHandler and decorate (scenario, ox_target, blip).

See: FiveM OneSync docs and State bags docs.

Runtime API (exports)

-- Server
exports.lrr_npc_chat:AddNpc(definition)      -- same schema as a character file
exports.lrr_npc_chat:RemoveNpc(npc_id)

-- Client
exports.lrr_npc_chat:OpenDialogue(npc_id)    -- force-start a chat
exports.lrr_npc_chat:CloseDialogue()
exports.lrr_npc_chat:IsDialogueOpen()
exports.lrr_npc_chat:GetNpcPed(npc_id)
exports.lrr_npc_chat:IsNpcPed(ped)
exports.lrr_npc_chat:GetNpcIdFromPed(ped)

Client events

AddEventHandler('lrr_npc_chat:client:npcSpawned',   function(npc_id, ped) end)
AddEventHandler('lrr_npc_chat:client:npcDespawned', function(npc_id, ped) end)
AddEventHandler('lrr_npc_chat:client:npcsLoaded',   function() end)

Demo characters

FileWhereShowcases
mike_barkeep.luaPitchers Barrandom greeting pool, nested buy menus, tip flow, highlights, fail_response
anya_fence.luaVespucci alleyitem-gated topics (HasItem), override-from-hook unlocking a discount sub-menu
jenkins_mechanic.luaPremium Deluxejob-aware discount, multi-tier upgrade tree, client anim hook
lola_hostess.luaVanilla UnicornVIP pass + dance voucher chain, flirt tone, photo+tip topic-bar
eli_grandpa.luaMirror Park benchdeep response pool (8 war stories), help-up terminal, mug + guilt system message
carlos_sicario.luaEl Burro alleybulk-buy followup, hostile threaten close, "respect" bribe unlock
hank_hotdog.luaVespucci boardwalkminimal lightweight vendor — 4 topics, secret menu unlock

Compatibility

  • FiveM, lua54, OneSync on
  • QBX-Core / QB-Core / ESX / standalone
  • ox_inventory / qb-inventory / esx_inventory
Pair with

Wire any topic's server hook to Account Levelsexports.account_levels:RequireLevel(src, n) rejects with a localized notification when the player's tier is too low.