Quick navigation

Go to

Weapons & consumables

Grenades and healing items

Configure grenade, MaxDoc inhaler and Bounce Back injector charges, equipment and regeneration with client and server Lua APIs.

5 min read1,052 wordsMarkdown
On this page7 sections

Open77.consumables controls the local player's grenade and healing hotkeys, selected item type and usable charges. On the server, the same API takes a playerId first and sends a correlated request to that player's client.

Requires a client exposing Open77.consumables, a server runtime providing the server methods, and open77_weapons resource 0.2.0. Treat the API as experimental and check compatibility before enabling consumable loadouts.

Use kind = "grenade" for the grenade hotkey or "healing" for inhalers and injectors. These are independent kits. Charges are not inventory stack sizes: Cyberpunk 2.31 consumes a rechargeable stat pool when using these items. The older Open77.weapons.giveGadget API still changes inventory membership.

Give a kit

-- server.lua: the owner relay needs network.events.
local requestId, reason = Open77.consumables.configure(playerId, "grenade", {
    record = "Items.GrenadeFragRegular",
    count = 5,
    capacity = 5,
    equipped = true,
    recharge = false,
})
if not requestId then print("Grenade request refused: " .. reason) end

Open77.consumables.configure(playerId, "healing", {
    record = "Items.FirstAidWhiffV0", -- MaxDoc inhaler
    count = 3,
    equipped = true,
    recharge = false,
})

Client scripts omit playerId:

local requestId, reason = Open77.consumables.setCount("grenade", 7)

The request ID means queued, not applied. Native code reads the actual charge pool, hotkey binding, capacity and regeneration policy before completing. Wait for completion before issuing another write to the same kit.

Methods

The signatures below are for client Lua. Prefix every argument list with playerId when calling from server Lua.

Method Behavior
configure(kind, options) Set any combination of record, count, capacity, equipped, recharge.
setType(kind, record) Change type, preserving count and equipped status.
setCount(kind, count) Set usable charges; zero leaves the type selected with no uses available.
equip(kind, record?) Bind the selected type, or supply a new type.
unequip(kind) Clear the hotkey and equipment slots, preserving the selected type and charges.
setRecharge(kind, enabled) Enable or disable native regeneration.
snapshot(kind) Request a fresh asynchronous readback.
get(kind) Read the latest cached state synchronously, or nil, reason.
reset(kind) Remove this API's capacity modifiers and restore its captured regeneration policy.

count is an integer from 0 through 999; capacity from 1 through 999. An explicit capacity smaller than the count is rejected. Without a capacity, a write grows the capacity if needed to fit the requested count. Omitted values preserve current state. The first managed write defaults to recharge=false; later partial writes preserve that policy. An initial kit should specify its type, count and equipped status explicitly.

Reset preserves the selected item and clamps excess charges to the restored native capacity. It does not remove unrelated inventory or stat modifiers. Controls are scoped to the current player body/session and are released on disconnect or body replacement. Apply the desired kit from your game mode's spawn lifecycle when creating a new body. This API is not persistent inventory storage. A failed write reports the observed state; it is not a transaction that rolls back every intermediate equipment change.

Types

Types must resolve to an actual item with the matching equipment area and native class. A syntactically valid name alone is insufficient. Arbitrary food, drinks and weapons are rejected.

Kind Example record Native type
grenade Items.GrenadeFragRegular Frag grenade / QuickSlot
healing Items.FirstAidWhiffV0 MaxDoc / inhaler
healing Items.BonesMcCoy70V1 Bounce Back / injector

Use MaxDoc for inhalers and Bounce Back for injectors. For other grenade types, consult the weapon catalogue. A record must match the requested kind; inventory stack size does not determine usable throws.

Readback and completion

get returns a table with kind, record, tweakDbId, count, capacity, equipped, recharge, managed, sequence, reportedAgeMs and source. source is native_readback on the client and owner_report on the server. The age measures time since the last observation/report, not a heartbeat. Use snapshot when a fresh response is required.

For a pre-existing native item whose name has not been resolved, record can be empty even though tweakDbId identifies the selected item. Compare tweakDbId in that case; an empty name does not mean an empty hotkey. Types explicitly configured through this API retain the supplied record name.

local state, reason = Open77.consumables.get(playerId, "healing")
if state then
    print(state.record, state.count, state.equipped, state.reportedAgeMs)
end

-- Server completion: the standard weapon relay also carries consumable writes.
AddEventHandler("open77:weapons:completed", function(
    playerId, requestId, operation, accepted, reason, state)
    if operation ~= "consumable" then return end
    -- Match requestId against your own pending requests.
    if accepted then print("Applied", state.kind, state.count, state.equipped)
    else print("Refused", reason) end
end)

Client scripts receive native state before completion:

AddEventHandler("open77:consumables:state", function(
    requestId, kind, record, tweakDbId, count, capacity,
    equipped, recharge, managed, sequence, uses, change)
    -- Native event arguments are strings. Unsolicited updates use requestId "0".
    print(kind, tonumber(count), equipped == "true", change)
end)

Client completion retains the existing native open77:weapons:completed(requestId, operation, accepted, reason, ...) signature: accepted is the string "true" or "false". Read the preceding state event or get(kind) for consumable details. Server completion uses a boolean and a typed result table.

Observe actual uses and apply healing policy

-- server.lua
AddEventHandler("onConsumableUsed", function(
    playerId, kind, record, remaining, uses, sequence, tweakDbId)
    -- Host event arguments are strings; convert as needed.
    playerId, remaining, uses = tonumber(playerId), tonumber(remaining), tonumber(uses)
    print(playerId, "used", kind, record, "remaining", remaining)
end)

This event follows the native grenade throw/healing-use path. Changing a count through Lua does not fabricate a use. The host rejects stale reports and suppresses repeated use sequences. onConsumableUsed is a reserved host event: a resource cannot publish it (reserved_event). Each frag, incendiary or cutting grenade charge it reports also credits the open77_weapons blast relay, which lets the matching detonation (onPlayerExplosion) move nearby cars and knock down NPCs on every screen. Finite managed stock also limits vanilla free-use/refund effects; native regeneration is available with recharge=true. Perks can modify charge costs and refunds. Check the interactions used by your game mode when combining native perks with finite managed stock.

Health in Open77 is server-authoritative. A native healing animation and charge consumption do not automatically authorize server health. Your game mode decides the heal amount and checks its own inventory, cooldown and life state before calling Open77.players.heal(playerId, amount) with players.damage.apply permission. A report from the authenticated owner is still client input, not an anti-cheat proof of entitlement. Do not blindly grant health or inventory rewards from this event. Native grenade explosion damage already has its existing replication path; do not create a second explosion from the use callback.

Permissions and errors

Client reads require player.weapons.read; client writes require player.weapons.edit. Server cached reads require player.weapons.read. Server requests follow the existing weapons convention: network.events authorizes dispatch through the open77_weapons owner relay, whose client manifest grants native read/edit capabilities. The server relay does not add a separate player.weapons.edit gate. The official resource handles baseline reporting and subsequent native changes. There is no per-frame Lua polling requirement for consumers.

Immediate errors include invalid kind, record syntax, options/counts, denied permissions, missing player or unavailable API. Native completions can fail with invalid_consumable_type, no_consumable_type, consumable_unavailable, consumable_busy, consumable_readback_mismatch, consumable_reset_mismatch, player_replaced or session_ended. Retry a busy request after the player has finished using the item. A failed readback is not success even if part of the equipment change is visible. An old client returns consumables_unavailable_on_this_client instead of claiming a write succeeded.

Function reference

See the client consumables API and server consumables API for individual signatures and examples.

// For agents & LLMsthis page as Markdownllms.txtllms-full.txt

// Guides Β· Lua API

Search the docs

1,905 guides and functions indexed

Try