Scripting Lua: Primeiros Passos
O que vai aprender
Esta página ensina a escrever o seu primeiro poder Lua para EliteMobs, desde um ficheiro vazio até uma habilidade de boss funcional. No final, compreenderá hooks, context, tempos de recarga e a estrutura geral de cada ficheiro de poder Lua.
Depois de se sentir confortável com o básico, continue com as páginas complementares:
- Hooks e Ciclo de Vida -- nomes de hooks, ordem de execução e disponibilidade do context
- Scripts de NPC -- ciclo de vida, interação e hooks de proximidade dos NPCs
- Boss e Entidades -- as APIs
context.boss,context.player,context.playersecontext.entities - Mundo e Ambiente -- partículas, sons, relâmpagos, spawning e a API
context.world - Zonas e Alvos -- zonas Lua nativas, utilitários de Script para alvos e as APIs
context.zones/context.script - Exemplos e Padrões -- poderes completos e funcionais para estudar e adaptar
- Enums e Valores -- links para os Javadocs do Spigot para Particle, Material, PotionEffectType e outras constantes de string
- Resolução de Problemas -- erros comuns, dicas de depuração e a checklist QC
- Programas de Comportamento -- IA de mobs escrita em Lua, selecionada com a definição
behaviorde um boss
A maioria dos poderes incluídos no EliteMobs usa Lua, mas os nomes e comportamentos dos métodos auxiliares podem mudar entre versões. Verifique os poderes personalizados num servidor de preparação antes de os usar em produção e consulte a referência após atualizar.
Lua não substitui o sistema YAML eliteScript: existente, e o EliteScript não foi descontinuado.
- Use EliteScript quando quiser scripting declarativo baseado em YAML com as páginas existentes de Ações, Alvos, Zonas, Condições, Tempos de Recarga e Vetores Relativos.
- Use ficheiros de poder Lua quando precisar de variáveis, loops, seleção aleatória, funções auxiliares reutilizáveis, estado persistente por boss e um fluxo de programação mais tradicional.
A API Script Utilities (context.script) passa as tabelas de especificação às classes de alvos, zonas, vetores relativos e partículas do EliteScript. Os seus analisadores e limitações continuam a aplicar-se, mas os objetos Lua autónomos não dispõem de todos os contextos das ações YAML. Alvos herdados e posições de aterragem precisam de contexto que esses objetos não têm. Os auxiliares Lua também não aplicam automaticamente esperas, condições ou caches de alvos das ações YAML. A página Zonas e Alvos explica estas diferenças.
O que são poderes Lua
Poderes Lua são ficheiros .lua autónomos que ficam na árvore normal de powers do EliteMobs e são referenciados exatamente como ficheiros de poder normais.
Esta página ensina os poderes Lua de boss. Os scripts Lua de NPC são uma funcionalidade separada: ficam em plugins/EliteMobs/npc_scripts/, ligam-se através de uma lista scripts: na configuração do NPC e usam hooks específicos de NPC como on_npc_proximity_enter. Veja Scripts de NPC.
Para que servem os poderes Lua
Os poderes Lua destacam-se quando precisa de:
- Rotações de ataque e seleção aleatória de movimentos
- Estado persistente entre hooks com
context.state - Ações atrasadas e repetidas sem construir tudo com esperas YAML
- Funções auxiliares personalizadas partilhadas num ficheiro
- Ramificações complexas que seriam inconvenientes em EliteScript puro
- Lógica de boss que ainda quer reutilizar definições de alvos e zonas estilo EliteScript
Se o seu poder é principalmente "ativar evento, executar algumas ações com script", os docs existentes de EliteScript continuam a ser a forma mais rápida e clara de o construir. Se o seu poder precisa de fluxo de programa real, Lua é a ferramenta para esse trabalho.
Para quem é esta página
Esta página foi escrita para três tipos de leitores:
- Alguém que já conhece EliteScript e quer aprender Lua sem aprender "programação real" de uma só vez
- Alguém novo no scripting de EliteMobs que precisa de uma referência completa com nomes exatos
- Alguém que usa IA para redigir poderes e precisa de detalhes suficientes para saber quando a IA inventou algo falso
Não precisa de se tornar um programador Lua completo antes de escrever poderes úteis. Para a maioria dos poderes práticos de EliteMobs, só precisa de saber:
- Como colocar um hook válido na tabela retornada
- Como ler valores do
context - Como parar cedo com
if ... then return end - Como chamar alguns métodos auxiliares corretamente
- Como copiar as especificações corretas de alvos e zonas dos docs existentes de EliteScript
Modelo Mental: EliteScript vs. Lua
Se conhece EliteScript, esta comparação é a forma mais rápida de entender poderes Lua:
| Se pensa em termos de EliteScript | Em Lua isso normalmente significa |
|---|---|
| Events | Nomes de hooks como on_spawn ou on_boss_damaged_by_player |
| Cooldowns | context.cooldowns (ver context.cooldowns abaixo) |
| Ações | Chamadas diretas de métodos como context.world:spawn_particle_at_location(...) ou context.script:damage(...) |
| Alvos | context.script:target({...}) -- usa os nomes de campo de EliteScript Target |
| Zonas | context.script:zone({...}) ou auxiliares nativos context.zones -- ver Zonas e Alvos |
| Vetores relativos | context.script:relative_vector({...}) ou context.vectors |
| Fluxo do script | As suas próprias instruções if de Lua, funções auxiliares, temporizadores e estado |
A maior diferença é esta:
- EliteScript diz o que deveria acontecer em YAML.
- Lua permite decidir quando, porquê e qual ramo deveria executar usando código.
Se EliteScript parece "configuração", Lua parece "configuração mais tomada de decisão".
Mini introdução a Lua
A maioria dos poderes usa alguns conceitos Lua: variáveis (local x = 5), funções (function foo() end), condições if (if x then ... end), tabelas ({key = value}) e nil (ausência de valor). Os pontos e vírgulas são opcionais. As tabelas usam chavetas; as funções e os blocos de controlo terminam com end.
Para uma explicação completa com exemplos, veja o Motor de Scripting Lua do MagmaCore -- Mini introdução a Lua. Essa introdução é partilhada por todos os plugins Nightbreak, por isso aprendê-la uma vez serve em todo o lado.
O seu primeiro poder funcional, construído passo a passo
Se é completamente novo, esta é a melhor progressão para a "primeira vitória".
Antes do Passo 1: Guarde e anexe a um boss
Guarde o ficheiro Lua na pasta normal de poderes do EliteMobs, por exemplo:
plugins/
EliteMobs/
powers/
first_test.lua
Depois adicione esse nome de ficheiro à configuração do boss usando a lista normal powers::
powers:
- first_test.lua
O ciclo completo para iniciantes é:
- Guardar o ficheiro em
plugins/EliteMobs/powers/ - Adicionar o nome do ficheiro
.luaà listapowers:do boss - Executar
/em reloadpara carregar os ficheiros alterados e depois invocar um novo boss de teste. O recarregamento afeta o plugin e o conteúdo ativo; use um servidor de preparação. - Acionar o hook e verificar o resultado visível e a consola. Guardar o ficheiro ou invocar outro boss sem recarregar não atualiza uma definição já carregada.
Se precisar de mais contexto sobre ficheiros de boss, listas de poderes ou como os bosses personalizados são estruturados, veja Criar Bosses Personalizados. Essa página explica a configuração dos bosses, incluindo a lista powers:.
Passo 1: Fazer o ficheiro carregar
return {
api_version = 1,
on_spawn = function(context)
end
}
O carregador valida a sintaxe Lua, a tabela devolvida e os nomes dos hooks. Um hook vazio não tem resultado visível; a ausência de erros não prova que o ficheiro foi encontrado ou associado ao boss. Adicione o som do Passo 2 e invoque um novo boss para verificar esse percurso.
Passo 2: Fazer o boss fazer algo visível
return {
api_version = 1,
on_spawn = function(context)
context.boss:play_sound_at_self("entity.blaze.ambient", 1.0, 1.0)
end
}
Agora tem a prova mais importante para iniciantes: o seu hook está a disparar.
Passo 3: Reagir ao golpe de um jogador
return {
api_version = 1,
on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end
context.player:send_message("&eYou hit the boss.")
end
}
Isto ensina três ideias fundamentais:
on_boss_damaged_by_playeré o nome do hookcontext.playeré o jogador envolvido nesse hookreturnsai cedo quando os dados necessários estão em falta
Passo 4: Prevenir spam com um tempo de recarga
return {
api_version = 1,
on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end
if not context.cooldowns:local_ready("hello_message") then
return
end
context.player:send_message("&eYou woke up the boss.")
context.cooldowns:set_local(60, "hello_message")
end
}
Cada tick do tempo de recarga local representa 50 milissegundos de tempo decorrido: 60 corresponde a três segundos mesmo quando os TPS do servidor baixam. Este tempo de recarga com nome é partilhado pelos poderes deste boss, não por bosses diferentes. Prefixe as chaves com o nome do poder ao combinar poderes personalizados. Este exemplo verifica apenas a chave local, não o tempo de recarga global do boss.
Passo 5: Adicionar um efeito real
return {
api_version = 1,
on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end
if not context.cooldowns:local_ready("shock") then
return
end
context.cooldowns:set_local(100, "shock")
context.player:send_message("&cStatic jumps from the boss into your armor!")
context.world:strike_lightning_at_location(context.player.current_location)
end
}
Isto cria um relâmpago real no mundo, não apenas um efeito visual. O tempo de recarga é definido antes do efeito para que os eventos provocados por ele o vejam. Este tempo de recarga local de 100 ticks corresponde a cinco segundos de tempo decorrido.
Primeiro fluxo de trabalho real
Ao construir um novo poder Lua do zero, use esta ordem:
- Criar o ficheiro e fazer
on_spawnfuncionar. - Mudar para o hook que realmente quer.
- Confirmar que o hook tem os dados esperados, como
context.playeroucontext.event. - Adicionar primeiro uma mensagem ou som, antes de dano ou partículas.
- Adicionar tempos de recarga.
- Adicionar um efeito de jogo.
- Só depois disso, adicionar auxiliares, estado, lógica de agendador ou zonas.
Essa ordem torna a depuração muito mais fácil porque só muda uma coisa de cada vez.
Onde ficam os ficheiros Lua
Coloque ficheiros .lua na mesma árvore de pastas que os ficheiros de poder .yml normais:
plugins/
EliteMobs/
powers/
mycoolpower.lua
attack_push.yml
subfolder/
myotherpower.lua
Os poderes Lua são descobertos automaticamente a partir dos diretórios de poderes que o EliteMobs já carrega.
Metadados YAML opcionais
Um poder personalizado mycoolpower.lua pode ter um mycoolpower.yml ou mycoolpower.yaml carregado ao lado, no mesmo diretório. A descoberta lê os campos isEnabled, effect, powerType, powerCooldown e globalCooldown desse YAML; isEnabled: false impede o registo desse ficheiro Lua personalizado. Sem metadados correspondentes, não usa efeito e recebe o tipo de poder MISCELLANEOUS. Os hooks Lua continuam a controlar quando os ataques ocorrem e têm de verificar os seus próprios tempos de recarga.
Um ficheiro YAML carregado com o mesmo nome base, ao lado de um poder Lua incluído, aplica os mesmos campos a esse poder. Sem ele, o poder incluído mantém os metadados fornecidos. Consulte Criar Poderes para os campos dos metadados.
Como os bosses referenciam poderes Lua
Os ficheiros de boss continuam a usar a lista normal powers::
powers:
- attack_push.yml
- mycoolpower.lua
Não é necessário nenhum campo especial. Os poderes Lua não são carregados através de eliteScript:.
Regras de nomenclatura de ficheiros
- Os bosses referenciam poderes Lua pelo nome de ficheiro, não pelo caminho da pasta.
- O EliteMobs atualmente regista os poderes Lua descobertos apenas pelo nome base.
- Evite nomes duplicados como
powers/fire.luaepowers/bosses/fire.lua. O EliteMobs carrega apenas o ficheiro cujo caminho completo vem primeiro por ordem alfabética e regista um aviso sobre a cópia ignorada, a não ser que os dois ficheiros sejam idênticos.
Poderes Lua pré-fabricados
A maioria dos poderes incluídos usa Lua. O código atual define 68 poderes Lua concretos pré-fabricados, instalados em plugins/EliteMobs/powers/ quando faltam, incluindo attack_fire.lua, frost_cone.lua, meteor_shower.lua e shield_wall.lua. Leia-os como exemplos, tendo em conta que uma cópia local editada pode diferir da versão fornecida pelo plugin.
Os ficheiros pré-fabricados só são escritos quando faltam, pelo que as alterações sobrevivem aos reinícios. Quando uma atualização altera um poder incluído, uma cópia não editada de uma versão anterior fornecida é movida para plugins/MagmaCore/outdated files/EliteMobs/ no arranque, e a versão atual é escrita no seu lugar; as cópias editadas mantêm-se. Remover um ficheiro incluído permite que a próxima inicialização do plugin regenere a versão fornecida; preserve as alterações personalizadas de que ainda precisa antes de o substituir.
Fragmentos de código partilhados
Os poderes Lua de boss podem incluir um ficheiro auxiliar com uma diretiva de comentário numa linha própria:
-- @include helpers/common.inc
O carregador insere o texto do ficheiro referenciado antes de validar o poder. Os caminhos são relativos ao ficheiro que contém a diretiva e têm de permanecer no diretório do ficheiro principal do poder, incluindo após resolver ligações simbólicas. São rejeitadas inclusões circulares, profundidades acima de 16 níveis, ficheiros maiores que 4 MiB e código expandido acima de 4 194 304 caracteres.
Use uma extensão diferente de .lua, como .inc, para que a descoberta não tente também carregar os fragmentos como poderes autónomos. Declare as funções auxiliares antes da tabela return final. Esta diretiva é específica do carregamento de poderes de boss; não ativa require nem acesso ao sistema de ficheiros, e os scripts de NPC não a processam.
Os nomes .yml antigos continuam a resolver
As configurações de boss que referenciam attack_fire.yml continuam a funcionar. Para uma referência .yml ou .yaml, o EliteMobs procura primeiro um poder Lua carregado com o mesmo nome base e depois recorre ao nome literal. Um ficheiro inválido ou desativado não é uma substituição carregada. Mantenha a grafia e as maiúsculas/minúsculas do nome base consistentes. Para personalizar um poder Lua incluído, edite o ficheiro existente e recarregue; criar nomes base duplicados noutros diretórios não é um mecanismo de substituição fiável.
Contrato mínimo do ficheiro
Cada poder Lua deve retornar uma tabela com return. Essa tabela é intencionalmente restrita.
Campos de nível superior obrigatórios e opcionais
| Campo | Obrigatório | Tipo | Notas |
|---|---|---|---|
api_version | Sim | Number | Use o inteiro 1 |
priority | Não | Number | Use um inteiro. Os valores menores executam primeiro; predefinição 0. Os poderes YAML usam prioridade 0 |
| Chaves de hook suportadas | Não | Function | Use um dos nomes exatos da Referência de Hooks |
Regras de validação
- O ficheiro deve retornar uma tabela.
api_versioné obrigatório e atualmente deve ser1.prioritydeve ser numérico se presente.- Cada chave de nível superior adicional deve ser um nome de hook suportado.
- Cada chave de hook deve apontar para uma função.
- Chaves de nível superior desconhecidas são rejeitadas.
Isto significa que funções auxiliares e constantes locais devem ficar acima do return final, não dentro da tabela retornada, a menos que sejam hooks reais.
O corpo do ficheiro é avaliado na validação e novamente num ambiente Lua separado para cada instância de execução do poder num boss. context só existe como argumento de um hook ou callback, não como variável global do ficheiro. As variáveis locais podem persistir nessa instância, mas os valores da validação não são transferidos para ela. O ambiente remove acesso a ficheiros, sistema operativo e Java, bem como require; use a diretiva de inclusão para fragmentos de código. Um erro não tratado num hook ou callback fecha essa instância e cancela as tarefas que lhe pertencem; não há nova tentativa no golpe seguinte. Corrija o ficheiro e recarregue antes de tentar novamente.
Templates para copiar e colar
Menor poder Lua válido
return {
api_version = 1,
on_spawn = function(context)
end
}
Template inicial recomendado
local ATTACK_COOLDOWN = "my_attack"
local function can_run_attack(context)
return context.cooldowns:local_ready(ATTACK_COOLDOWN)
and context.cooldowns:global_ready()
end
local function run_attack(context)
context.boss:play_sound_at_self("entity.blaze.shoot", 1.0, 1.0)
context.cooldowns:set_local(100, ATTACK_COOLDOWN)
context.cooldowns:set_global(20)
end
return {
api_version = 1,
priority = 0,
on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end
if not can_run_attack(context) then
return
end
run_attack(context)
end
}
Layout para ficheiros maiores
local CONSTANT_NAME = "value"
local function helper_function(context)
end
local function another_helper(context, value)
end
return {
api_version = 1,
priority = 0,
on_spawn = function(context)
end,
on_enter_combat = function(context)
end,
on_boss_damaged_by_player = function(context)
end,
on_exit_combat = function(context)
end
}
Hooks
Hooks são funções com nomes especiais na tabela que retorna. O EliteMobs chama-os quando algo acontece -- o boss aparece, recebe dano, entra em combate, etc. Já viu on_spawn e on_boss_damaged_by_player no tutorial acima.
Os hooks mais comuns para começar são on_spawn, on_boss_damaged_by_player, on_enter_combat e on_exit_combat. Para a lista completa de todos os hooks, que chaves de context estão disponíveis em cada um e como funciona a ordem de execução, consulte Hooks e Ciclo de Vida.
O que é context?
Cada função de hook recebe um argumento chamado context. Pense nele como uma caixa de ferramentas que o EliteMobs lhe entrega cada vez que algo acontece -- contém tudo o que precisa para interagir com o boss, o jogador, o mundo, os tempos de recarga e mais.
on_boss_damaged_by_player = function(context)
-- context.boss = the boss that was hit
-- context.player = the player who hit it
-- context.world = tools for spawning particles, sounds, etc.
-- context.cooldowns = cooldown management
-- context.state = your own persistent storage
end
Não é você que cria context -- o EliteMobs cria-o e passa-o ao seu hook. Basta ler dele e chamar métodos nele.
Cada hook recebe um novo context. A tabela context.state pertence à instância de execução deste poder neste boss e pode guardar dados para hooks e callbacks posteriores. Os outros poderes têm estado separado; fechar ou recarregar a instância descarta o seu estado.
APIs chave de context
Aqui estão as APIs mais importantes que usará de context:
-
context.state-- Uma tabela Lua mantida para a instância deste poder neste boss. Guarde fases, identificadores de tarefas e indicadores entre hooks. Não é preservada após remover ou recarregar a instância. -
context.log-- Registo na consola comlog:info(msg),log:warn(msg)elog:debug(msg). Inclua o nome do poder na mensagem para distinguir a saída de vários poderes. -
context.cooldowns-- Tempos de recarga com nome e um tempo global partilhados pelos poderes do boss. As chaves explícitas partilham o mesmo espaço de nomes nesse boss; prefixe-as com o nome do poder. Uma chavenilseleciona o tempo de recarga predefinido deste nome de ficheiro Lua.cooldowns:check_local(key, ticks)verifica e define o tempo local numa chamada; não verifica nem define o global. Consulte Hooks e Ciclo de Vida para a API completa. -
context.scheduler--scheduler:run_after(ticks, callback)adia um único callback.scheduler:run_every(ticks, callback)repete sem esperar pelo primeiro intervalo; usescheduler:run_repeating(delay, interval, callback)para um atraso inicial explícito. Os callbacks recebem um contexto novo sem o evento ou jogador que os originou. Use esse argumento e guarde deliberadamente o UUID ou a posição necessária; não suponha quectx.playercontinua disponível após o atraso. Cancele repetições exclusivas de combate emon_exit_combat; caso contrário, as tarefas continuam até serem canceladas ou a instância fechar. Consulte Hooks e Ciclo de Vida. -
context.boss/context.player-- O boss e o jogador envolvidos no evento atual. Consulte Boss e Entidades para todos os campos e métodos. -
context.world-- Gerar partículas, entidades, sons, relâmpagos, blocos. Consulte Mundo e Ambiente. -
context.zones/context.script-- Geometria de zonas, alvos, dano, partículas. Consulte Zonas e Alvos.
Sintaxe de métodos: : vs. .
Para uma explicação de : e ., consulte o Motor de Scripting Lua do MagmaCore. Os métodos de entidades e os serviços partilhados aceitam ambas as formas. context.event.set_damage_amount(value) e context.event.multiply_damage_amount(multiplier) exigem ponto; os dois pontos passam a tabela do evento como primeiro argumento. context.event:transfer_damage(target, amount) aceita dois pontos. Construtores globais como em.create_location(...) usam ponto.
Próximos passos
Agora que conhece o básico, explore o resto da documentação de scripting Lua:
- Hooks e Ciclo de Vida -- nomes de hooks, ordem de execução e que chaves de context estão disponíveis em cada hook
- Scripts de NPC -- localização dos ficheiros, hooks, eventos de proximidade e
context.npc - Boss e Entidades --
context.boss,context.player,context.players,context.entitiesecontext.event - Mundo e Ambiente -- partículas, sons, relâmpagos, spawning e
context.world - Zonas e Alvos -- zonas Lua nativas, utilitários de Script para alvos/zonas/partículas e vetores relativos
- Exemplos e Padrões -- poderes completos e funcionais com explicações
- Enums e Valores -- links para os Javadocs do Spigot para Particle, Material, PotionEffectType e mais
- Resolução de Problemas -- erros comuns, dicas de depuração e checklist QC
Para scripting baseado em YAML, as páginas de EliteScript continuam a ser a referência canónica:
