Esempio Completo: Risorsa Arma Custom con ESX + OX

INFO

Risorsa completa che integra un’arma custom (M4A1 Tactical) con ESX, ox_inventory, ox_target e ox_lib.

ObiettivoDettagli
Cosa imparerai• Struttura completa di una risorsa FiveM
• Integrare arma custom con ESX + OX stack
• Configurare shop, acquisti e notifiche end-to-end
Prerequisiti• Aver completato le guide 02-07
• Un’arma custom pronta con file .ydr/.ytd
• ox_lib, ox_inventory, ox_target installati

Struttura

weapon_m4a1/
├── fxmanifest.lua
├── config.lua
├── client.lua
├── server.lua
└── stream/                ← File dell'arma custom
    ├── w_m4a1.ydr
    ├── w_m4a1_fp.ydr
    ├── w_m4a1.ytd
    ├── w_m4a1.weaponlayouts.meta
    ├── w_m4a1.weaponanimations.meta
    └── w_m4a1.weaponpickups.meta

NOTE

Gli item (arma + munizioni) si definiscono in ox_inventory/data/weapons.lua, non in questa risorsa. Vedi sezione “items.lua” più sotto.


fxmanifest.lua

-- fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
 
author 'TuoNome'
description 'M4A1 Tactical - ESX + OX Integration'
version '1.0.0'
 
shared_scripts {
    '@ox_lib/init.lua',
    '@es_extended/imports.lua',
    'config.lua'
}
 
client_scripts {
    'client.lua'
}
 
server_scripts {
    '@oxmysql/lib/MySQL.lua',
    'server.lua'
}
 
files {
    'stream/*.meta'
}
 
data_file 'WEAPON_INFO_FILE' 'stream/*.weaponlayouts.meta'
data_file 'WEAPON_ANIMATIONS_FILE' 'stream/*.weaponanimations.meta'
data_file 'WEAPON_PICKUP_FILE' 'stream/*.weaponpickups.meta'

config.lua

-- config.lua
Config = Config or {}
 
Config.Debug = false
 
-- Arma
Config.WeaponName = 'WEAPON_M4A1_CUSTOM'
Config.WeaponLabel = 'M4A1 Tactical'
Config.WeaponHash = 'w_m4a1'
 
-- Prezzi
Config.WeaponPrice = 5000
Config.AmmoPrice = 50        -- Per 30 proiettili
 
-- Munizioni
Config.AmmoType = 'ammo-rifle'
Config.DefaultAmmo = 120
 
-- Negozio
Config.ShopCoords = vector3(250.0, -150.0, 29.5)

items.lua

WARNING

In ox_inventory armi e munizioni non si registrano con export a runtime (RegisterWeapon/RegisterItem non esistono). Si definiscono nei file ox_inventory/data/weapons.lua e ox_inventory/data/items.lua. Questo file items.lua qui sotto non serve: l’esempio reale è la voce da aggiungere nei data file.

-- ox_inventory/data/weapons.lua  (aggiungi alla tabella Weapons)
['WEAPON_M4A1_CUSTOM'] = {
    label = 'M4A1 Tactical',
    weight = 2500,
    durability = 0.02,
    ammoname = 'ammo-rifle',
},
 
-- ox_inventory/data/weapons.lua  (aggiungi alla tabella Ammo)
['ammo-rifle'] = {
    label = '5.56x45',
    weight = 4,
},

NOTE

Se preferisci non toccare i file di ox_inventory (che gli update sovrascrivono), tieni un backup o usa un modulo di merge. L’importante è che gli item siano nel data/ prima che la risorsa parta.

E il file items.lua nella tua risorsa lo puoi eliminare dalla struttura e dal server_scripts: il ruolo è coperto dai data file di ox_inventory.


client.lua

-- client.lua
local ESX = exports['es_extended']:getSharedObject()
 
-- Crea lo shop con ox_target
CreateThread(function()
    exports.ox_target:addBoxZone({
        coords = Config.ShopCoords,
        size = vec3(2.0, 2.0, 2.0),
        rotation = 0,
        debug = Config.Debug,
        options = {
            {
                name = 'open_weapon_shop',
                icon = 'fa-solid fa-gun',
                label = 'Apriti Armeria',
                onSelect = function()
                    openWeaponShop()
                end,
                distance = 2.5,
            }
        }
    })
end)
 
function openWeaponShop()
    lib.registerContext({
        id = 'weapon_shop',
        title = 'Armeria',
        options = {
            {
                title = Config.WeaponLabel,
                description = 'Fucile d\'assalto - ' .. Config.WeaponPrice .. '$',
                icon = 'fa-solid fa-gun',
                onSelect = function()
                    TriggerServerEvent('m4a1:buyWeapon')
                end
            },
            {
                title = 'Munizioni 5.56mm',
                description = '30 proiettili - ' .. Config.AmmoPrice .. '$',
                icon = 'fa-solid fa-bullseye',
                onSelect = function()
                    TriggerServerEvent('m4a1:buyAmmo', 30)
                end
            },
            {
                title = 'Chiudi',
                icon = 'fa-solid fa-xmark',
            }
        }
    })
    lib.showContext('weapon_shop')
end
 
-- Le notifiche arrivano dal server con exports.ox_lib:notify (vedi server.lua)
-- Sul client basta chiamare lib.notify(...) per notifiche locali.
 
-- Comando di debug (se Config.Debug = true)
if Config.Debug then
    RegisterCommand('givem4', function()
        TriggerServerEvent('m4a1:giveWeapon')
    end, false)
end

WARNING

Il comando di debug givem4 è accessibile a tutti i giocatori quando Config.Debug = true. Non abilitarlo in produzione o aggiungi un controllo ACL.


server.lua

-- server.lua
local ESX = exports['es_extended']:getSharedObject()
 
-- Acquisto arma
RegisterNetEvent('m4a1:buyWeapon')
AddEventHandler('m4a1:buyWeapon', function()
    local xPlayer = ESX.GetPlayerFromId(source)
 
    if not xPlayer then return end
 
    -- Controlla se ha già l'arma
    if exports.ox_inventory:Search(source, 'count', Config.WeaponName) > 0 then
        exports.ox_lib:notify(source, { title = 'Armeria', description = 'Hai già questa arma!', type = 'error' })
        return
    end
 
    -- Controlla soldi
    if xPlayer.getAccount('money').money >= Config.WeaponPrice then
        xPlayer.removeAccountMoney('money', Config.WeaponPrice)
        -- ox_inventory crea l'arma come item con le munizioni nel metadata
        exports.ox_inventory:AddItem(source, Config.WeaponName, 1, { ammo = Config.DefaultAmmo })
 
        exports.ox_lib:notify(source, { title = 'Armeria', description = Config.WeaponLabel .. ' acquistato per ' .. Config.WeaponPrice .. '$', type = 'success' })
    else
        exports.ox_lib:notify(source, { title = 'Armeria', description = 'Non hai abbastanza denaro! Servono ' .. Config.WeaponPrice .. '$', type = 'error' })
    end
end)
 
-- Acquisto munizioni
RegisterNetEvent('m4a1:buyAmmo')
AddEventHandler('m4a1:buyAmmo', function(amount)
    local xPlayer = ESX.GetPlayerFromId(source)
 
    if not xPlayer then return end
 
    amount = tonumber(amount) or 30
    local price = Config.AmmoPrice * math.ceil(amount / 30)
 
    if xPlayer.getAccount('money').money >= price then
        xPlayer.removeAccountMoney('money', price)
        -- Aggiunge munizioni come item separato
        exports.ox_inventory:AddItem(source, Config.AmmoType, amount)
 
        exports.ox_lib:notify(source, { title = 'Armeria', description = amount .. ' munizioni acquistate per ' .. price .. '$', type = 'success' })
    else
        exports.ox_lib:notify(source, { title = 'Armeria', description = 'Non hai abbastanza denaro!', type = 'error' })
    end
end)
 
-- Comando debug
RegisterNetEvent('m4a1:giveWeapon')
AddEventHandler('m4a1:giveWeapon', function()
    local xPlayer = ESX.GetPlayerFromId(source)
 
    if xPlayer then
        exports.ox_inventory:AddItem(source, Config.WeaponName, 1, { ammo = Config.DefaultAmmo })
        exports.ox_lib:notify(source, { title = 'Debug', description = Config.WeaponLabel .. ' ricevuta!', type = 'success' })
    end
end)
 
-- All'avvio
AddEventHandler('onResourceStart', function(resourceName)
    if GetCurrentResourceName() == resourceName then
        print('^2[M4A1] Risorsa pronta! weapon=' .. Config.WeaponName)
    end
end)

Come Usare

TIP

Procedura di deploy:

  1. Crea l’arma custom (segui Modulo 03 - Armi Custom)
  2. Metti i file .ydr/.ytd/.meta in stream/
  3. Copia questa risorsa in resources/weapon_m4a1
  4. Aggiungi ensure weapon_m4a1 in server.cfg
  5. Riavvia il server
  6. Vai allo shop alle coordinate Config.ShopCoords
  7. Interagisci con E → compra l’arma

QUESTION

Riflessione: Quali altri eventi potresti aggiungere? Esempio: notifica quando l’arma si rompe (durabilità = 0), sistema di upgrade, o vendita dell’arma.

Verifica

  • L’arma si vede nel negozio di ox_inventory
  • L’acquisto scala i soldi
  • L’arma appare nell’inventario
  • Puoi equipaggiarla e sparare
  • Le munizioni si consumano
  • ox_target mostra l’opzione al punto giusto

TIP

✅ Modulo Integrazione ESX + OX completato!


Torna alla: Indice Generale