Explorer
KNOW-PAT-103

FiveM - Item Usable to Vehicle Spawn + ox_target : BMX, animation, return item

Domaine
fivem
Type
pattern
Priorité
P2

Parent : [[INDEX-FIVEM-GAMEPLAY]]

FiveM - Item Usable to Vehicle Spawn + ox_target : BMX, animation, return item

Problème

Un item utilisable doit spawner un véhicule, jouer une animation, et permettre de récupérer l'item en supprimant le véhicule.

Solution

Item usable via ox_inventory → spawn véhicule client avec animation, ox_target sur l'entité locale pour delete + return item.

-- server/main.lua
RegisterNetEvent('vape:bmx-useItem')
AddEventHandler('vape:bmx-useItem', function()
    local src = source
    local hasItem = exports.ox_inventory:Search(src, 'count', 'bmx')
    if hasItem < 1 then
        TriggerClientEvent('ox_lib:notify', src, { type = 'error', description = 'Pas de BMX' })
        return
    end
    
    exports.ox_inventory:RemoveItem(src, 'bmx', 1)
    TriggerClientEvent('vape:bmx-spawn', src)
end)

-- client/main.lua
local spawnedBMX = nil

RegisterNetEvent('vape:bmx-spawn')
AddEventHandler('vape:bmx-spawn', function()
    local ped = PlayerPedId()
    local coords = GetEntityCoords(ped)
    local heading = GetEntityHeading(ped)
    
    -- Animation
    RequestAnimDict('anim@heists@box_carry@')
    while not HasAnimDictLoaded('anim@heists@box_carry@') do Wait(0) end
    TaskPlayAnim(ped, 'anim@heists@box_carry@', 'idle', 8.0, -8.0, 1000, 49, 0, false, false, false)
    
    -- Spawn
    local model = GetHashKey('bmx')
    RequestModel(model)
    while not HasModelLoaded(model) do Wait(0) end
    
    spawnedBMX = CreateVehicle(model, coords.x, coords.y, coords.z, heading, true, false)
    SetPedIntoVehicle(ped, spawnedBMX, -1)
    SetModelAsNoLongerNeeded(model)
    
    -- ox_target pour récupérer
    exports.ox_target:addLocalEntity(spawnedBMX, {
        {
            name = 'delete_bmx',
            label = 'Récupérer le BMX',
            icon = 'fa-solid fa-bicycle',
            distance = 2.0,
            onSelect = function()
                if spawnedBMX and DoesEntityExist(spawnedBMX) then
                    DeleteEntity(spawnedBMX)
                    spawnedBMX = nil
                    TriggerServerEvent('vape:bmx-returnItem')
                end
            end
        }
    })
end)

-- server : return item
RegisterNetEvent('vape:bmx-returnItem')
AddEventHandler('vape:bmx-returnItem', function()
    local src = source
    exports.ox_inventory:AddItem(src, 'bmx', 1)
    TriggerClientEvent('ox_lib:notify', src, { type = 'success', description = 'BMX récupéré' })
end)

Pourquoi

  • Le serveur vérifie la possession avant de donner l'ordre de spawn
  • ox_target sur l'entité locale est plus propre qu'un marker/thread
  • Animation + spawn + interaction = UX complète

Quand l'utiliser

  • Items spawners (tentes, véhicules temporaires, props)
  • Items consommables avec effet visuel

Quand NE PAS l'utiliser

  • Véhicules permanents (garage) → pas de return item
  • Si ox_target n'est pas disponible

Exemples

Correct

-- Gestion du netId pour le server-side cleanup
RegisterNetEvent('vape:bmx-spawn')
AddEventHandler('vape:bmx-spawn', function()
    -- ... spawn code ...
    local netId = NetworkGetNetworkIdFromEntity(spawnedBMX)
    TriggerServerEvent('vape:bmx-register', netId)
end)

-- Server garde une table pour cleanup au restart
local spawnedItems = {}
RegisterNetEvent('vape:bmx-register')
AddEventHandler('vape:bmx-register', function(netId)
    spawnedItems[source] = netId
end)

AddEventHandler('playerDropped', function()
    local netId = spawnedItems[source]
    if netId then
        local entity = NetworkGetEntityFromNetworkId(netId)
        if DoesEntityExist(entity) then
            DeleteEntity(entity)
        end
        spawnedItems[source] = nil
    end
end)

Incorrect

-- Le client spawn sans vérification serveur → duplication possible
RegisterCommand('spawnbmx', function()
    CreateVehicle(GetHashKey('bmx'), GetEntityCoords(PlayerPedId()), GetEntityHeading(PlayerPedId()), true, false)
end)

Références

  • Vape_BMXItem/client/main.lua L1-52
  • Vape_BMXItem/server/main.lua L1-24
  • REF-003 (ox_target documentation)