Scripting Lua: Scripts de NPC
Os scripts Lua de NPC do EliteMobs são ficheiros .lua autónomos que se ligam a configurações de NPC. São separados dos poderes Lua de boss: os poderes de boss ficam em plugins/EliteMobs/powers/, enquanto os scripts de NPC ficam em plugins/EliteMobs/npc_scripts/.
Os scripts de NPC correm agora no mesmo runtime de scripting unificado do MagmaCore que os poderes de boss, os props do FreeMinecraftModels e os itens do FMM. Isso significa que um script de NPC recebe toda a superfície de scripting partilhada -- context.world (incluindo strike_lightning), context.zones, context.scheduler, context.cooldowns, context.log, context.event e context.player -- mais uma tabela context.npc específica de NPC. Tudo o que o MagmaCore expõe aos scripts está também disponível aqui.
Os scripts Lua de NPC ainda são experimentais. Os hooks específicos de NPC e os auxiliares de context.npc podem mudar. As tabelas partilhadas (context.world, context.zones, context.scheduler, context.cooldowns, context.log, context.event, context.player) são as mesmas documentadas no Motor de Scripting e na Referência da API Lua.
Localização dos ficheiros
Crie os ficheiros de script de NPC em:
plugins/
EliteMobs/
npc_scripts/
wave.lua
As subpastas são percorridas, recursivamente. No entanto, os scripts são registados apenas pelo nome do ficheiro, por isso npc_scripts/wave.lua e npc_scripts/town/wave.lua colidem -- mantenha os nomes base únicos em toda a árvore.
A extensão .lua é opcional numa configuração de NPC: tanto - wave como - wave.lua resolvem para wave.lua. Se uma configuração de NPC referenciar um script que não existe, o EliteMobs regista um aviso e o NPC continua a aparecer.
Associar scripts a NPCs
Adicione uma lista scripts: à configuração do NPC:
scripts:
- wave.lua
Vários scripts podem ser associados a um só NPC:
scripts:
- wave.lua
- greeting_particles.lua
Os scripts correm por ordem de prioridade. Valores de priority mais baixos correm primeiro. Se a prioridade for omitida, assume o valor 0.
Estrutura do script
Todos os scripts de NPC têm de devolver uma tabela:
return {
api_version = 1,
priority = 0,
on_spawn = function(context)
context.state.spawned = true
context.npc:play_model_animation("idle")
end
}
Apenas estes campos de topo são aceites:
| Campo | Tipo | Notas |
|---|---|---|
api_version | number | Obrigatório. Tem de ser 1. |
priority | number | Opcional. Valores mais baixos correm primeiro. |
on_spawn | function | Corre depois de o NPC aparecer. |
on_remove | function | Corre quando o NPC é removido. |
on_game_tick | function | Corre a cada tick do servidor enquanto o NPC for válido. Mantenha isto muito leve. |
on_npc_interact | function | Corre quando um jogador interage com o NPC. |
on_npc_proximity_enter | function | Corre uma vez quando um jogador entra no raio de ativação deste NPC. |
on_npc_proximity_leave | function | Corre uma vez quando um jogador sai do raio de ativação deste NPC. |
on_zone_enter | function | Corre quando um jogador entra numa zona que este script está a vigiar (ver context.zones). |
on_zone_leave | function | Corre quando um jogador sai de uma zona vigiada. |
A vigilância de zonas só segue jogadores -- mobs e outras entidades nunca acionam on_zone_enter / on_zone_leave.
Chaves de topo desconhecidas são rejeitadas durante o carregamento do script. As funções auxiliares devem ser declaradas como funções local acima da tabela devolvida.
Hooks de proximidade
Os hooks de proximidade de NPC usam o valor de configuração activationRadius do NPC.
| Hook | Dispara quando |
|---|---|
on_npc_proximity_enter | Um jogador passa de fora do raio de ativação do NPC para dentro dele. |
on_npc_proximity_leave | Um jogador passa de dentro do raio de ativação do NPC para fora dele. |
Estes hooks são acompanhados por NPC e por jogador pelo scanner de proximidade do lado do servidor. Estar perto de um NPC não impede outro NPC de disparar o seu próprio evento de entrada, e permanecer dentro do raio não gera eventos de entrada repetidos.
O comportamento normal de saudação, diálogo e indicador de missão continua a funcionar. O hook Lua acrescenta comportamento por cima dele.
Superfície de scripting partilhada
Como os scripts de NPC correm no runtime unificado, todos os hooks de NPC recebem também as tabelas de context partilhadas do MagmaCore usadas pelos scripts do FreeMinecraftModels. Os poderes de boss do EliteMobs correm no mesmo runtime, mas usam variantes específicas de boss para várias tabelas. As listas completas de métodos estão na Referência da API Lua e no Motor de Scripting:
| Tabela | O que faz |
|---|---|
context.world | Efeitos e consultas de mundo: strike_lightning, spawn_particle, play_sound, set_block_at, place_temporary_block, spawn_entity, spawn_firework, get_nearby_entities, get_nearby_players, raycast, entre outros. São aceites tanto a forma por coordenadas (strike_lightning(x, y, z)) como a forma por tabela de localização (strike_lightning_at_location(loc)). |
context.zones | Cria zonas espaciais (create_sphere(x, y, z, radius), create_cylinder(x, y, z, radius, height), create_cuboid(x, y, z, xSize, ySize, zSize)) -- cada uma devolve um handle numérico. watch(handle, on_enter, on_leave) inicia o acompanhamento (os callbacks disparam os seus hooks on_zone_enter / on_zone_leave, e não as funções que passa); unwatch(handle) para-o. |
context.scheduler | run_later(ticks, fn), run_repeating(delay, interval, fn), cancel(task_id). |
context.cooldowns | Tempos de recarga partilhados do MagmaCore: local_ready, local_remaining, check_local, set_local, global_ready, set_global. |
context.log | info(msg), warn(msg), error(msg) — escreve na consola do servidor. |
context.event | O evento Bukkit atual, quando existe algum. Ver abaixo. |
context.player | O jogador que interage/aciona, quando presente. Ver abaixo. |
context.state | Uma tabela Lua simples que persiste para esta instância de script de NPC até o NPC ser removido. |
Exemplo: relâmpago ao interagir
return {
api_version = 1,
on_npc_interact = function(context)
-- NPC scripts can now reach the full world API.
context.world:strike_lightning_at_location(context.npc:get_location())
end
}
context.npc
context.npc está disponível em todos os hooks de NPC.
Campos
| Campo | Tipo | Notas |
|---|---|---|
name | string | Nome de exibição do NPC, vindo da configuração. |
filename | string | Nome do ficheiro de configuração do NPC. |
uuid | string | UUID do NPC em tempo de execução. |
activation_radius | number | Raio de ativação configurado. |
current_location | tabela de localização | Localização instantânea quando a entidade de suporte existe. |
entity_type | string | Tipo de entidade do Bukkit quando a entidade de suporte existe. |
Métodos
| Método | Argumentos | Devolve | Notas |
|---|---|---|---|
is_valid() | - | boolean | Se o NPC ainda tem uma entidade de suporte válida. |
get_location() | - | tabela de localização | Localização atual do NPC, ou a localização de spawn se a entidade não estiver disponível. |
get_eye_location() | - | tabela de localização | Localização atual dos olhos, ou a localização de spawn como alternativa. |
get_activation_radius() | - | number | Raio de ativação atualmente configurado. |
get_nearby_players(radius) | number | table | Wrappers de jogador dentro do raio a partir do NPC. |
face_direction_or_location(target) | vetor ou localização | nil | Vira-se para um vetor de direção ou na direção de uma localização/localização de jogador. |
say_greeting(player?) | jogador, UUID, nome ou nil | nil | Envia uma saudação configurada. Por omissão, usa o jogador que acionou, quando disponível. |
say_dialog(player?) | jogador, UUID, nome ou nil | nil | Envia o diálogo configurado. Por omissão, usa o jogador que acionou, quando disponível. |
say_farewell(player?) | jogador, UUID, nome ou nil | nil | Envia o texto de despedida configurado. Por omissão, usa o jogador que acionou, quando disponível. |
play_model_animation(name) | string | nil | Reproduz uma animação de modelo personalizado, se existir. Caso contrário, não faz nada de forma segura. |
patrol_pause() | - | booleano | Pausa a patrulha configurada. |
patrol_resume() | - | booleano | Termina uma espera ou caminhada temporária e retoma a patrulha. |
walk_to(x, y, z) | três números | booleano | Caminha até um deslocamento desde a origem e retoma a patrulha. Rotas longas são resolvidas automaticamente. |
hold(x, y, z) | três números | booleano | Caminha até um deslocamento e permanece ali. |
teleport(x, y, z) | três números | booleano | Teleporta quando o destino processa entidades. |
Os métodos de movimento devolvem false se o NPC não tiver uma patrulha configurada ou se o pedido não puder ser aceito. Consulte Patrulhas de NPCs e chefes.
context.player
context.player está disponível em on_npc_interact, on_npc_proximity_enter e on_npc_proximity_leave. É nil nos hooks de ciclo de vida que não envolvem um jogador.
É o wrapper de jogador partilhado do MagmaCore — a mesma tabela completa de entidade viva/jogador que os poderes de boss e os scripts do FMM usam, pelo que expõe muito mais do que o básico (vida, efeitos de poção, send_message, show_title, show_action_bar, get_held_item, raycasting, entre outros). Consulte a Referência da API Lua para a lista completa. Os mais usados aqui:
| Campo / Método | Notas |
|---|---|
name | Nome do jogador. |
uuid | UUID do jogador. |
current_location | Tabela da localização atual do jogador; é um campo, não um método. |
get_eye_location() | Localização atual dos olhos do jogador. |
send_message(text) | Envia uma mensagem de chat. Suporta códigos de cor. |
Verifique sempre se context.player é nil antes de o usar em funções auxiliares partilhadas.
entity_type está em minúsculasNas tabelas de entidade partilhadas do MagmaCore, entity_type é o nome do Bukkit em minúsculas ("player", "zombie"). Só context.npc.entity_type e as tabelas de entidade dos poderes de boss do EliteMobs usam a forma em maiúsculas. Compare sem distinguir maiúsculas de minúsculas se um script tiver de funcionar com ambos.
Campos do EliteMobs adicionados a todas as tabelas de entidade partilhadas
Enquanto o EliteMobs está em execução, acrescenta campos adicionais a todas as tabelas de entidade do MagmaCore -- context.player, os wrappers devolvidos por context.npc:get_nearby_players(...), e os que os scripts de props e itens do FreeMinecraftModels veem:
| Campo | Tipo | Notas |
|---|---|---|
is_elite | boolean | true se o EliteMobs regista a entidade como um elite |
is_custom_boss | boolean | true se for um Custom Boss (sempre false quando is_elite é false) |
is_significant_boss | boolean | true para um Custom Boss cujo multiplicador de vida seja superior a 1 -- na prática, a verificação de "isto é um boss a sério, não um reforço" |
elite | table ou nil | Presente apenas em elites. Ver abaixo |
A subtabela elite:
| Campo / Método | Tipo | Notas |
|---|---|---|
elite.level | number | Nível do elite |
elite.name | string ou nil | Nome de exibição do elite |
elite.health | number | Vida atual do elite (leitura em tempo real) |
elite.max_health | number | Vida máxima do elite (leitura em tempo real) |
elite.is_custom_boss | boolean | O mesmo valor do campo de nível superior |
elite.health_multiplier | number | Multiplicador de vida configurado |
elite.damage_multiplier | number | Multiplicador de dano configurado |
elite:remove() | — | Faz desaparecer o elite |
-- Warn the approaching player if a real boss is loose near this NPC
on_npc_proximity_enter = function(context)
if context.player == nil then return end
local here = context.npc:get_location()
local nearby = context.world:get_nearby_entities(here.x, here.y, here.z, 40)
for i = 1, #nearby do
if nearby[i].is_significant_boss then
context.player:send_message("&cA boss is nearby: " .. tostring(nearby[i].elite.name))
return
end
end
end
Estes campos não aparecem nos wrappers de entidade dos poderes de boss do EliteMobs, que são construídos por um construtor de tabelas separado do lado do boss -- consulte Bosses e Entidades para esse conjunto.
context.event
context.event é nil quando o hook não tem um evento Bukkit. Quando presente, é a tabela de evento partilhada do MagmaCore:
| Campo / Método | Notas |
|---|---|
is_cancelled | Se o evento subjacente está cancelado (só faz sentido para eventos canceláveis). |
cancel() | Cancela o evento, quando este é cancelável. |
uncancel() | Anula o cancelamento do evento, quando este é cancelável. |
player | O ator do evento (por exemplo, o jogador que interage), como wrapper de jogador, quando presente. |
Para o jogador que interage ou aciona a proximidade, prefira context.player (está definido nesses hooks).
Estado, scheduler e tempos de recarga
context.state é uma tabela Lua simples que persiste para esta instância de script de NPC até o NPC ser removido.
context.scheduler é o scheduler partilhado do MagmaCore. Tanto os nomes do MagmaCore como os nomes run_after / run_every do EliteMobs funcionam — são aliases para o mesmo comportamento:
| Método | Argumentos | Notas |
|---|---|---|
run_later(ticks, callback) / run_after(ticks, callback) | number, function | Corre uma vez após um atraso. Devolve um ID de tarefa. |
run_repeating(delay, interval, callback) | number, number, function | Corre repetidamente após um atraso inicial. Devolve um ID de tarefa. |
run_every(interval, callback) | number, function | Corre a cada interval ticks (atraso inicial 0). Devolve um ID de tarefa. |
cancel(task_id) / cancel_task(task_id) | number | Cancela uma tarefa própria. |
Os callbacks do scheduler recebem um context novo. Não recebem o context.player nem o context.event originais. Todas as tarefas próprias são canceladas automaticamente quando o NPC é removido.
context.cooldowns é a tabela de tempos de recarga partilhada do MagmaCore:
| Método | Argumentos | Devolve | Notas |
|---|---|---|---|
local_ready(key?) | string | boolean | Verdadeiro quando o tempo de recarga local expirou. |
local_remaining(key?) | string | number | Ticks restantes, ou 0 quando está pronto. |
check_local(key?, duration) | string, number | boolean | Se estiver pronto, inicia o tempo de recarga e devolve verdadeiro. |
set_local(duration, key?) | number, string | nil | Define ou reinicia o tempo de recarga. |
global_ready() | - | boolean | Verdadeiro quando o tempo de recarga global partilhado está pronto. |
set_global(duration) | number | nil | Inicia o tempo de recarga global. |
Os scripts de NPC usam agora a ordem de tempos de recarga partilhada do MagmaCore (check_local(key?, duration)), a mesma dos poderes de boss e dos scripts do FreeMinecraftModels. Versões experimentais anteriores de NPC usavam check_local(duration, key?) — atualize quaisquer scripts antigos para a ordem partilhada.
Exemplo: acenar ao aproximar
Este script faz o NPC virar-se para o jogador que entra e reproduzir a animação de modelo personalizado wave. A entrada por proximidade já dispara uma vez por par NPC/jogador enquanto o jogador permanecer dentro do raio; o tempo de recarga evita que ciclos rápidos de saída/reentrada repitam a animação demasiadas vezes.
return {
api_version = 1,
priority = 0,
on_npc_proximity_enter = function(context)
if context.player == nil then return end
if context.cooldowns:check_local("wave:" .. context.player.uuid, 60) then
context.npc:face_direction_or_location(context.player.current_location)
context.npc:play_model_animation("wave")
end
end
}
play_model_animation(name) não faz nada, de forma segura, quando o NPC não tem um modelo personalizado ou quando o modelo não tem essa animação.
Orientações de desempenho
- Mantenha os hooks
on_game_tickpequenos. Correm 20 vezes por segundo para cada instância de script de NPC que os declare. (Scripts que não declaramon_game_ticknunca são executados por tick.) - Prefira
on_npc_proximity_entereon_npc_proximity_leavepara comportamento de proximidade, em vez de sondar os jogadores próximos a cada tick. - Use
context.cooldowns:check_local(...)para limitar animações, sons e rajadas de partículas. - Use
context.scheduler:run_repeating(...)com um intervalo razoável quando um comportamento não precisa de correr a cada tick. - Evite pesquisas grandes em Lua.
context.npc:get_nearby_players(radius)é adequado para verificações locais pequenas, mas varrimentos amplos devem permanecer no runtime do plugin.
Páginas relacionadas
- Criar NPCs -- campos de configuração de NPC, incluindo
activationRadius - Primeiros Passos com Lua -- poderes Lua de boss
- Motor de Scripting -- conceitos Lua partilhados e o runtime unificado
- Referência da API Lua -- lista completa de métodos para
context.world,context.player,context.zonese mais
