Pular para o conteúdo principal

Scripting Lua: Resolução de Problemas

webapp_banner.jpg

Esta página aborda problemas comuns que pode encontrar ao escrever ou depurar poderes em Lua, além de conselhos de migração para autores que vêm do EliteScript. Se está a depurar scripts de NPC, veja Scripts de NPC. Se procura exemplos funcionais, veja Exemplos e Padrões. Se está apenas a começar, veja Começar.

Motor Lua Partilhado

O EliteMobs usa o motor de scripting Lua do MagmaCore, partilhado entre os plugins da Nightbreak. Para documentação sobre conceitos partilhados como a sandbox, o agendador (scheduler), as zonas, a API de mundo, as tabelas de entidades e os métodos de UI do jogador, veja a página Motor de Scripting Lua do MagmaCore.


Problemas Comuns

1. O poder não carrega de todo

Verifique a consola do servidor à procura de erros quando o servidor arranca. A causa mais comum é um erro de sintaxe Lua (falta de end, parênteses por fechar, etc.). Verifique também que o ficheiro termina em .lua e está colocado no diretório powers correto.

2. O hook nunca dispara

Verifique que o nome do hook está escrito exatamente como listado na lista de hooks. Erros comuns: on_boss_hit (errado) vs. on_boss_damaged_by_player (correto), ou on_tick (errado) vs. on_game_tick (correto).

3. context.player é nil

Nem todos os hooks fornecem um jogador. on_spawn, on_game_tick e on_exit_combat não têm um jogador. on_enter_combat fornece context.player (o jogador que acionou o combate). Em on_boss_damaged (dano genérico), quem causa o dano pode não ser um jogador. Adicione sempre uma proteção contra nil antes de usar context.player.

4. Timeout / orçamento de execução excedido

Se um hook ou callback demorar demasiado tempo, o poder é automaticamente desativado para evitar lag. A mensagem da consola tem este aspeto:

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

Causas comuns: iterar sobre demasiadas entidades, criar demasiadas zonas por tick, ou executar operações de string dispendiosas em on_game_tick. Mova o trabalho dispendioso para trás de uma barreira de cooldown ou reduza o trabalho feito por chamada.

5. O callback do agendador usa dados desatualizados

Provavelmente está a usar o context exterior em vez do parâmetro do callback. Mude function() ... context.boss ... end para function(tick_context) ... tick_context.boss ... end.

6. A consulta de zona não devolve entidades

Verifique novamente a definição da zona. Para zonas nativas, garanta que kind está em minúsculas ("sphere", não "SPHERE"). Para utilitários de script, garanta que shape está em maiúsculas ("CONE", não "cone"). Verifique também que origin ou Target realmente resolve para uma localização válida.

7. As partículas não aparecem

Verifique que o nome da partícula é um valor válido do enum Particle do Bukkit em MAIÚSCULAS. Erro comum: "flame" (errado) vs. "FLAME" (correto). Verifique também que amount é pelo menos 1 e que a localização está num chunk carregado.

8. O cooldown parece não funcionar

Certifique-se de que está a usar check_local(key, duration) (que verifica E define numa única chamada), e não local_ready(key) seguido de um set_local(duration, key) separado. Se usar local_ready sozinho, apenas verifica mas nunca define o cooldown.

9. O boss continua a usar o poder depois de morrer

Adicione lógica de limpeza em on_exit_combat e/ou on_death para cancelar as tarefas do agendador. Se o boss morrer, o on_exit_combat deverá disparar, mas adicionar limpeza explícita em ambos os hooks é mais seguro.


Ler as Mensagens de Erro

Quando algo corre mal num poder Lua, a consola imprime um bloco de erro amigável prefixado com [Lua]. Estas mensagens dizem-lhe exatamente qual o ficheiro, qual a linha, qual o hook e o que correu mal -- em linguagem simples. Leia sempre a mensagem completa antes de depurar.

Um erro típico tem este aspeto:

[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.

O sistema traduz os erros comuns de Lua para linguagem simples. Eis os mais comuns:

Erro Lua brutoO que a consola lhe diz
attempt to call nilTentou chamar um método ou função que não existe. Verifique se há erros de escrita no nome do método, ou certifique-se de que está a usar : (dois-pontos) para chamadas de método, não . (ponto).
index expected, got nilTentou aceder a um campo de algo que é nil. Verifique se código anterior o inicializou.
attempt to indexTentou aceder a uma propriedade de um valor nil ou inválido.
bad argumentMostra os detalhes específicos da incompatibilidade do argumento (tipo esperado vs. tipo real).
Timeout<filename> demorou Xms em 'hook_name' (limite: 50ms) -- script desativado para evitar lag.
dica

Quando vir um erro [Lua] na consola, a mensagem de erro diz-lhe exatamente qual o ficheiro, qual a linha, qual o hook e o que correu mal em linguagem simples. Leia a mensagem completa antes de mergulhar no código -- geralmente aponta-lhe diretamente para a solução.


Não Assuma que Existem Aliases Não Documentados

A API Lua expõe um conjunto específico de nomes de métodos. Se está a escrever poderes à mão ou com assistência de IA, não assuma que existem nomes abreviados ou alternativos. Os seguintes são exemplos de nomes que não existem e que irão causar erros:

  • show_temporary_boss_bar() -- use player:show_boss_bar(title, color, style, duration) em vez disso.
  • run_command_as_player() -- use player:run_command(command) em vez disso.
  • em.location(...) -- o nome do método está errado. Use em.create_location(x, y, z) em vez disso, ou context.boss:get_location() / context.player.current_location.
  • em.vector(...) -- o nome do método está errado. Use em.create_vector(x, y, z) em vez disso, ou tabelas {x=0, y=1, z=0} simples.
  • em.zone.sphere(...) -- o nome do método está errado. Use em.zone.create_sphere_zone(radius) em vez disso, ou uma tabela de definição de zona como {kind = "sphere", radius = 5, origin = location}.
  • entity:teleport_to(...) -- use entity:teleport_to_location(location).
  • entity:set_velocity(...) -- use entity:set_velocity_vector(vector).
  • entity:set_facing(...) -- use entity:face_direction_or_location(direction_or_location).

Na dúvida, consulte as páginas da Referência da API (Bosses e Entidades, Mundo e Ambiente, Zonas e Alvos). Se não estiver documentado lá, não existe.


Conselhos de Migração Para Autores de EliteScript

Se já escreve bons EliteScripts, a forma mais fácil de aprender os poderes em Lua é:

  1. Continue a pensar em termos de eventos, alvos, zonas, vetores relativos e partículas. Os conceitos são os mesmos -- só a sintaxe muda. Os eventos do EliteScript tornam-se nomes de hook como on_spawn ou on_boss_damaged_by_player. Os alvos e zonas são passados como tabelas para context.script usando os mesmos nomes de campo documentados nas páginas Zonas do EliteScript e Alvos do EliteScript.

  2. Mova o seu fluxo de controlo para Lua. Sorteios aleatórios, funções auxiliares partilhadas, ciclos, estado persistente (context.state) e agendamento de tarefas (context.scheduler) são as coisas que o Lua acrescenta e que o EliteScript puro não consegue fazer facilmente. Comece por converter um poder com ramificações ou condições para Lua, mantendo tudo o resto igual.

  3. Use context.script para a geometria de alvos e zonas. Os utilitários de script aceitam os mesmos nomes de campo que o EliteScript (targetType, shape, Target, Target2, range, offset, coverage), por isso pode continuar a usar a documentação existente do EliteScript como referência para essas especificações. Isto permite-lhe aproveitar padrões familiares enquanto ganha a flexibilidade do Lua para a camada da lógica.


Caminho de Progressão para Iniciantes

Se quer aprender este sistema do zero, esta progressão funciona bem:

  1. Escreva um ficheiro apenas com api_version = 1 e on_spawn.
  2. Faça o boss enviar uma mensagem ou tocar um som.
  3. Adicione um cooldown com context.cooldowns.
  4. Adicione um hook acionado pelo jogador, como on_boss_damaged_by_player.
  5. Adicione uma ação atrasada com context.scheduler:run_after(...).
  6. Adicione uma consulta de zona Lua nativa simples ou um context.script:target(...) simples.
  7. Só então avance para ataques rotativos, máquinas de estados e mecânicas de múltiplos passos.

Cada passo constrói sobre o anterior, e pode testar em todas as fases. Não tente escrever um boss de múltiplas fases como o seu primeiro poder Lua.


Próximos Passos

  • Começar -- estrutura de ficheiros, hooks, primeiro poder passo a passo, modelos para copiar e colar
  • Scripts de NPC -- scripts de proximidade, interação e ciclo de vida de NPCs
  • Exemplos e Padrões -- poderes funcionais completos que pode estudar e adaptar