Saltar al contenido principal

Scripting Lua: Solución de problemas

webapp_banner.jpg

Esta página cubre los problemas comunes que puedes encontrar al escribir o depurar poderes en Lua, además de consejos de migración para autores que vienen de EliteScript. Si estás depurando scripts de NPC, consulta Scripts de NPC. Si buscas ejemplos funcionales, consulta Ejemplos y patrones. Si apenas estás comenzando, consulta Primeros pasos.

Motor Lua compartido

EliteMobs utiliza el motor de scripting Lua de MagmaCore, compartido entre los plugins de Nightbreak. Para la documentación sobre conceptos compartidos como el sandbox, el planificador, las zonas, la API del mundo, las tablas de entidades y los métodos de UI del jugador, consulta la página del Motor de scripting Lua de MagmaCore.


Problemas comunes

1. El poder no se carga en absoluto

Revisa la consola del servidor en busca de errores cuando el servidor arranca. La causa más común es un error de sintaxis en Lua (falta un end, paréntesis sin cerrar, etc.). Verifica también que el archivo termine en .lua y esté ubicado en el directorio powers correcto.

2. El hook nunca se dispara

Verifica que el nombre del hook esté escrito exactamente como aparece en la lista de hooks. Errores comunes: on_boss_hit (incorrecto) en lugar de on_boss_damaged_by_player (correcto), o on_tick (incorrecto) en lugar de on_game_tick (correcto).

3. context.player es nil

No todos los hooks proporcionan un jugador. on_spawn, on_game_tick y on_exit_combat no tienen jugador. on_enter_combat proporciona context.player (el jugador que inició el combate). En on_boss_damaged (daño genérico), quien causa el daño puede no ser un jugador. Añade siempre una comprobación de nil antes de usar context.player.

4. Tiempo de espera / presupuesto de ejecución excedido

Si un hook o callback tarda demasiado, el poder se desactiva automáticamente para evitar el lag. El mensaje de la consola se ve así:

[Lua] my_power.lua took 73ms in 'on_game_tick' (limit: 50ms) — script disabled to prevent lag.

Causas comunes: iterar sobre demasiadas entidades, crear demasiadas zonas por tick o ejecutar operaciones de cadenas costosas en on_game_tick. Mueve el trabajo costoso detrás de una barrera de enfriamiento o reduce el trabajo realizado por llamada.

5. El callback del planificador usa datos obsoletos

Probablemente estás usando el context externo en lugar del parámetro del callback. Cambia function() ... context.boss ... end por function(tick_context) ... tick_context.boss ... end.

6. La consulta de zona no devuelve entidades

Revisa con cuidado la definición de la zona. Para las zonas nativas, asegúrate de que kind esté en minúsculas ("sphere", no "SPHERE"). Para las utilidades de script, asegúrate de que shape esté en mayúsculas ("CONE", no "cone"). Verifica también que origin o Target realmente resuelva a una ubicación válida.

7. Las partículas no aparecen

Verifica que el nombre de la partícula sea un valor válido del enum Particle de Bukkit en MAYÚSCULAS. Error común: "flame" (incorrecto) en lugar de "FLAME" (correcto). Comprueba también que amount sea al menos 1 y que la ubicación esté en un chunk cargado.

8. El enfriamiento no parece funcionar

Asegúrate de usar check_local(key, duration) (que comprueba Y establece en una sola llamada), no local_ready(key) seguido de un set_local(duration, key) por separado. Si usas local_ready solo, únicamente compruebas pero nunca estableces el enfriamiento.

9. El jefe sigue usando poderes después de morir

Añade lógica de limpieza en on_exit_combat y/o on_death para cancelar las tareas del planificador. Si el jefe muere, on_exit_combat debería dispararse, pero añadir una limpieza explícita en ambos hooks es más seguro.


Lectura de los mensajes de error

Cuando algo sale mal en un poder de Lua, la consola imprime un bloque de error amigable con el prefijo [Lua]. Estos mensajes te indican exactamente qué archivo, qué línea, qué hook y qué salió mal, en lenguaje sencillo. Lee siempre el mensaje completo antes de depurar.

Un error típico se ve así:

[Lua] Error in 'push_zone.lua' at line 35 during 'on_boss_damaged_by_player':
[Lua] -> You tried to call a method or function that doesn't exist.
[Lua] -> Check the method name for typos, or make sure you're using ':' (colon) for method calls, not '.' (dot).
[Lua] -> Script has been disabled for this entity to prevent further errors.

El sistema traduce los errores comunes de Lua a lenguaje sencillo. Estos son los más frecuentes:

Error crudo de LuaLo que te dice la consola
attempt to call nilIntentaste llamar a un método o función que no existe. Revisa el nombre del método en busca de erratas, o asegúrate de usar : (dos puntos) para las llamadas a métodos, no . (punto).
index expected, got nilIntentaste acceder a un campo de algo que es nil. Verifica que el código anterior lo haya inicializado.
attempt to indexIntentaste acceder a una propiedad de un valor nil o inválido.
bad argumentMuestra los detalles concretos del desajuste de argumentos (tipo esperado frente a tipo real).
Tiempo de espera<filename> tardó X ms en 'hook_name' (límite: 50 ms) -- script desactivado para evitar el lag.
consejo

Cuando veas un error [Lua] en la consola, el mensaje de error te indica exactamente qué archivo, qué línea, qué hook y qué salió mal, en lenguaje sencillo. Lee el mensaje completo antes de meterte en el código: normalmente te lleva directo a la solución.


No asumas que existen alias no documentados

La API de Lua expone un conjunto específico de nombres de métodos. Si escribes poderes a mano o con la ayuda de una IA, no asumas que existen abreviaturas o nombres alternativos. Los siguientes son ejemplos de nombres que no existen y que provocarán errores:

  • show_temporary_boss_bar() -- usa player:show_boss_bar(title, color, style, duration) en su lugar.
  • run_command_as_player() -- usa player:run_command(command) en su lugar.
  • em.location(...) -- el nombre del método es incorrecto. Usa em.create_location(x, y, z) en su lugar, o context.boss:get_location() / context.player.current_location.
  • em.vector(...) -- el nombre del método es incorrecto. Usa em.create_vector(x, y, z) en su lugar, o tablas simples {x=0, y=1, z=0}.
  • em.zone.sphere(...) -- el nombre del método es incorrecto. Usa em.zone.create_sphere_zone(radius) en su lugar, o una tabla de definición de zona como {kind = "sphere", radius = 5, origin = location}.
  • entity:teleport_to(...) -- usa entity:teleport_to_location(location).
  • entity:set_velocity(...) -- usa entity:set_velocity_vector(vector).
  • entity:set_facing(...) -- usa entity:face_direction_or_location(direction_or_location).

En caso de duda, consulta las páginas de Referencia de la API (Jefes y entidades, Mundo y entorno, Zonas y objetivos). Si no está documentado ahí, no existe.


Consejos de migración para autores de EliteScript

Si ya escribes buenos EliteScripts, la forma más fácil de aprender los poderes en Lua es:

  1. Sigue pensando en términos de eventos, objetivos, zonas, vectores relativos y partículas. Los conceptos son los mismos: solo cambia la sintaxis. Los eventos de EliteScript se convierten en nombres de hooks como on_spawn u on_boss_damaged_by_player. Los objetivos y las zonas se pasan como tablas a context.script usando los mismos nombres de campos documentados en las páginas de Zonas de EliteScript y Objetivos de EliteScript.

  2. Lleva tu flujo de control a Lua. Las tiradas aleatorias, las funciones auxiliares compartidas, los bucles, el estado persistente (context.state) y la planificación de tareas (context.scheduler) son las cosas que Lua añade y que el EliteScript puro no puede hacer fácilmente. Comienza convirtiendo a Lua un poder con ramificaciones o condicionales mientras mantienes todo lo demás igual.

  3. Usa context.script para la selección de objetivos y la geometría de zonas. Las utilidades de script aceptan los mismos nombres de campos que EliteScript (targetType, shape, Target, Target2, range, offset, coverage), así que puedes seguir usando la documentación existente de EliteScript como referencia para esas especificaciones. Esto te permite aprovechar patrones familiares mientras ganas la flexibilidad de Lua para la capa de lógica.


Ruta de progresión para principiantes

Si quieres aprender este sistema desde cero, esta progresión funciona bien:

  1. Escribe un archivo solo con api_version = 1 y on_spawn.
  2. Haz que el jefe envíe un mensaje o reproduzca un sonido.
  3. Añade un enfriamiento con context.cooldowns.
  4. Añade un hook activado por el jugador, como on_boss_damaged_by_player.
  5. Añade una acción retardada con context.scheduler:run_after(...).
  6. Añade una consulta de zona nativa de Lua simple o un context.script:target(...) simple.
  7. Solo entonces pasa a ataques rotatorios, máquinas de estados y mecánicas de varios pasos.

Cada paso se apoya en el anterior, y puedes probar en cada etapa. No intentes escribir un jefe de varias fases como tu primer poder en Lua.


Próximos pasos

  • Primeros pasos -- estructura de archivos, hooks, recorrido del primer poder, plantillas para copiar y pegar
  • Scripts de NPC -- scripts de proximidad, interacción y ciclo de vida de los NPC
  • Ejemplos y patrones -- poderes funcionales completos que puedes estudiar y adaptar