Saltar al contenido principal

Scripting Lua: Primeros Pasos

Esta página te enseña a escribir tu primer script Lua para un prop o un objeto personalizado de FreeMinecraftModels, desde un archivo vacío hasta un script interactivo funcional. Al finalizar, entenderás los hooks, el context, las APIs de props y objetos, y la estructura general de cada archivo de script.

Una vez que te sientas cómodo con lo básico, continúa con las páginas complementarias:

  • API de Props e Ítems -- las APIs de context.prop, context.item, context.event, context.world y los demás context
  • Ejemplos y Patrones -- scripts funcionales completos para props y objetos que puedes estudiar y adaptar
  • Solución de Problemas -- errores comunes, consejos de depuración y la lista de verificación QC
Función Experimental

Los scripts Lua de props y de objetos son actualmente experimentales. Los nombres de los hooks, los métodos auxiliares y el comportamiento aún pueden cambiar a medida que FreeMinecraftModels evoluciona, así que prueba cuidadosamente antes de usarlos en un servidor de producción.

Relación con EliteMobs Lua

FreeMinecraftModels usa el runtime Lua de MagmaCore. Si ya escribes poderes Lua para EliteMobs, los conceptos fundamentales -- archivos de script que devuelven una tabla, api_version, hooks, context, estado, cooldowns, programación y la sandbox -- te resultarán familiares. Los hooks exactos y los nombres de los métodos del context siguen dependiendo del plugin:

  • Los scripts de EliteMobs se ejecutan en jefes y tienen hooks como on_boss_damaged_by_player, on_enter_combat, etc.
  • Los scripts de props de FMM se ejecutan en props y tienen hooks como on_right_click, on_left_click, on_zone_enter, etc.
  • Los scripts de objetos de FMM se ejecutan en objetos personalizados y tienen hooks como on_equip, on_attack_entity, on_consume, on_game_tick, etc.

Las APIs context.world, context.zones, context.scheduler, context.state y context.log documentadas aquí son las versiones de FreeMinecraftModels/MagmaCore. Los scripts de NPC de EliteMobs usan las mismas tablas genéricas de MagmaCore más context.npc; los poderes de jefe de EliteMobs usan variantes específicas de jefe para varias tablas. Esta página cubre lo que es específico de los props y objetos de FMM.


Qué Son los Scripts de Props

Los scripts de props son archivos .lua independientes que se encuentran en la carpeta plugins/FreeMinecraftModels/scripts/. Se referencian desde un archivo de configuración YAML que se ubica junto al archivo del modelo, y se ejecutan cada vez que el prop aparece en el mundo.

Para Qué Son Buenos los Scripts de Props

Los scripts de props destacan cuando necesitas:

  • Props interactivos que responden a los clics de los jugadores (puertas, palancas, botones)
  • Props decorativos invulnerables que no pueden ser destruidos por los jugadores
  • Disparadores de proximidad que detectan cuando los jugadores entran o salen de un área
  • Props animados que reproducen animaciones al interactuar o con un temporizador
  • Props emisores de sonido que reproducen sonidos al hacer clic o al acercarse
  • Cualquier comportamiento de prop que requiera lógica más allá de la decoración estática

Si tu prop es puramente decorativo y no necesita interacción, no necesitas un script.


Qué Son los Scripts de Objetos

Los scripts de objetos usan el mismo formato de archivo .lua y la misma carpeta scripts/ que los scripts de props. La diferencia es que se adjuntan a objetos personalizados: modelos que tienen un campo material: establecido en su archivo de configuración YML. Mientras que los scripts de props se ejecutan cuando una entidad prop aparece en el mundo, los scripts de objetos se ejecutan cuando un jugador equipa el objeto personalizado (mano principal, mano secundaria o ranura de armadura) y se detienen cuando el objeto se desequipa.

Cómo Funcionan los Scripts de Objetos

  • Activación: se crea una instancia de script cuando un jugador equipa un objeto personalizado de FMM. Los scripts son por jugador y por tipo de objeto: una ScriptInstance por par (jugador, itemId).
  • Desactivación: la instancia de script se destruye cuando el objeto se desequipa (se saca de la ranura activa, se tira o el jugador se desconecta).
  • Identificación del objeto: los objetos personalizados se identifican por la clave PDC (PersistentDataContainer) fmm_item_id, que es distinta del model_id del prop. Para obtener un objeto correctamente etiquetado, usa /fmm giveitem <id> o el menú de administrador.
  • Context: los hooks de objeto reciben un context con context.player, context.item, context.world, context.state, context.scheduler, context.log y, cuando corresponde, context.event.

Para Qué Son Buenos los Scripts de Objetos

Los scripts de objetos destacan cuando necesitas:

  • Armas personalizadas con habilidades especiales (espadas de escarcha, varitas mágicas)
  • Herramientas con acciones únicas de clic derecho o clic con Shift
  • Objetos consumibles con efectos personalizados
  • Armaduras con efectos pasivos mientras se llevan puestas
  • Objetos que registran su uso o tienen durabilidad limitada
  • Cualquier comportamiento de objeto en mano que vaya más allá de las mecánicas vanilla

Para Quién Es Esta Página

Esta página está escrita para tres tipos de lectores:

  • Alguien que ya conoce el scripting Lua de EliteMobs y quiere aprender los hooks y APIs específicos de FMM
  • Alguien que es nuevo en el scripting Lua y necesita una referencia completa con nombres exactos para props
  • Alguien que usa IA para redactar scripts de props y necesita suficiente detalle para saber cuándo la IA inventó algo falso

No necesitas convertirte en un desarrollador Lua completo antes de escribir scripts de props útiles. Para la mayoría de los scripts de props prácticos, lo único que realmente necesitas es:

  • Cómo colocar un hook válido en la tabla devuelta
  • Cómo leer valores de context
  • Cómo detener la ejecución anticipadamente con if ... then return end
  • Cómo llamar a algunos métodos auxiliares de forma exacta

Introducción Rápida a Lua

No necesitas ser un experto en Lua para escribir scripts de FMM. La mayoría de los scripts solo usan un puñado de conceptos: variables (local x = 5), funciones (function foo() end), comprobaciones if (if x then ... end), tablas ({key = value}) y nil (el valor "nada" de Lua). La sintaxis es ligera: sin punto y coma, sin llaves, solo end para cerrar bloques.

Para un recorrido completo con ejemplos, consulta el Motor de Scripting Lua de MagmaCore — Introducción Rápida a Lua. Esa introducción es compartida por todos los plugins de Nightbreak, así que aprenderla una vez sirve para todos.


Dónde Se Ubican los Archivos

Archivos de Script

Coloca los archivos .lua en la carpeta central de scripts:

plugins/
FreeMinecraftModels/
scripts/
invulnerable.lua
interactive_door.lua
proximity_sound.lua

FMM detecta todos los archivos .lua en plugins/FreeMinecraftModels/scripts/ al iniciar.

En la lista scripts: de un modelo, incluye la extensión .lua por claridad. FMM también acepta entradas sin la extensión y le añade .lua internamente. El archivo en disco debe seguir terminando en .lua, y los nombres siguen distinguiendo mayúsculas de minúsculas.

Archivos de Modelo y Archivos de Configuración

Cada archivo de modelo puede tener un archivo .yml de configuración asociado en el mismo directorio:

plugins/
FreeMinecraftModels/
models/
torch_01.fmmodel
torch_01.yml <-- config de script para torch_01
scripts/
invulnerable.lua <-- referenciado por torch_01.yml

El archivo .yml de configuración es lo que conecta un modelo con sus scripts.


Formato del Archivo de Configuración

El archivo de configuración YAML que se ubica junto a un archivo de modelo tiene estos campos:

isEnabled: true
voxelize: false
solidify: false
scripts:
- invulnerable.lua

Para los ítems personalizados (modelos que los jugadores pueden sostener o equipar), también estableces el campo material y, opcionalmente, name, lore y enchantments:

isEnabled: true
material: DIAMOND_SWORD
name: "&bFrost Blade"
lore:
- "&7A sword forged in eternal ice"
- "&7Slows enemies on hit"
enchantments:
- "SHARPNESS,5"
- "UNBREAKING,3"
scripts:
- frost_sword.lua
CampoTipoPredeterminadoNotas
isEnabledbooleantrueSi los scripts están activos para este prop/ítem
scriptslista de strings[]Nombres de archivos .lua en la carpeta scripts/
voxelizebooleanfalseAjusta la colocación a rotaciones de 90 grados y al alineamiento con la rejilla de bloques
solidifybooleanfalseColoca bloques barrera solo por paquetes en la huella del prop (requiere voxelize)
materialstring""Un nombre válido de Material de Bukkit (p. ej. DIAMOND_SWORD). Establecerlo convierte el modelo en un ítem personalizado que los jugadores pueden sostener o equipar, lo que activa el sistema de scripting de ítems
namestring""Nombre visible del ítem personalizado. Admite códigos de color &
lorelista de strings[]Líneas de lore mostradas en el tooltip del ítem. Admite códigos de color &
enchantmentslista de strings[]Encantamientos aplicados al ítem. Formato: "ENCHANTMENT_NAME,LEVEL" (p. ej. "SHARPNESS,5")

Puedes adjuntar múltiples scripts al mismo prop. Cada script es su propia instancia independiente.

ID del ítem y límite de scripts
  • El ID del ítem se deriva del nombre del archivo YML sin su extensión. Por ejemplo, frost_sword.yml produce un ID de ítem frost_sword. Este es el ID que usan /fmm giveitem y la clave PDC fmm_item_id.
  • Los ítems vinculan solo un script de la lista scripts:. FMM revisa las entradas en orden y usa el primer script que puede resolver, ignorando las entradas posteriores. A diferencia de los ítems, los props ejecutan todos los scripts listados que puedan resolver, cada uno como una instancia independiente.
  • La extensión .lua se añade automáticamente a los nombres de archivo de script si la omites, así que frost_sword y frost_sword.lua son equivalentes en la lista scripts:.

Generación Diferida de Configuración

Cuando un prop aparece y no existe un archivo .yml asociado, FMM crea automáticamente un archivo de configuración predeterminado con isEnabled: true y una lista scripts: vacía. Esto sucede de forma asíncrona, por lo que el prop no tendrá scripts en su primer spawn -- solo después de que se cree la configuración y la edites para agregar nombres de archivos de script.

Esto significa:

  1. Coloca tu archivo de modelo en models/
  2. Genera el prop una vez (FMM crea el .yml automáticamente)
  3. Edita el .yml generado para agregar tus nombres de archivos de script
  4. Vuelve a generar el prop o recarga (los scripts ahora están activos)

Referencia de Hooks

Cada archivo de script Lua de prop devuelve una tabla. Cada clave en esa tabla (además de api_version y priority) debe ser uno de los hooks listados a continuación. El runtime llama a la función correspondiente cada vez que se dispara el evento del juego asociado.

HookSe dispara cuandoNotas
on_spawnEl prop aparece en el mundoSe ejecuta una vez cuando el script se vincula
on_game_tickUna vez por tick del servidor (50 ms)Solo activo si el script define este hook
on_destroyEl prop se elimina del mundoHook de limpieza
on_left_clickUn jugador hace clic izquierdo (golpea) el propcontext.event es el evento de daño
on_right_clickUn jugador hace clic derecho en el propcontext.event es el evento de interacción
on_zone_enterUn jugador entra en una zona vigiladaRequiere que se haya configurado una vigilancia de zona
on_zone_leaveUn jugador sale de una zona vigiladaRequiere que se haya configurado una vigilancia de zona
Hook de prop reservado

El validador de scripts actual acepta on_projectile_hit en los scripts de props, pero el runtime actual todavía no despacha impactos de proyectil a los scripts de props. Usa el on_projectile_hit de ítem para comportamientos de proyectil ligados a un ítem con script, o la API de Bukkit ModeledEntityHitByProjectileEvent para gestionar proyectiles sobre entidades modeladas desde el plugin.


Referencia de Hooks de Ítems

Los scripts de ítems devuelven una tabla igual que los scripts de props, con api_version = 1 y funciones de hook. Los siguientes hooks están disponibles para los scripts de ítems. Todos los hooks de ítems reciben context con context.player, context.item y (cuando corresponde) context.event.

La columna de notas indica la familia de eventos Bukkit subyacente. El wrapper de Lua no expone campos específicos de Bukkit sin procesar como target, block, projectile o item; usa context.player, context.event.player y las consultas auxiliares de entidades/mundo cuando necesites context adicional.

Hooks de Combate

HookSe dispara cuandoNotas
on_attack_entityEl jugador ataca a una entidad mientras sostiene el ítemcontext.event es el evento de daño
on_kill_entityEl jugador mata a una entidad mientras sostiene el ítemcontext.event es el evento de muerte
on_take_damageEl jugador recibe daño mientras el ítem está equipadocontext.event es el evento de daño
on_shield_blockEl jugador bloquea daño con un escudocontext.event es el evento de daño
on_shoot_bowEl jugador dispara un arcocontext.event es el evento de disparo de arco
on_projectile_hitUn proyectil disparado por el jugador golpea algocontext.event es el evento de impacto de proyectil
on_projectile_launchEl jugador lanza un proyectilcontext.event es el evento de lanzamiento de proyectil

Hooks de Interacción

HookSe dispara cuandoNotas
on_right_clickEl jugador hace clic derecho mientras sostiene el ítemcontext.event es el evento de interacción
on_left_clickEl jugador hace clic izquierdo mientras sostiene el ítemcontext.event es el evento de interacción
on_shift_right_clickEl jugador hace shift+clic derecho mientras sostiene el ítemcontext.event es el evento de interacción
on_shift_left_clickEl jugador hace shift+clic izquierdo mientras sostiene el ítemcontext.event es el evento de interacción
on_interact_entityEl jugador hace clic derecho en una entidad mientras sostiene el ítemcontext.event es el evento de interacción con entidad

Hooks de Equipamiento

HookSe dispara cuandoNotas
on_equipEl ítem se equipa (se mueve a una ranura activa)Buen lugar para inicializar el estado
on_unequipEl ítem se desequipa (se saca de una ranura activa)Buen lugar para hacer limpieza
on_swap_handsEl jugador intercambia el ítem entre la mano principal y la secundariacontext.event es el evento de intercambio
on_dropEl jugador suelta el ítemcontext.event es el evento de soltar

Hooks de Utilidad

HookSe dispara cuandoNotas
on_break_blockEl jugador rompe un bloque mientras sostiene el ítemcontext.event es el evento de rotura de bloque
on_consumeEl jugador consume el ítem (comida/poción)context.event es el evento de consumo
on_item_damageEl ítem sufre daño de durabilidadcontext.event es el evento de daño de ítem
on_fishEl jugador usa una caña de pescarcontext.event es el evento de pesca
on_deathEl jugador muere mientras el ítem está equipadocontext.event es el evento de muerte

Hook de Ciclo de Vida

HookSe dispara cuandoNotas
on_game_tickCada tick del servidor mientras el ítem está equipadoÚsalo con moderación -- se ejecuta 20 veces por segundo

Contrato Mínimo del Archivo

Cada script Lua de prop debe hacer return de una tabla.

Campos de Nivel Superior Obligatorios y Opcionales

CampoObligatorioTipoNotas
api_versionNumberActualmente debe ser 1
priorityNoNumberSe valida si está presente, pero FMM actualmente no ordena los scripts por él. Los props se ejecutan en el orden de la lista scripts:; los ítems vinculan solo el primer script válido
claves de hook soportadasNoFunctionDebe usar uno de los nombres exactos de hook listados en la Referencia de Hooks

Reglas de Validación

  • El archivo debe devolver una tabla.
  • api_version es obligatorio y actualmente debe ser 1.
  • priority debe ser numérico si está presente.
  • Cada clave de nivel superior adicional debe ser un nombre de hook soportado.
  • Cada clave de hook debe apuntar a una función.
  • Las claves de nivel superior desconocidas son rechazadas.
nota

priority es útil para mantener los scripts portables entre runtimes basados en MagmaCore, pero el orden de ejecución actual de FreeMinecraftModels lo dicta la configuración. Coloca los scripts de props en el orden en que quieres que se ejecuten dentro de la lista scripts: del modelo.

Las funciones auxiliares y las constantes locales deben estar encima del return final, no dentro de la tabla devuelta.


Tu Primer Script de Prop Funcional, Paso a Paso

Antes del Paso 1: Configurar la Config

  1. Coloca tu archivo de modelo (ej. my_prop.fmmodel) en plugins/FreeMinecraftModels/models/
  2. Genera el prop una vez para generar la configuración .yml
  3. Crea tu archivo de script en plugins/FreeMinecraftModels/scripts/first_test.lua
  4. Edita plugins/FreeMinecraftModels/models/my_prop.yml:
isEnabled: true
scripts:
- first_test.lua
  1. Vuelve a generar el prop o recarga el servidor

Paso 1: Hacer que el Archivo se Cargue

return {
api_version = 1,

on_spawn = function(context)
end
}

Si esto se carga sin errores en la consola, has demostrado:

  • El archivo es Lua válido
  • FMM lo encontró en la carpeta scripts/
  • La configuración lo referencia correctamente
  • La forma de la tabla devuelta es correcta

Paso 2: Hacer que el Prop Haga Algo Visible

return {
api_version = 1,

on_spawn = function(context)
context.log:info("Prop script loaded for: " .. (context.prop.model_id or "unknown"))
end
}

Revisa la consola del servidor. Si ves el mensaje de log, tu hook se está ejecutando.

Paso 3: Reaccionar al Clic de un Jugador

return {
api_version = 1,

on_right_click = function(context)
context.log:info("Prop was right-clicked!")
end
}

Haz clic derecho en el prop en el juego. Si la consola muestra el mensaje, el hook de clic está funcionando.

Paso 4: Cancelar el Daño para Hacer el Prop Invulnerable

return {
api_version = 1,

on_left_click = function(context)
if context.event then
context.event.cancel()
end
end
}

Este es el patrón utilizado por el script predefinido invulnerable.lua. Cancela el evento de daño para que el armor stand que respalda al prop no pueda ser destruido.

Paso 5: Reproducir una Animación al Hacer Clic

return {
api_version = 1,

on_right_click = function(context)
context.prop:play_animation("open", true, false)
end
}

Esto reproduce la animación "open" en el modelo del prop, mezclada y sin bucle.


¿Qué Es context?

Cada función de hook recibe un argumento llamado context. Piensa en él como una caja de herramientas que FMM te entrega cada vez que algo sucede -- contiene todo lo que necesitas para interactuar con el prop, el mundo, las zonas y más.

Tú no creas context -- FMM lo crea y lo pasa a tu hook. Para los detalles completos de las APIs de context compartidas (context.state, context.log, context.cooldowns, context.scheduler, context.world, context.zones), consulta la página del Motor de Scripting Lua de MagmaCore.


APIs Clave de context

Aquí tienes un resumen de lo que está disponible. Para detalles completos, consulta API de Props.

  • context.prop -- (Solo scripts de props) La entidad del prop. Proporciona model_id, current_location, play_animation() y stop_animation().

  • context.item -- (Solo scripts de ítems) El ítem personalizado. Proporciona id, material(), get_amount(), set_amount(), consume(), get_uses(), set_uses(), get_name(), set_name(), get_lore(), set_lore(), get_durability(), get_durability_percentage(), use_durability() y use_durability_percentage(). Consulta API de Props e Ítems para todos los detalles.

  • context.player -- El jugador en los hooks impulsados por un jugador. Los scripts de ítems lo resuelven a partir del portador del ítem; los hooks de clic de props y los hooks genéricos de zona lo resuelven a partir del jugador que los dispara. Es nil en los hooks de ciclo de vida de props, en los callbacks programados de props y en los hooks que no involucran a un jugador.

  • context.event -- Un pequeño wrapper del evento Bukkit o del actor jugador que activó este hook. Disponible en los hooks de clic, combate, interacción y zona genéricos. Proporciona event.player, is_cancelled y, cuando el evento Bukkit subyacente es cancelable, cancel() / uncancel(); no expone campos específicos de Bukkit como target, block, projectile o item. Es nil en los hooks que no tienen evento ni actor jugador (como on_spawn, on_game_tick y on_equip).

  • context.state -- Una tabla Lua simple que persiste durante la vida de la instancia del script. Consulta context.state.

  • context.cooldowns -- Auxiliares de cooldown locales y globales. Usa context.cooldowns:check_local("key", ticks) para cooldowns normales por script. Consulta context.cooldowns.

  • context.log -- Registro en consola. Consulta context.log.

  • context.scheduler -- Tareas diferidas y repetitivas. Consulta context.scheduler.

  • context.world -- Interacción con el mundo: partículas, sonidos, consultas de bloques, rayos, entidades cercanas. Consulta context.world.

  • context.zones -- Creación y vigilancia de zonas espaciales (esferas, cilindros, cuboides). Consulta context.zones.


Sintaxis de Métodos: : vs .

Para una explicación de la sintaxis de métodos : frente a . en Lua, consulta la página del Motor de Scripting Lua de MagmaCore. La API de FMM acepta ambas formas.


Plantillas Listas para Copiar y Pegar

Script de Prop Válido Más Pequeño

return {
api_version = 1,

on_spawn = function(context)
end
}

Plantilla de Prop Invulnerable

return {
api_version = 1,

on_left_click = function(context)
if context.event then
context.event.cancel()
end
end
}

Plantilla de Prop Interactivo

return {
api_version = 1,

on_spawn = function(context)
context.state.is_active = false
end,

on_right_click = function(context)
context.state.is_active = not context.state.is_active

if context.state.is_active then
context.prop:play_animation("activate", true, true)
else
context.prop:stop_animation()
end
end
}

Script de Ítem Válido Más Pequeño

return {
api_version = 1,

on_equip = function(context)
end
}

Plantilla de Ítem con Acción de Clic Derecho

return {
api_version = 1,

on_right_click = function(context)
if not context.cooldowns:check_local("activate", 40) then return end

-- Your action here
context.player:send_message("&aItem activated!")
end
}

Disposición de Archivo Más Grande

local ANIMATION_NAME = "idle"

local function do_something(context)
context.log:info("Doing something!")
end

return {
api_version = 1,
priority = 0,

on_spawn = function(context)
context.state.task_id = nil
end,

on_right_click = function(context)
do_something(context)
end,

on_destroy = function(context)
if context.state.task_id ~= nil then
context.scheduler:cancel(context.state.task_id)
end
end
}

Primer Flujo de Trabajo Real

Al construir un script de prop completamente nuevo, usa este orden:

  1. Crea el archivo .lua y haz que on_spawn funcione.
  2. Agrega el nombre del archivo de script a la configuración .yml del prop.
  3. Cambia al hook que realmente deseas (ej. on_right_click).
  4. Agrega un mensaje de log primero, antes de animaciones o efectos.
  5. Agrega un efecto real (animación, sonido, partícula).
  6. Solo después de eso, agrega funciones auxiliares, state, lógica de scheduler o zonas.

Ese orden facilita enormemente la depuración porque solo cambia una cosa a la vez.


Scripts Predefinidos

FMM viene con cuatro scripts Lua predefinidos:

  • invulnerable.lua -- Cancela los eventos de daño por clic izquierdo, haciendo el prop indestructible. Este es el script de prop útil más simple.
  • pickupable.lua -- Permite a los jugadores recoger un prop golpeándolo tres veces. Cada golpe reproduce una animación de daño en el prop, y al tercer golpe el prop se elimina y suelta su ítem de colocación para que el jugador lo recoja.
  • storage_double.lua -- Convierte un prop en un cofre doble (54 ranuras). El clic derecho abre una GUI de inventario persistente. Reproduce animaciones y sonidos de apertura/cierre. El contenido se guarda en el prop y sobrevive a los reinicios del servidor. Al destruirlo, todo el contenido se suelta.
  • storage_single.lua -- Igual que storage_double, pero con 3 filas (27 ranuras) en lugar de 6.

Puedes encontrar más ejemplos en la página Ejemplos y Patrones.


Lua Sandbox

Los scripts de props y de ítems se ejecutan dentro del mismo entorno LuaJ sandboxed que EliteMobs. Las restricciones de la sandbox son idénticas. Para la lista completa de globals eliminados y funciones de la biblioteca estándar disponibles, consulta la página del Motor de Scripting Lua de MagmaCore.


Próximos Pasos

  • API de Props e Ítems -- referencia completa de context.prop, context.item, context.event, context.world, context.zones y context.scheduler
  • Ejemplos y Patrones -- scripts funcionales completos para props e ítems con explicaciones
  • Solución de Problemas -- problemas comunes, consejos de depuración y una lista de verificación QC

Si también escribes poderes Lua de jefe para EliteMobs, la sandbox, api_version, la tabla de estado, los conceptos de cooldown y la estructura basada en hooks te resultarán familiares, pero el context de jefe usa nombres de métodos específicos de EliteMobs. Consulta la documentación Lua de EliteMobs para las APIs exactas de jefes.