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_targetsur 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)