NX CreativeNX CreativeDocs
Scriptsnx_hud

Customization

The files of nx_hud you can read and edit. Configs, data, inventory gameplay, payment methods, framework bridges, the HUD's data adapters and translations, with a worked example of a custom payment method.

Most of nx_hud is escrowed, but the parts a server owner adapts stay plain Lua and JSON in every release. This page lists them and what each one holds. For options that only take a value, see Configuration. For changes made from your own resource instead of in these files, see Exports.

Edits take effect at the next full server restart. Keep a copy of the files you change, and carry your changes across when you update.

The readable files

PathWhat it holds
config/*.luaThe five config files. See Configuration.
data/*.luaItems, weapons, shops, stashes, crafting, vehicles, evidence lockers, licences and animations.
inventory/custom/client.luaItem effects, who can be searched, the give animation, distances and the drop bag.
inventory/custom/server.luaPayment methods, weapon serial numbers and default loot.
inventory/bridge/**The inventory's framework bridges (ESX, Qbox) and their contract.
target/bridge/**Targeting's framework adapters (ESX, Qbox) and their contract.
bridge/client/*.luaWhere the HUD reads status, money, fuel, the seatbelt and weapon ammo.
inventory/server/45_give.luaGiving an item to another player.
inventory/server/64_vehicles.luaTrunks and gloveboxes: sizes, owned vehicles and loot in unowned ones.
inventory/server/66_dumpsters.luaDumpsters.
inventory/server/70_shops.luaShops: their instances, stock, prices and the purchase.
inventory/server/72_payments.luaHow a purchase is paid: methods, resolvers, price modifiers.
inventory/client/60_shops.luaShop and bench markers, targets, shopkeepers, blips and the openers.
inventory/server/78_licenses.luaBuying licences.
inventory/server/80_commands.luaThe admin inventory commands.
inventory/server/88_compat.luaconvertinventory and what ox_inventory keeps for old scripts.
server/vehicle.luaIndicator sync for a driver who is not the vehicle's network owner.
target/client/70_defaults.luaThe built-in vehicle door options of targeting.
compat/ui/80_esx.luaShowing ESX's own notifications and prompts in the HUD.
compat/ui/81_sharedprefs.luaPer-player preferences that several scripts share.
compat/core/server/60_txadmin.luatxAdmin announcements and warnings shown as notifications.
inventory/locales/*.json, target/locales/*.json, compat/locales/*.jsonTranslations.

init.lua, compat/abi.lua and compat/imports/** are readable too, because other resources load them when they include @ox_lib/init.lua. They are ox_lib's modules: leave them as they are.

Data files

The files in data/ use ox_inventory's formats, so data you already have works, and nxinv migrate apply copies yours across (see Installation). The starter files open with a comment that describes their shape.

nx_hud reads a few extra fields:

  • Shops (data/shops.lua): a payment field on a shop sets how its items are paid for. Give it one name (payment = 'bank') to fix the payment, or a list (payment = { 'money', 'bank' }) to let the player pick in the shop. The first name in the list is the default. Shops without a payment use payments.choices from config/inventory.lua (cash or card out of the box). A currency field on an item names an item or a payment method. It wins over the shop's payment, unless it is one of the names in the list, in which case the player can still pick.
  • Licences (data/licenses.lua): a currency or payment field sets what the licence is paid with. Before anything is charged, the player confirms the price and how it's paid (see Buying a licence).
  • Crafting (data/crafting.lua): recipes take chance, hideChance, failResult, requires and resultMetadata. The bench shows the chance and what a recipe still needs. See Recipe fields.
  • Items (data/items.lua): accessory makes an item wearable, backpack makes a worn bag, armour a plate carrier and plate an armour plate (see Wearable items). carry makes the character carry the item in their hands (see Carrying items).

Wearable items

An item with an accessory field can be worn. While worn it sits in its gear slot, next to the pockets (see Worn gear). The sockets it can go on are set in NxInventory.accessories.sockets: back, vest, watch (the left wrist) and bracelet (the right wrist) out of the box.

One socket. socket names it, and male and female give the clothing numbers:

['watch_classic'] = { label = 'Classic Watch', weight = 100, stack = false,
    accessory = { socket = 'watch', male = { drawable = 0, texture = 0 }, female = { drawable = 0, texture = 0 } } },

Several sockets. sockets lists each socket the item fits, each with its own clothing numbers. One bracelet item then goes on either wrist:

['bracelet_bangles_red'] = { label = 'Red Bangles', weight = 60, stack = false,
    accessory = { sockets = {
        watch    = { male = { drawable = 40, texture = 3 }, female = { drawable = 29, texture = 3 } },
        bracelet = { male = { drawable = 8,  texture = 3 }, female = { drawable = 15, texture = 3 } },
    } } },
  • Using the item, or wearing it from code with setWorn(source, slot, true), puts it on the first free socket. When all of them are taken, it replaces the item on the first one. Using a worn item takes it off. An item that has a use of its own (a weapon, ammo, an item that is used up, or a usable item of your framework) keeps that use, and is worn from its menu.
  • "First" follows NxInventory.accessories.order: with the default, the left wrist, then the right. Add order = { 'bracelet', 'watch' } inside an item's accessory to change the order for that item.
  • The item's menu offers each socket ("Put on left wrist", "Put on right wrist"), and the player can drag it onto either gear slot.
  • A worn item remembers its socket, so it is on the same wrist after a relog.

A socket entry can be true instead of a table to use the accessory's own male and female numbers. sockets can also be a list, { { socket = 'watch', male = {...} }, { socket = 'bracelet', ... } }, whose order is then the item's order.

Looks. male is used for the male freemode ped and any other model, female for the female freemode ped. A look keyed by a model name, such as ['a_m_y_hipster_01'] = { drawable = 2, texture = 0 }, is used for that model. A flat { drawable, texture } in place of male and female fits every model. An accessory table in the item's metadata wins over the item's own, so one item name can come in many looks.

Bags and vests. On the back socket, backpack = { slots, maxWeight, bonus } makes a bag with its own grid. While it is worn, bonus (by default its maxWeight) is added to what the pockets hold, and it cannot come off while the pockets would then be over their limit. On the vest socket, armour = { plates = n } makes a plate carrier that takes items with plate = { armour = n }. See Armour plates.

['backpack'] = { label = 'Backpack', weight = 5000, stack = false, close = false,
    accessory = { socket = 'back', male = { drawable = 45, texture = 0 }, female = { drawable = 45, texture = 0 } },
    backpack = { slots = 15, maxWeight = 25000, bonus = 25000 } },
['heavypc'] = { label = 'Heavy Bulletproof Vest', weight = 1500, stack = false, consume = 0,
    accessory = { socket = 'vest', male = { drawable = 4, texture = 0 }, female = { drawable = 4, texture = 0 } },
    armour = { plates = 2 } },
['heavyplate'] = { label = 'Heavy Plate', weight = 500, stack = false, plate = { armour = 50 } },

inventory/custom/client.lua

The client side of the inventory's gameplay, in the NxInventoryClient table.

  • keepOpen: how far a player can walk from an open stash, drop, trunk or searched player before it closes. A stash or shop that sets its own distance uses that instead.
  • pickupRadius: the inventory key opens the closest drop, stash, shop or bench within this distance.
  • drop: the bag model a drop shows when drop props are on. The inventory:dropmodel convar wins over it.
  • giveAnim: the animation played when a player gives an item. false plays none.
  • searchableAnims and canBeSearched(ped, info): when other players may search this player. By default that is a dead, downed or cuffed player, or one with their hands up. Police can search anyone. A server resource can take this decision over with setSearchCheck.
  • effects: what using an item does, keyed by item name. They run only for items in data/items.lua with no client.export or client.event of their own. Shipped effects: bandage, armour, parachute, phone (npwd) and clothing.

An effect receives (itemDef, item, slot, inv). inv holds helpers:

HelperWhat it does
inv.use(itemDef, onUsed)Use the item up through the server, then run onUsed. Skip it and nothing is used up.
inv.notify(text, type)Show a notification.
inv.text(key, fallback)A string from the inventory's locale files.
inv.loadModel(model)Load a model before you use it.
inv.warn(msg), inv.error(msg)Write to the client console.
inv.wearParachuteBag(ped)Put the parachute bag on the ped.
-- inventory/custom/client.lua
E.energydrink = function(itemDef, item, slot, inv)
    inv.use(itemDef, function()
        SetRunSprintMultiplierForPlayer(PlayerId(), 1.2)
        inv.notify('You feel awake', 'success')
    end)
end

Other resources can override any of this at runtime with registerItemEffect, setStealCheck, addOpenGate and addUseGate.

inventory/custom/server.lua

The server side, in the NxInventoryCustom table. Two helper tables are ready at the top of the file:

  • Inv: count(inv, item), remove(inv, item, count) and add(inv, item, count).
  • Framework: balance, remove and add for a player's money that the framework keeps outside the inventory, as in (src, account, amount, reason), and society, societyRemove and societyAdd for a society account, as in (name, amount, reason). hasJob(src, job, minGrade) says whether the player holds a job at that grade or above. ESX and Qbox answer them through the framework bridge.

Payment methods

  • itemPayment: how a purchase paid with an item is checked and taken. This is ox_inventory's behaviour.
  • 'bank': paying by card from the player's bank account. The shop calls it "Card" and shows the bank balance.
  • 'society:*': a society account, for example 'society:police'. Only members of that job can pay with it. Set SOCIETY_MIN_GRADE above the method to the boss grade if only bosses should spend company money. On ESX it is esx_addonaccount's society_<name>. On Qbox it is Renewed-Banking's account. With another banking script, fill in getSociety, removeSociety and addSociety at the bottom of inventory/bridge/qbx/server.lua.
  • resolvePayment: commented out. Uncomment it to choose the payment per purchase, for example cash first and the bank when the cash is short.

Every method gets (src, amount, ctx). The fields of ctx are listed under Payments. Besides canAfford, charge and refund, a method can have:

  • balance(src, ctx): the amount the player has. The shop shows it next to the method and limits the amount a player can buy by it. Without it, the shop shows only the label and the purchase decides.
  • allowed(src, ctx): return false and a reason to keep the method from this player. It drops out of the player's choices, and items priced only in it are locked with the reason.
  • prefix and suffix: how an amount reads. Both left out gives $1,000. prefix = '', suffix = ' chips' gives 1,000 chips.

In the shop, the player switches between the shop's methods with Q and E, or by clicking the method at the bottom left. The last choice is remembered for every shop that takes it.

Serial numbers and loot

  • serial(code): a new weapon's serial number. code is set for service weapons ('POL') and nil otherwise.
  • vehicleLoot and dumpsterLoot: the loot rows used when the inventory:vehicleloot and inventory:dumpsterloot convars are not set. A row is { item, min, max, chance }, with the chance in percent (80 when left out).

Worked example: a card with a fee

Cash or card works out of the box: bank is the card, and every shop without a payment offers both. This example adds a second card that charges the bank account plus a 2 % fee, and puts it in one shop.

1. Register the method at the end of the payments section of inventory/custom/server.lua, below the 'society:*' method:

-- currency = 'card': the bank account, plus a 2 % card fee
local CARD_FEE = 0.02
local function withFee(amount) return math.ceil(amount * (1 + CARD_FEE)) end

Custom.registerPaymentMethod('card', {
    label = 'Card, 2% fee',
    balance = function(src) return Framework.balance(src, 'bank') end,
    canAfford = function(src, amount) return (Framework.balance(src, 'bank') or 0) >= withFee(amount) end,
    charge = function(src, amount, ctx) return Framework.remove(src, 'bank', withFee(amount), ctx.reason) end,
    refund = function(src, amount, ctx) return Framework.add(src, 'bank', withFee(amount), ctx.reason) end,
})

amount is the purchase total, so the fee is worked out per purchase. refund gives the whole charge back if the goods cannot be placed, for example when the player's pockets are full.

2. Use it in a shop in data/shops.lua. List it in the shop's payment so players can pick it, and set currency on an item to make an exception:

corner_store = {
    name = 'Corner store',
    payment = { 'money', 'card' },
    blip = { id = 52, colour = 69, scale = 0.7 },
    inventory = {
        { name = 'water', price = 4 },
        { name = 'sandwich', price = 9 },
        { name = 'coffee', price = 5, currency = 'money' },   -- cash only
    },
    -- locations and targets as before
},

3. Restart the whole server. In the shop the player sees Card, 2% fee with their bank balance and can switch to it with Q / E. Prices show without the fee, so the label is where players learn about it. Purchase messages read $9 (Card, 2% fee).

The same method works for a licence in data/licenses.lua:

{ name = 'weapon', coords = vec3(14.20, -1106.10, 29.80), price = 4000, currency = 'card' },

Buying a licence

At a licence counter, E opens a confirmation first, so no money moves on a single key press. It names the licence, the price after any licence price modifier, the payment method and, when known, the balance: "Buy the Weapon licence for $2,000 by Card?" with "Balance: $8,500" underneath. Cash prices show what the player is carrying instead. Nothing is charged until the player presses Buy, and Cancel (or Esc) walks away.

When the purchase can't go ahead, the player is told why instead of seeing a dialog:

SituationWhat the player sees
Already has the licence"You already have the Weapon licence"
Can't afford it"You can not afford the Weapon licence ($2,000)"
The method isn't available to them (its allowed said no)"You can not pay for the Weapon licence: " followed by the method's reason
A buyLicense hook refused"You can not buy the Weapon licence right now"

The server checks everything again when the player confirms, so a balance that changed in the meantime is still caught. The new texts are the nx_licence_* keys in inventory/locales/.

To register the method from your own resource instead, call exports.nx_hud:registerPaymentMethod with the same table. The Framework helpers exist only inside inventory/custom/server.lua, so take the money through your banking script's exports there.

Inventory framework bridges

inventory/bridge/ holds one folder per framework, each with a client.lua and a server.lua: esx and qbx. The bridge that runs is the one named by the inventory:framework convar. It attaches when its framework starts and detaches when it stops.

inventory/bridge/_interface.lua is the contract. Its header lists every function a bridge can provide: player data and groups, boss checks, the framework's usable items, licences, money outside the inventory (getMoney, removeMoney, addMoney), society accounts (getSociety, removeSociety, addSociety), owned vehicles and inventory conversion. Every field is optional. A missing one falls back to ox_inventory's default, and a payment method that needs a missing money function refuses the purchase and says so in the console.

To support a framework of your own, write inventory/bridge/<name>/client.lua and server.lua against that file, set inventory:framework to <name>, and add both files to fxmanifest.lua. The manifest names each bridge file one by one, because FiveM's file patterns do not match a wildcard folder:

-- fxmanifest.lua
client_scripts {
    -- ...
    'inventory/bridge/esx/client.lua',
    'inventory/bridge/qbx/client.lua',
    'inventory/bridge/mycore/client.lua',   -- add
    -- ...
}

server_scripts {
    -- ...
    'inventory/bridge/esx/server.lua',
    'inventory/bridge/qbx/server.lua',
    'inventory/bridge/mycore/server.lua',   -- add
    -- ...
}

nxinv bridge in the server console shows which bridge is selected and whether it is attached.

Targeting framework adapters

target/bridge/ works the same way for targeting, client side only: target/bridge/_interface.lua is the contract, and esx/client.lua and qbx/client.lua answer the job, gang and item checks of target options. A new adapter is target/bridge/<name>/client.lua, added to client_scripts in fxmanifest.lua next to the other two. Without an adapter, targeting still works, but options limited to a job or gang show for everyone.

The HUD's data adapters

bridge/client/ is where the HUD reads its numbers. 00_core.lua describes the contract at the top.

  • 10_framework.lua: hunger, thirst, stress and money for Qbox, QBCore and ESX (through esx_status).
  • 20_fuel.lua: the fuel scripts Config.fuel = 'auto' looks for, in order, and how each one is read.
  • 30_seatbelt.lua: the seatbelt scripts Config.seatbelt = 'auto' follows, and how each one is read.
  • 40_ammo.lua: what the weapon pill shows for a weapon whose rounds a script keeps itself. Scripts register with registerAmmoReader.

To add a fuel script that is not on the list, add an entry to ADAPTERS and its name to ORDER in 20_fuel.lua:

['my_fuel'] = { read = export('my_fuel', 'GetFuel') },   -- the export answers 0 to 100

A seatbelt script goes into SCRIPTS in 30_seatbelt.lua:

{ res = 'my_seatbelt', read = function() return exports.my_seatbelt:IsBuckled() == true end },

If you write the fuel or seatbelt script yourself, registering it from that script with registerFuelReader or registerSeatbeltReader keeps your change out of nx_hud's files. /hud bridge in F8 prints which adapters answer.

Gameplay modules

These files carry ox_inventory's behaviour for one feature each. They are readable so you can see exactly what happens, and change it when your server needs something else:

  • 45_give.lua: giving to another player. The range is NxInventory.rules.giveRange; both players must be in the same routing bucket while strict.sameBucketGive is on.
  • 64_vehicles.lua: trunks and gloveboxes. Sizes come from data/vehicles.lua; unowned vehicles get random loot while inventory:randomloot is on.
  • 66_dumpsters.lua: dumpsters. Their size is NxInventory.rules.dumpster, and they are never saved.
  • 70_shops.lua: shops. Opening one, its stock and prices, and a purchase in order: checks, the buyItem hook, the charge and the goods.
  • 72_payments.lua: payment methods, resolvers and price modifiers. Prefer the exports and inventory/custom/server.lua; change this file only for what they cannot do.
  • client/60_shops.lua: the client side of shops, benches and licence counters: markers, targets, shopkeeper peds, blips and the openers.
  • 78_licenses.lua: buying a licence at its counter, through the payment methods and the buyLicense hook.
  • 80_commands.lua: the admin commands and their ACE groups. See Commands.
  • 88_compat.lua: convertinventory and the callbacks old scripts still call.

The client and HUD files in the same group:

  • server/vehicle.lua: lets a driver who is not the vehicle's network owner set the indicators. Only the driver may, at most ten times a second.
  • target/client/70_defaults.lua: the six built-in vehicle door options.
  • compat/ui/80_esx.lua: shows esx_notify and esx_textui calls in the HUD while those resources are stopped (Compat.esx).
  • compat/ui/81_sharedprefs.lua: shared preferences other scripts read.
  • compat/core/server/60_txadmin.lua: txAdmin announcements, direct messages and restart warnings as notifications, and warnings as an alert dialog. Off unless setr ox:txAdminNotifications 1, and each one only while txAdmin hides its own version.

Translations

nx_hud ships in ten languages: English (en), Suomi (fi), Deutsch (de), Français (fr), Português do Brasil (pt-br), Español (es), Türkçe (tr), Nederlands (nl), Italiano (it) and Polski (pl). That covers the HUD and its settings sheet, notifications, menus and dialogs, the inventory with its shops, crafting and licences, and targeting.

  • Set the server's language with Compat.locale in config/compat.lua. A setr ox:locale line in server.cfg overrides it.
  • Each player can switch their own language in the /ox_lib settings menu. setr ox:userLocales 0 turns that off, so everyone sees the server's language.
  • A line missing from a translation falls back to English.
FolderCoversShips with
compat/locales/The HUD, its settings sheet, notifications, menus and dialogsThe ten languages above. The ox_lib basics (Confirm, Cancel, the settings menu) also come in 26 more.
inventory/locales/The inventory, shops, crafting and licencesThe ten languages above
target/locales/TargetingThe ten languages above

To add a language, copy en.json to <code>.json in each folder and translate the values. Keep the keys and any %s as they are. Spotted a wrong translation, or need another language? Tell us on Discord.

NX Docs