Ir al contenido

API

SL-Interact se usa desde tus scripts con exports de cliente. Cada interacción es del recurso que la dio de alta y se borra sola cuando ese recurso se para.

Los ejemplos salen del recurso de ejemplo que trae SL-Interact (sl_interact_demo, junto a sl_interact en la descarga). Su código está abierto: ponlo en tu carpeta resources, arráncalo y escribe /slidemo para que aparezcan todos los ejemplos delante de ti.

local SI = exports.sl_interact
local id = SI:addCoords(vec3(147.6, -1035.8, 29.3), {
label = 'Cajero',
icon = 'cash',
options = {
{ key = 'E', label = 'Usar', onSelect = function(ctx) AbrirCajero() end },
{ key = 'G', label = 'Ver saldo', serverEvent = 'banco:server:saldo' },
},
})
-- Más tarde: cambiarla o quitarla
SI:update(id, { status = { ok = false, label = 'Fuera de servicio' } })
SI:remove(id)

Cada función devuelve un id (un número) para update y remove, o nil si la interacción venía mal. El motivo sale en la consola F8 con el nombre de tu recurso.

Export Dónde va la interacción
addCoords(coords, def) Un punto fijo del mundo (vector3)
addLocalEntity(entity, def) Un objeto, NPC o vehículo creado por este cliente (handle)
addEntity(netId, def) Una entidad en red. Sale cuando existe cerca, aunque se cree después
addModel(models, def) Todos los objetos, NPC o vehículos de uno o varios modelos (nombre o hash)
addGlobalVehicle(def) Todos los vehículos
addGlobalPed(def) Todos los NPC
addGlobalObject(def) Todos los objetos
addGlobalPlayer(def) Todos los demás jugadores
addMany(list) Muchas a la vez (ver muchas a la vez)

Sin bones, una interacción en una entidad es la entidad entera: el jugador puede apuntar a cualquier parte, y la distancia se mide hasta su cuerpo. Con bones, es solo ese sitio. Las dos caben en el mismo coche: el maletero al apuntar atrás, el menú del coche en cualquier otra parte.

-- Todos los NPC de este modelo
SI:addModel('a_m_y_business_01', { label = 'Empresario', icon = 'talk', reveal = 'look', options = { ... } })
-- El maletero de todos los coches, solo al apuntar atrás
SI:addGlobalVehicle({
label = 'Maletero', icon = 'box', bones = 'boot',
options = { { key = 'E', label = 'Abrir / cerrar', onSelect = function(ctx) Maletero(ctx.entity) end } },
})
Campo Tipo Descripción
label texto Nombre en la etiqueta
icon texto Icono del punto: uno de serie o uno registrado con registerIcon
options lista Lo que puede hacer el jugador (ver opciones)
distance número Metros hasta los que sale el punto. De serie: Config.Points.distance (2.0)
offset vector3 Dónde va el punto respecto a la entidad. De serie: el centro de su modelo
bones texto o lista Huesos de la entidad, un punto por hueso ('boot', 'bonnet', 'door_dside_f'…)
reveal texto Cuándo sale el punto: 'always' (al acercarse), 'look' (solo al mirarlo) o 'key' (solo manteniendo la tecla de mostrar, Alt)
easy booleano Se enfoca con solo mirar hacia él, sin apuntar al punto
aimBoost número Hace más fácil enfocarlo: 2 cuenta como el doble de cerca de la mira
highlight texto o false 'outline', 'box', 'both' o false. El contorno del juego solo se dibuja en objetos
grow booleano El objeto crece un poco mientras se mira
moving booleano La entidad se mueve: su punto se recalcula en cada fotograma. NPC y vehículos ya lo hacen
inVehicle booleano Sale también desde dentro de un vehículo (de serie, desde un vehículo no sale nada)
badge texto Texto corto junto al nombre: un precio, una cantidad…
status tabla Chapa junto al nombre: { ok = false, label = 'Fuera de servicio' }. Verde o roja
disabled true o texto La interacción entera está desactivada. Con un texto, sus teclas se ponen rojas con ese motivo
data lo que sea Lo que quieras recibir en ctx.data
Hueso Dónde va el punto
door_dside_f, door_pside_f, door_dside_r, door_pside_r La maneta de esa puerta. Se mueve con la puerta al abrirla
boot El borde del maletero. Sube con la tapa al abrirlo
bonnet El borde del capó. Sube con él al abrirlo
Cualquier otro hueso El propio hueso (petroltank, wheel_lf…)

Cada opción es una tecla de la etiqueta, o una línea del menú.

Campo Tipo Descripción
label texto Texto de la opción (obligatorio)
key texto Una de Config.Keys ('E', 'G', 'H'). Sin ella, la opción coge la primera tecla libre
onSelect función Se ejecuta al pulsar la tecla: function(ctx) ... end
event texto Evento de cliente que se lanza con ctx
serverEvent texto Evento de servidor que se lanza con ctx (sin el handle de la entidad, con netId si está en red)
canInteract función function(ctx) return true end, o return false, 'motivo' (ver condiciones)
groups texto o tabla Trabajos o bandas que pueden usarla: 'police', { 'police', 'ambulance' } o { police = 2 } (grado mínimo)
items texto o tabla Items que tiene que llevar: 'lockpick', { 'lockpick', 'gloves' } o { water = 2 } (cantidad)
disabled true o texto No se puede usar. Con un texto, la tecla se pone roja con ese motivo al pulsarla
showDisabled booleano Si falla una condición, la tecla sale apagada en vez de esconderse
hold número Milisegundos que hay que mantener la tecla. La tecla se llena mientras tanto
confirm true o texto La primera pulsación pregunta ('¿Venderlo? Pulsa otra vez'), la segunda lo hace
options lista Un submenú
quantity true o tabla Elegir antes una cantidad con la rueda: { min = 1, max = 10, step = 1, default = 1 }. Llega en ctx.quantity
price número Precio junto a la opción. Con quantity, el total sale mientras se elige
description texto Texto de ayuda debajo de la opción, en los menús
badge texto Texto corto junto a la opción
image texto o lista Imagen de la opción: un archivo de tu recurso, nui://, https:// o data:image/...
itemImage texto o lista Imagen de un item del inventario del servidor, por su nombre

Una opción necesita onSelect, event, serverEvent u options; si no, no hace nada.

Los archivos de tu recurso (iconos e imágenes) tienen que estar en files { } de tu fxmanifest.lua.

onSelect, canInteract y event reciben una tabla ctx:

Campo Descripción
id Id de la interacción
key Tecla u opción del menú que se usó
entity Handle de la entidad (solo en entidades)
netId Id de red, si la entidad está en red
coords Posición de la entidad o del punto
distance Distancia al jugador
model Modelo de la entidad
bone Hueso del punto, si tiene
quantity Cantidad elegida, en opciones con quantity
data El data que se dio al dar de alta

Un evento de servidor recibe la misma tabla sin entity ni distance:

RegisterNetEvent('tienda:server:comprar', function(ctx)
local src = source
-- ctx.netId, ctx.coords, ctx.quantity, ctx.data...
end)

Cuando una interacción tiene más opciones que teclas, o alguna opción tiene options (submenú) o quantity, la etiqueta pasa a ser un menú. La rueda del ratón elige, la primera tecla (E) usa la elegida, y la segunda (G) o Retroceso vuelven atrás. Las teclas puestas con key no cuentan dentro de un menú.

SI:addModel('prop_vend_soda_01', {
label = 'Máquina', icon = 'product',
options = {
{
label = 'Bebidas', description = 'Bebidas frías.',
options = {
{ label = 'Agua', price = 2, itemImage = 'water', quantity = { min = 1, max = 5 },
serverEvent = 'maquina:server:comprar', },
{ label = 'Cola', price = 3, itemImage = 'kurkakola', serverEvent = 'maquina:server:comprar' },
},
},
{ label = 'Darle una patada', hold = 1000, onSelect = function() return false, 'No cae nada' end },
{ label = 'Romper el cristal', confirm = '¿Romperlo? Pulsa otra vez', onSelect = function() RomperCristal() end },
},
})

Las condiciones más rápidas son las que SL-Interact comprueba solo, sin llamar a tu script: distance, groups, items, disabled e inVehicle. Úsalas siempre que puedas.

canInteract es para todo lo demás. Solo se llama cuando el jugador enfoca la interacción, cada 500 ms mientras la sigue enfocando y al pulsar la tecla. Nunca por las que están cerca sin mirarlas.

canInteract devuelve Qué ve el jugador
true La tecla sale normal
false La tecla no sale y pulsarla no hace nada
false, 'motivo' La tecla no sale. Si se pulsa, se pone roja un momento con el motivo
false, 'motivo' con showDisabled = true La tecla sale apagada desde el principio y se pone roja con el motivo al pulsarla

Para lo que solo sabe tu script o el servidor (dinero, stock, permisos):

  • onSelect puede devolver false, 'motivo': la tecla se pone roja con el motivo.
  • Cliente: exports.sl_interact:deny(key, motivo).
  • Servidor: exports.sl_interact:deny(source, key, motivo). Si el jugador ya no mira nada, sale como un aviso normal.
-- Servidor: decir que no después de mirar el dinero
RegisterNetEvent('tienda:server:comprar', function(ctx)
local src = source
if not QuitarDinero(src, 50) then
exports.sl_interact:deny(src, ctx.key, 'No tienes suficiente dinero')
return
end
end)
Export Descripción
update(id, campos) Cambia cualquier campo de una interacción: nombre, opciones, insignia, estado, disabled…
updateMany({ [id] = campos }) Varias a la vez. Devuelve cuántas cambió
remove(id) o remove({ ids }) Quita una o varias
removeMany(ids) Quita una lista de ids
removeLocalEntity(entity) Quita tus interacciones de esa entidad
removeEntity(netId) Quita tus interacciones de esa entidad en red
removeModel(models) Quita tus interacciones de esos modelos
-- El texto de una puerta cambia con su estado
SI:update(puertaId, { options = {
{ key = 'E', label = abierta and 'Cerrar' or 'Abrir', onSelect = CambiarPuerta },
} })

Dar de alta cientos de interacciones una a una cruza de tu recurso a SL-Interact cientos de veces. addMany lo hace en una sola llamada. Cada entrada dice dónde va con uno de estos campos: coords, entity, netId, model / models, o global ('vehicle', 'ped', 'object' o 'player').

local ids = SI:addMany({
{ coords = vec3(25.7, -1347.3, 29.5), label = 'Estante 1', options = { ... } },
{ coords = vec3(26.4, -1347.3, 29.5), label = 'Estante 2', options = { ... } },
{ model = 'prop_atm_01', label = 'Cajero', options = { ... } },
})
-- ids vuelve en el mismo orden (false en las que venían mal)
SI:updateMany({ [ids[1]] = { badge = 'x3' }, [ids[2]] = { disabled = 'Vacío' } })
SI:removeMany(ids)

Iconos de serie: product, door, checkout, pay, sale, pc, box, lock, unlock, build, cash, person, car, hand, gear, talk.

Registra los tuyos con registerIcon(nombre, imagen) antes de usarlos:

-- Un archivo de tu recurso (en files { } de tu fxmanifest)
SI:registerIcon('estrella', 'star.svg')
-- O: 'nui://recurso/archivo.png', 'https://...', 'data:image/png;base64,...'

Cuando SL-Interact se para, olvida todas las interacciones que le dieron. Con el servidor en marcha, vuelve a dar las tuyas cuando arranque:

AddEventHandler('onClientResourceStart', function(name)
if name == 'sl_interact' then DarMisInteracciones() end
end)

Si se reinicia el servidor entero, o tu recurso después de SL-Interact, no hace falta nada más.