Skip to content

Inventory (wf_inventory) ​

An ox_inventory-style inventory where the server decides everything. The client only sends intentions ("move my slot 3 to slot 7 of the open stash") and never an inventory id of its choice, a count the server did not check, or item metadata.

Items ​

Items are data, in wf_inventory/data/items.json:

json
"water": {
  "label": "Bouteille d'eau",
  "weight": 500,
  "stack": true,
  "image": "water.png",
  "description": "Désaltère.",
  "use": { "consume": 1 }
},
"medikit": { "label": "Trousse de secours", "weight": 1200, "stack": true, "stackMax": 5 },
"phone":   { "label": "Téléphone", "weight": 190, "stack": false }
FieldMeaning
labelname shown to players
weightgrams per unit (integer)
stackfalse = one per slot
stackMaxlargest stack (default: unlimited)
imagefile name for a future HTML interface
usemakes the item usable; consume = units removed on use; status = needs restored, e.g. { "hunger": 40 }
type"weapon" for weapons (with weapon, ammo, maxAmmo), see The RP character in game

Item names are lower-case letters, digits and _, up to 32 characters. An item removed from items.json is skipped (and logged) when an inventory is loaded — it is never invented back.

Metadata ​

Each stack can carry metadata (a JSON object up to 4 KB): serial numbers, quality, the ID card holder… Two stacks only merge when their metadata is equal (key order does not matter). metadata.weight overrides the unit weight.

lua
exports.wf_inventory:AddItem(source, "lockpick", 1, { quality = 80 })
exports.wf_inventory:GetItemCount(source, "lockpick", { quality = 80 })  -- 1

Inventory types ​

TypeIdPersistentAccess
playerchar:<characterId>yesthe character; moderators with wf.inventory.open_any
trunktrunk:<PLATE>yesanyone (v1: the server does not know vehicle positions yet)
stashstash:<name>yesas registered: owner, job/group grade, distance
dropdrop:<n>no, expires after expireMinutesanyone within interactDistance

Access is checked again on every action: walking away from a stash or losing the job closes it.

lua
-- a police armory, grade 1 or more, usable within 3 m of its position
exports.wf_inventory:RegisterStash({
  id = "police_armory", label = "Armurerie", slots = 50, maxWeight = 200000,
  groups = { police = 1 }, coords = { x = -3110.0, y = 5530.0, z = 956.0 },
})
exports.wf_inventory:OpenInventory(source, "stash", "police_armory")

Rules ​

  • Weight: the sum of weight × count may not exceed the inventory's maxWeight (too_heavy).
  • Slots: adding fills existing stacks first, then free slots in order (inventory_full).
  • Moving (wf_inventory:move): to an empty slot = move (part of a stack allowed); same stackable item and metadata = merge (up to stackMax); otherwise = swap, whole stacks only.
  • Giving (wf_inventory:give): only to a player with a loaded character within interactDistance, using the server's positions. Unknown position = refused. Without a target, the server picks the nearest player.
  • Dropping (wf_inventory:drop): creates a ground inventory at the player's position, or adds to one within dropMergeDistance.
  • Using (wf_inventory:use): calls the script registered with RegisterUsableItem, fires wf_inventory:usedItem, then removes use.consume units unless the script returned false. One use per useCooldownMs.

All of these are covered by game-server/tests/wframework/inventory.test.js.

Usable items ​

lua
exports.wf_inventory:RegisterUsableItem("bandage", function(source, item, slot)
  -- heal the player… return false to keep the item
end)

AddEventHandler("wf_inventory:usedItem", function(source, name, slot, data)
  print(source, "used", name)
end)

In game (v1) ​

The v1 interface is a keyboard list drawn with the client's native UI (no HTML):

KeyAction
F2open / close
Up / Downselect
Left / Rightyour inventory / the open one (trunk, stash, ground)
Enteruse
Spacemove the whole stack to the other side
Ggive 1 to the nearest player
Xdrop 1 on the ground
Eopen what is on the ground nearby

Toward an HTML interface (NUI-like) ​

A real ox_inventory-style interface (grid, drag and drop, item images) needs things the client does not have yet:

  1. An embedded web view drawn over the game: e.g. a CEF or WebView2 off-screen browser rendered to a texture and shown through the ScriptHook overlay (scripthook_ovl.cpp, ImGui on the game's swap chain) or as an SH_W_IMAGE widget texture.
  2. Input routing: mouse cursor and keyboard focus given to the page while it is open (the native UI's ShUiFocus already captures the keyboard; the mouse needs the same).
  3. A message bridge like FiveM's SendNUIMessage / RegisterNUICallback between the Lua client scripts and the page's JavaScript.
  4. Resource files served to the client (files in resource.json) and loaded from the local cache, as the launcher already does for the loading screen.

The server side does not change: the page sends the same wf_inventory:* intentions and receives the same views. Item image fields are already there for it.

WildM is an independent community project. It is not affiliated with or endorsed by Ubisoft. Ghost Recon Wildlands is a trademark of Ubisoft Entertainment.