Armi Custom con ESX + ox_inventory

INFO

Dopo aver registrato l’arma come item (file 02), vediamo come integrarla completamente: negozio, dare via script, crafting.

ObiettivoDettagli
Cosa imparerai• Creare negozi armi con ox_inventory (data/shops.lua / RegisterShop)
• Gestire acquisti e crafting di armi custom
• Notifiche da server con ox_lib
Prerequisiti• Aver registrato un’arma in ox_inventory (file 02)
• Conoscenza base di ESX events
• ox_target installato

1. Negozio Armi con ox_inventory

ox_inventory carica i negozi da ox_inventory/data/shops.lua. Il formato reale usa name (label), inventory (item + prezzo) e targets/locations (dove aprirsi).

Esempio: Armeria

-- ox_inventory/data/shops.lua
return {
    ['weapon_shop_basic'] = {
        name = 'Armeria',
        blip = { id = 110, colour = 1, scale = 0.7 },
        targets = {
            {
                loc = vec3(250.0, -150.0, 30.0),   -- centro del negozio
                length = 1.5, width = 1.5, heading = 0,
                minZ = 29.0, maxZ = 31.0,
                distance = 2.5,
            }
        },
        inventory = {
            { name = 'WEAPON_M4A1_CUSTOM', price = 5000 },
            { name = 'ammo-rifle', price = 50, count = 100 },
            { name = 'ammo-pistol', price = 30, count = 100 },
        },
    },
 
    ['ammo_shop'] = {
        name = 'Negozio Munizioni',
        blip = { id = 110, colour = 2, scale = 0.7 },
        targets = {
            {
                loc = vec3(150.0, -200.0, 25.0),
                length = 1.5, width = 1.5, heading = 0,
                minZ = 24.0, maxZ = 26.0,
                distance = 2.5,
            }
        },
        inventory = {
            { name = 'ammo-rifle', price = 60, count = 100 },
            { name = 'ammo-pistol', price = 40, count = 100 },
            { name = 'ammo-shotgun', price = 70, count = 100 },
        },
    },
}

NOTE

Il tipo di negozio (la chiave, es. weapon_shop_basic) è quello che userai per aprirlo. Le targets sostituiscono la vecchia location: ox_target mostra il negozio come punto interagibile.

Registrare uno shop a runtime

In alternativa ai file, puoi registrare un negozio dal server della tua risorsa con l’export RegisterShop:

-- server.lua
exports.ox_inventory:RegisterShop('weapon_shop_custom', {
    name = 'Armeria Custom',
    targets = {
        { loc = vec3(250.0, -150.0, 30.0), length = 1.5, width = 1.5, heading = 0, minZ = 29.0, maxZ = 31.0, distance = 2.5 }
    },
    inventory = {
        { name = 'WEAPON_M4A1_CUSTOM', price = 5000 },
    },
})

Aprire lo shop

Sul client, per aprire un negozio registrato:

-- client.lua
exports.ox_inventory:openInventory('shop', {
    type = 'weapon_shop_basic',
})

2. Dare Arma con ESX

Comando Admin

-- server.lua
RegisterCommand('givecustomweapon', function(source, args)
    local target = tonumber(args[1]) or source
    local xPlayer = ESX.GetPlayerFromId(target)
 
    if xPlayer then
        -- ox_inventory gestisce arma + munizioni
        xPlayer.addWeapon('WEAPON_M4A1_CUSTOM', 120)
 
        -- Notifica da server con ox_lib (non TriggerClientEvent ox_lib:notify)
        exports.ox_lib:notify(source, {
            title = 'Arma Ricevuta',
            description = 'Hai ricevuto la M4A1 Tactical',
            type = 'success'
        })
    end
end, true)
 
-- Permessi admin (ACE)
add_ace resource.mia_risorsa command.givecustomweapon allow

WARNING

Per notificare da server usa exports.ox_lib:notify(source, {...}). TriggerClientEvent('ox_lib:notify', ...) non funziona: lib.notify esiste solo sul client.

Acquisto con Denaro (validazione lato server)

-- server.lua
RegisterNetEvent('myresource:buyWeapon')
AddEventHandler('myresource:buyWeapon', function()
    local xPlayer = ESX.GetPlayerFromId(source)
    local price = Config.WeaponPrice or 5000
 
    -- Mai fidarsi del client: prezzo calcolato qui, non arrivato dal client
    if xPlayer.getAccount('money').money >= price then
        xPlayer.removeAccountMoney('money', price)
        xPlayer.addWeapon('WEAPON_M4A1_CUSTOM', 120)
 
        exports.ox_lib:notify(source, {
            title = 'Acquisto Riuscito',
            description = 'Hai acquistato la M4A1 Tactical per ' .. price .. '$',
            type = 'success'
        })
    else
        exports.ox_lib:notify(source, {
            title = 'Saldo Insufficiente',
            description = 'Ti servono ' .. price .. '$',
            type = 'error'
        })
    end
end)

Con ox_target (punto vendita)

-- client.lua
CreateThread(function()
    exports.ox_target:addBoxZone({
        coords = vector3(250.0, -150.0, 29.5),
        size = vec3(1.5, 1.5, 2.0),
        rotation = 0,
        debug = Config.Debug,
        options = {
            {
                name = 'buy_custom_weapon',
                icon = 'fa-solid fa-gun',
                label = 'Compra M4A1 Tactical',
                onSelect = function()
                    exports.ox_inventory:openInventory('shop', { type = 'weapon_shop_basic' })
                end,
                canInteract = function()
                    return true
                end
            }
        }
    })
end)

3. Crafting con ox_inventory

Il crafting nativo si definisce in ox_inventory/data/crafting.lua con ricette raggruppate. Il formato reale usa ingredients (tabella nome → quantità), duration, count e points/zones.

Configurazione

-- ox_inventory/data/crafting.lua
return {
    {
        name = 'crafting_workbench',   -- nome del gruppo ricette
        items = {
            {
                name = 'ammo-rifle',   -- item che si crea
                ingredients = {
                    gunpowder = 2,
                    scrapmetal = 5,
                },
                duration = 5000,       -- 5 secondi
                count = 30,            -- quanti se ne ottengono
            },
 
            {
                name = 'WEAPON_M4A1_CUSTOM',
                ingredients = {
                    scrapmetal = 20,
                    gunpowder = 5,
                    plastic = 10,
                    electronic_parts = 3,
                },
                duration = 15000,      -- 15 secondi
                count = 1,
            },
        },
        points = {                     -- punti di crafting (distanza di interazione)
            vec3(300.0, -100.0, 29.5),
        },
        -- In alternativa: zones = { { coords = vec3(...), size = vec3(...), distance = 1.5, rotation = 0 } },
        blip = { id = 566, colour = 31, scale = 0.8 },
    },
}

Aprire il banco da lavoro

-- client.lua
CreateThread(function()
    exports.ox_target:addBoxZone({
        coords = vector3(300.0, -100.0, 29.5),
        size = vec3(2.0, 2.0, 2.0),
        rotation = 0,
        debug = Config.Debug,
        options = {
            {
                name = 'open_crafting',
                label = 'Apri Banco da Lavoro',
                icon = 'fa-solid fa-screwdriver-wrench',
                onSelect = function()
                    exports.ox_inventory:openInventory('craft', {
                        name = 'crafting_workbench',
                    })
                end
            }
        }
    })
end)

4. Rimozione Arma alla Morte

INFO

ox_inventory gestisce automaticamente la perdita armi alla morte se configurato. Puoi personalizzare il comportamento ascoltando gli eventi ox_inventory:

-- server.lua
-- Impedisci la perdita di armi custom alla morte
AddEventHandler('ox_inventory:dropItem', function(inv, item, count, metadata)
    if item.name == 'WEAPON_M4A1_CUSTOM' then
        return false  -- Non lasciare cadere
    end
end)

NOTE

Le firme esatte degli eventi possono cambiare tra versioni: verifica i nomi nella versione installata o nella documentazione ufficiale prima di affidarti a questi handler in produzione.


TIP

Prossimo passo: 04 - Veicoli Custom con ESX


5. Armi Melee con ESX

NOTE

Le armi melee seguono le stesse regole delle armi da fuoco per quanto riguarda l’integrazione ESX, ma con alcune differenze importanti: non usano munizioni, ammo è sempre 1, e non hanno componenti. Per la guida completa, vedi Armi Melee (Spade).

Registrazione Item

-- ox_inventory/data/weapons.lua
Weapons = {
    ['WEAPON_LONGSWORD'] = {
        label = 'Longsword',
        weight = 1500,
        durability = 0.1,
        -- nessun ammoname: e' melee
    },
}

Dare con ESX (nessuna munizione)

-- Le melee non hanno munizioni — il secondo parametro e' ignorato
xPlayer.addWeapon('WEAPON_LONGSWORD', 1)

Torna alla: Indice Generale