client runtime · Lua API
Open77.weapons
Runs inside the game process. Reads and presentation are local; anything that changes shared state has to go through the server.
On this page20 sections
activate
Open77.weapons.activate(target, [options])Alias of Open77.weapons.setActive.
Requires player.weapons.edit; see setActive for template placement and asynchronous completion semantics.
Parameters 2
| Name | Type | Use | Default |
|---|---|---|---|
target | integer 1..3|string | required | — |
options | table | optional | — |
Returns
- request id, or nil
- reason
Example
-- Draw slot 1 when the round starts
RegisterNetEvent("arena:roundStarted", function()
Open77.weapons.activate(1)
end)Registered in ResourceHost.cpp as LuaWeaponSetActive (line 24945).
all
Open77.weapons.all()Alias of Open77.weapons.snapshot.
Requires player.weapons.read; results arrive asynchronously as slot-state events followed by completion.
Returns
- request id, or nil
- reason
Example
-- Same as snapshot: ask for every slot's verified state
Open77.weapons.all()
AddEventHandler("open77:weapons:state", function(requestId, slot, record)
print("slot", slot, record ~= "" and record or "empty")
end)Registered in ResourceHost.cpp as LuaWeaponSnapshot (line 24955).
applyBlast
Open77.weapons.applyBlast(blast)Applies a server-relayed weapon blast to the vehicles this client simulates.
Client-only; requires player.weapons.edit. Only the client that simulates a vehicle can move it: an impulse on another client's observer replica is overwritten by the owner's motion stream. A server resource that accepted a blast therefore finds the vehicles it reaches (Open77.vehicles.nearby) and their physics owner (Open77.vehicles.owner), and sends each owner the centre, its own copy of the approved profile and that owner's vehicle ids; the owner's client calls this. Accepts {x, y, z, radius (0-60, above zero), push (0-40), lift (0-40), falloff (0-4, default 1), vehicles (0-64 positive server vehicle ids), characters (optional boolean)}; at least one vehicle or characters = true is required, anything else returns nil, invalid_weapon_blast. With characters, every living, unseated network NPC this client presents inside the radius is also knocked down once with the native hit-reaction knockdown (the one remote player proxies play for a car impact), which throws the body a few metres and lets it get up; a copy whose native health has not been projected yet is skipped. The server sends the blast to every nearby player, so the authority copy and each observer copy fall together, and an observer copy that settles more than 1.5 m from its authority walks back to it once the reaction is over. A forced ragdoll with an impulse was measured to do nothing on a living network NPC and is not used. The blast is queued and applied on the game thread to each listed vehicle that this client simulates in its current authority epoch, from this client's own pose, with the same mass-scaled impulse and (1 - distance / radius)^falloff weight as a local blast. A listed vehicle whose authority grant is still in flight is retried for 0.75 s; one outside the radius on this client's pose is skipped. Never moves a vehicle another client simulates, a frozen vehicle or a character. Returns true when queued, or nil/reason (invalid_weapon_blast, weapon_blast_queue_full with 16 blasts pending). tuning() reports relayedBlasts, relayedImpulses, relayedExpired, relayedRefused and relayedCharacters, including when this resource owns no tuning profile.
Parameters 1
| Name | Type | Use | Default |
|---|---|---|---|
blast | table | required | — |
Returns
- true or nil
- reason on failure
Example
RegisterNetEvent("myblast:owned", function(value)
local ok, reason = Open77.weapons.applyBlast({x = value.x, y = value.y, z = value.z,
radius = value.radius, push = value.push, lift = value.lift, vehicles = value.vehicles})
if not ok then print("relayed blast refused: " .. tostring(reason)) end
end)Blast relay guide · Registered in ResourceHost.cpp as LuaWeaponApplyBlast (line 24953).
assign
Open77.weapons.assign(record, slot, [options])Assigns an exact TweakDB weapon template to a standard loadout slot.
Requires player.weapons.edit. slot is 1..3. Options are active (default false) and addToInventory (default true). The return value only confirms that the validated request was queued; listen for open77:weapons:completed for the state verified by REDscript.
Parameters 3
| Name | Type | Use | Default |
|---|---|---|---|
record | string | required | — |
slot | integer 1..3 | required | — |
options | table | optional | — |
Returns
- request id, or nil
- reason
Example
-- A pistol in slot 1, drawn immediately
local request, reason = Open77.weapons.assign("Items.Preset_Lexington_Default", 1, { active = true })
if not request then print("assign refused: " .. reason) endRegistered in ResourceHost.cpp as LuaWeaponAssign (line 24943).
clearTuning
Open77.weapons.clearTuning()Disables this resource's weapon tuning and restores its modifiers.
Requires a compatible development client exposing native weapon tuning with inventory-instance restoration. Client-only; requires player.weapons.edit. Disables the calling resource's profile and removes only the modifier handles it owns. Returns true when accepted, or nil/reason on failure; it is a successful no-op when this resource owns no profile and never clears another resource's tuning. If a holstered weapon's StatsObject is temporarily absent, exact handles remain queued against its inventory identity, even after the engine entity disappears. Drawing that same inventory item allows cleanup on its current entity; a different copy of the same record is not the same item. Read tuning().status and pendingModifiers to distinguish idle from restoring and wait for zero pending modifiers before reporting completion. Does not remove granted weapons, ammunition or components. Resource stop and failure also release the profile and retain deferred cleanup. The queue is bounded to 32 pending bindings; saturation refuses further changes with restore_queue_full.
Returns
- true or nil
- reason on failure
Example
-- Clear while this weapon is drawn, before switching weapons.
local ok, reason = Open77.weapons.clearTuning()
if not ok then print(reason) end
local state = Open77.weapons.tuning()
if state then print(state.status, state.pendingModifiers) endWeapon customization & workshop · Registered in ResourceHost.cpp as LuaWeaponClearTuning (line 24951).
components
Open77.weapons.components(slot)Lists every attachment slot of the weapon in a loadout slot, taken or empty.
Requires player.weapons.read. Native-parity E3. Read off the live item -- GetUsedSlotsOnItem / GetEmptySlotsOnItem -- so the answer is the blueprint of the weapon actually there, not a table. One open77:weapons:part row per slot arrives before the completion: (requestId, slot, attachmentSlot, attachmentSlotId, taken, base, record, tweakDbId), where base == "true" marks the weapon's own receiver, barrel or magazine and record names the installed part when taken. attachmentSlot is spelled from the 2.31 weapon-part vocabulary when the shipping build's reverse name table cannot; attachmentSlotId always travels. Refused by name: invalid_weapon_slot now, weapon_slot_empty from the bridge. See Weapon Lua API.
Parameters 1
| Name | Type | Use | Default |
|---|---|---|---|
slot | integer 1..3 | required | — |
Returns
- requestId, or nil
- reason
Example
-- Which mod slots does the rifle in slot 1 have, and what is in them?
local requestId = Open77.weapons.components(1)
AddEventHandler("open77:weapons:part", function(id, slot, attachmentSlot, _, taken, base, record)
if tostring(id) == tostring(requestId) and base ~= "true" then
print(attachmentSlot, taken == "true" and record or "empty")
end
end)Registered in ResourceHost.cpp as LuaWeaponComponents (line 24960).
gadgets
Open77.weapons.gadgets()Lists the quick slots: which gadget is in each, how many, and which one is on the throw hotkey.
Requires player.weapons.read. Native-parity E4. One open77:weapons:gadget row per quick slot arrives before the completion: (requestId, quickSlot, record, tweakDbId, quantity, active, difference); an empty slot has an empty record and quantity -1. The same event also carries the consume half without being asked: a row with requestId == "0" is the bridge's inventory listener reporting that the engine changed a gadget stack -- difference negative for a throw, quantity the count left. The bundled open77_weapons resource turns those into open77:weapons:gadgetConsumed(record, tweakDbId, remaining) locally and into the server's onGadgetConsumed. See Weapon Lua API.
Returns
- requestId, or nil
- reason
Example
-- Print the quick slots, and watch grenades being thrown
Open77.weapons.gadgets()
AddEventHandler("open77:weapons:gadget", function(id, slot, record, _, quantity, active, difference)
if tostring(id) == "0" then print("stack changed", record, difference, "left", quantity)
else print("quick slot", slot, record, quantity, active) end
end)Registered in ResourceHost.cpp as LuaWeaponGadgets (line 24963).
giveGadget
Open77.weapons.giveGadget(record, [count], [options])Grants a counted stack of grenades (or any quick-slot gadget) and puts it on the throw hotkey.
Requires player.weapons.edit. The record must be a Gadget_Record -- every 2.31 grenade is one (Items.GrenadeFragRegular, Items.GrenadeEMPRegular, Items.Preset_Grenade_Smoke_Default, ...; the category = grenade rows of docs/generated/weapons-2.31.csv); a weapon answers template_is_not_gadget. count is 1..999 units added to the stack. Unless options.equip is false, the stack is placed in quick slot 1 (GameplayEquipRequest) and assigned to EHotkey.RB (HotkeyAssignmentRequest) and the completion is accepted only when both took. The completion carries result.gadget = { slot, record, tweakDbId, quantity, active } after one open77:weapons:gadget row. Refused by name: invalid_gadget_template, template_is_not_gadget, invalid_gadget_count now; unsupported_gadget_area, item_creation_failed, equip_rejected, hotkey_rejected from the bridge. See Weapon Lua API.
Parameters 3
| Name | Type | Use | Default |
|---|---|---|---|
record | string a grenade / gadget record | required | — |
count | integer 1..999, default 1 | optional | — |
options | table `{ equip = true }` | optional | — |
Returns
- requestId, or nil
- reason
Example
-- Three frag grenades, equipped and on the throw key
local requestId, reason = Open77.weapons.giveGadget("Items.GrenadeFragRegular", 3)
print(requestId or reason)Registered in ResourceHost.cpp as LuaWeaponGiveGadget (line 24961).
holster
Open77.weapons.holster()Holsters the currently drawn right-hand weapon.
Requires player.weapons.edit. Loadout assignments and the selected equipment-area item are unchanged; completion reports whether the right-hand slot became empty.
Returns
- request id, or nil
- reason
Example
-- Entering the shop holsters the weapon
AddEventHandler("shop:entered", function()
Open77.weapons.holster()
end)Registered in ResourceHost.cpp as LuaWeaponHolster (line 24948).
remove
Open77.weapons.remove(slot)Removes the weapon assigned to one standard slot.
Requires player.weapons.edit. This unequips the item but does not delete it from inventory. The empty slot is verified asynchronously.
Parameters 1
| Name | Type | Use | Default |
|---|---|---|---|
slot | integer 1..3 | required | — |
Returns
- request id, or nil
- reason
Example
-- The round is over: empty slot 1
Open77.weapons.remove(1)Registered in ResourceHost.cpp as LuaWeaponRemove (line 24946).
removeComponent
Open77.weapons.removeComponent(slot, attachmentSlot)Empties one attachment slot of the weapon in a loadout slot; the part goes back to the inventory.
Requires player.weapons.edit. Native-parity E3. Queues the vanilla RemoveItemPart request for the named AttachmentSlots.* record and reports once the slot is visibly empty. A slot that is already empty is accepted rather than refused. The weapon's own base parts -- receiver, barrel, magazine, the rows open77:weapons:part flags with base == "true" -- are refused as base_part_slot: removing one breaks the weapon. Immediate refusals: invalid_weapon_slot, invalid_attachment_slot; from the bridge: weapon_slot_empty, slot_not_on_weapon, remove_rejected. See Weapon Lua API.
Parameters 2
| Name | Type | Use | Default |
|---|---|---|---|
slot | integer 1..3 | required | — |
attachmentSlot | string an `AttachmentSlots.*` record | required | — |
Returns
- requestId, or nil
- reason
Example
-- Take the silencer off the pistol in slot 2
local requestId, reason = Open77.weapons.removeComponent(2, "AttachmentSlots.PowerModule")
print(requestId or reason)Registered in ResourceHost.cpp as LuaWeaponRemoveComponent (line 24959).
setActive
Open77.weapons.setActive(target, [options])Selects and draws a standard weapon by slot or TweakDB template.
Requires player.weapons.edit. A numeric target must already contain a weapon. A template target reuses an assigned instance or equips it first; options accept fallback slot 1..3 and addToInventory (default true). Completion is asynchronous through open77:weapons:completed.
Parameters 2
| Name | Type | Use | Default |
|---|---|---|---|
target | integer 1..3|string | required | — |
options | table | optional | — |
Returns
- request id, or nil
- reason
Example
-- The 2 key draws whatever is in slot 2
RegisterKeyMapping("weapon_slot_2", "Weapon slot 2", "2", function()
Open77.weapons.setActive(2)
end)Registered in ResourceHost.cpp as LuaWeaponSetActive (line 24944).
setAmmo
Open77.weapons.setAmmo(slot, amounts)Sets exact spare and/or loaded ammunition for a weapon slot.
Requires player.weapons.edit. amounts accepts integer reserve and magazine counts plus optional activate. At least one count is required; omitted counts are preserved. The operation verifies the shared ammo total, spare reserve, magazine, and live capacity asynchronously through open77:weapons:completed.
Parameters 2
| Name | Type | Use | Default |
|---|---|---|---|
slot | integer 1..3 | required | — |
amounts | table { reserve?, magazine?, activate? } | required | — |
Returns
- request id, or nil
- reason
Example
-- Refill the pistol: full magazine plus 60 spare rounds
Open77.weapons.setAmmo(1, { magazine = 12, reserve = 60 })Registered in ResourceHost.cpp as LuaWeaponSetAmmo (line 24949).
setComponent
Open77.weapons.setComponent(slot, part, [options])Installs a part (scope, muzzle, generic mod) into an attachment slot of the weapon in a loadout slot.
Requires player.weapons.edit. Native-parity E3. A part is an Items.* record tagged itemPart (Items.w_att_scope_short_01, Items.w_silencer_01, Items.w_muzzle_brake_01, ...; 928 on 2.31, see docs/generated/weapon-parts-2.31.csv) and the slot an AttachmentSlots.* record (Scope, PowerModule for muzzles, Power_Handgun_WeaponMod1/2 for generic mods). The bridge script installs it through the vanilla ItemModificationSystem -- the same request the inventory screen queues -- so the part is real: visible on the weapon, kept across holstering, listed by the vanilla screen. Without options.attachmentSlot the first of the part's own placement slots this weapon declares is used, empty ones first. A part the player does not own is created for the install; a part already in the slot is swapped out and deleted, as the vanilla screen does. The returned request id proves validation and queuing only; the outcome arrives on open77:weapons:completed with result.part, preceded by one open77:weapons:part row for the slot. Refused by name: invalid_weapon_slot, invalid_part_template, template_is_not_part, invalid_attachment_slot now; weapon_slot_empty, slot_not_on_weapon, part_does_not_fit_weapon, part_does_not_fit_slot, base_part_slot, install_rejected from the bridge. Other players do not see the part on the owner's proxy: the proxy weapon is rebuilt from the snapshot's ItemID, which does not encode parts. See Weapon Lua API.
Parameters 3
| Name | Type | Use | Default |
|---|---|---|---|
slot | integer 1..3 | required | — |
part | string an `Items.*` part record | required | — |
options | table `{ attachmentSlot = "AttachmentSlots.Scope" }` | optional | — |
Returns
- requestId, or nil
- reason
Example
-- A short scope on the rifle in slot 1; the verified outcome arrives later
local requestId, reason = Open77.weapons.setComponent(1, "Items.w_att_scope_short_01",
{ attachmentSlot = "AttachmentSlots.Scope" })
if not requestId then print("refused:", reason) end
AddEventHandler("open77:weapons:completed", function(id, operation, accepted, why)
if tostring(id) == tostring(requestId) then print(operation, accepted, why) end
end)Registered in ResourceHost.cpp as LuaWeaponSetComponent (line 24958).
setTuning
Open77.weapons.setTuning(record, options)Configures a resource-owned tuning profile for the local held weapon.
Requires a compatible development client exposing native weapon tuning with inventory-instance restoration. Client-only; requires player.weapons.edit. Accepts an exact Items.* record and a complete options table: omitted fields reset to their defaults. Supports reloadSpeed, fireRate, recoil, spread, damage, magazineCapacity, projectilesPerShot, aimSpeed, chargeSpeed, smartProjectileSpeed and the blastRadius/blastPush/blastLift/blastFalloff/blastCooldown weapon-impact controls. Unknown keys, nonnumeric or nonfinite values and out-of-range multipliers are refused. Returns true when accepted, or nil/reason; read tuning() to distinguish active, waiting_for_weapon and waiting_for_restore. Only one resource owns tuning at a time; another receives weapon_tuning_owned_by_another_resource. Does not equip a weapon, rewrite shared TweakDB records, change projectile types or override server damage policy. Supported inputs are bound-weapon entity hits, actual native impact positions from GameEffectExecutor_StimOnHit, and tracked-projectile collision/explosion callbacks. No synthetic crosshair raycast is substituted. Bare-terrain Comrade coverage remains unverified; generic hitscan ground detection and grenades without the bound weapon object are not promised. Vehicle impulses require a streamed, unfrozen vehicle whose current physics owner is this client. The local open77:weaponBlast(owner,record,sequence,x,y,z) event uses string arguments after cooldown acceptance; it grants no network character authority. Resources request character launches separately through server policy. Smart projectile speed applies only to smart weapons. Clearing, switching or stopping the resource removes its own modifiers, with deferred restoration when the engine temporarily retires a holstered weapon's stats.
Parameters 2
| Name | Type | Use | Default |
|---|---|---|---|
record | string | required | — |
options | table | required | — |
Returns
- true or nil
- reason on failure
Example
local ok, reason = Open77.weapons.setTuning("Items.Preset_Lexington_Default", { reloadSpeed = 2, recoil = 0.5 })
if not ok then print(reason) endWeapon customization & workshop · Registered in ResourceHost.cpp as LuaWeaponSetTuning (line 24950).
slots
Open77.weapons.slots()Lists the three supported standard weapon slots.
Requires player.weapons.read. Slot numbers are one-based and deliberately exclude QuickSlot grenades, WeaponHeavy items, and ArmsCW cyberware.
Returns
- { 1, 2, 3 }, or nil
- reason
Example
-- Print the standard slots this API manages
for _, slot in ipairs(Open77.weapons.slots() or {}) do print("slot", slot) endRegistered in ResourceHost.cpp as LuaWeaponSlots (line 24942).
snapshot
Open77.weapons.snapshot()Requests the verified state of every standard weapon slot.
Requires player.weapons.read. Emits one open77:weapons:state event per available slot and then open77:weapons:completed. TweakDB IDs remain opaque hexadecimal strings and are the presence authority; record may be empty for items equipped outside this API because shipping builds do not retain every reverse TweakDB name.
Returns
- request id, or nil
- reason
Example
-- Ask for the verified state, then read it from the events
Open77.weapons.snapshot()
AddEventHandler("open77:weapons:state", function(requestId, slot, record, tweakDbId, active, drawn)
print(slot, record, active, drawn)
end)Registered in ResourceHost.cpp as LuaWeaponSnapshot (line 24954).
takeGadget
Open77.weapons.takeGadget([record], [count])Takes grenades back: some units of a record, or every unit of the gadget on the throw hotkey.
Requires player.weapons.edit. Native-parity E4. With no record the active gadget (GetActiveGadget) is taken; with no count every unit is. A quick slot left with nothing in it is unequipped so the next gadgets() does not name a stack of zero. The completion carries result.gadget with the remaining quantity. Refused by name: invalid_gadget_template, template_is_not_gadget, invalid_gadget_count now; gadget_not_owned, no_active_gadget, inventory_update_rejected from the bridge. See Weapon Lua API.
Parameters 2
| Name | Type | Use | Default |
|---|---|---|---|
record | string a grenade / gadget record; nil for the active gadget | optional | — |
count | integer 1..999, or -1 / nil for every unit | optional | — |
Returns
- requestId, or nil
- reason
Example
-- Confiscate whatever is on the throw key
local requestId, reason = Open77.weapons.takeGadget()
print(requestId or reason)Registered in ResourceHost.cpp as LuaWeaponTakeGadget (line 24962).
tuning
Open77.weapons.tuning()Reads this resource's native weapon tuning state and diagnostics.
Requires a compatible development client exposing native weapon tuning with inventory-instance restoration. Client-only; requires player.weapons.read. Returns a table or nil/reason. status distinguishes idle, waiting_for_weapon, active, restoring, waiting_for_restore and native failure states; active reports whether the matching weapon is bound. An owned profile includes record, opaque string weaponEntity, modifiers, native stats and cumulative blasts, impulsesQueued, failures and foreignSkipped counters. projectileContacts counts supported tracked-projectile contacts only; trackedProjectiles is the number still being tracked. Native entity hits and GameEffectExecutor_StimOnHit do not increment projectileContacts, so an accepted blast can leave it at zero. Neither counter establishes a character launch or observed movement. pendingModifiers counts this resource's deferred cleanup. Idle/restoring results can omit bound-weapon fields. Counters describe queued work rather than measured movement, and a native statistic does not prove that every weapon's animation or projectile logic consumes it. Treat weaponEntity as an opaque string, never as a Lua number.
Returns
- state table or nil
- reason on failure
Example
local state, reason = Open77.weapons.tuning()
if state then
print(state.status, state.active, state.impulsesQueued or 0)
else
print(reason)
endWeapon customization & workshop · Registered in ResourceHost.cpp as LuaWeaponTuning (line 24952).
unequip
Open77.weapons.unequip(slot)Alias of Open77.weapons.remove.
Requires player.weapons.edit; the inventory instance is retained.
Parameters 1
| Name | Type | Use | Default |
|---|---|---|---|
slot | integer 1..3 | required | — |
Returns
- request id, or nil
- reason
Example
-- Confiscate slot 3 (alias of remove)
Open77.weapons.unequip(3)Registered in ResourceHost.cpp as LuaWeaponRemove (line 24947).
// For agents & LLMsthis page as Markdownllms.txtllms-full.txt
