Pular para o conteúdo principal

Scripting Lua: Scripts de NPC

webapp_banner.jpg

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.

Funcionalidade Experimental

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:

CampoTipoNotas
api_versionnumberObrigatório. Tem de ser 1.
prioritynumberOpcional. Valores mais baixos correm primeiro.
on_spawnfunctionCorre depois de o NPC aparecer.
on_removefunctionCorre quando o NPC é removido.
on_game_tickfunctionCorre a cada tick do servidor enquanto o NPC for válido. Mantenha isto muito leve.
on_npc_interactfunctionCorre quando um jogador interage com o NPC.
on_npc_proximity_enterfunctionCorre uma vez quando um jogador entra no raio de ativação deste NPC.
on_npc_proximity_leavefunctionCorre uma vez quando um jogador sai do raio de ativação deste NPC.
on_zone_enterfunctionCorre quando um jogador entra numa zona que este script está a vigiar (ver context.zones).
on_zone_leavefunctionCorre 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.

HookDispara quando
on_npc_proximity_enterUm jogador passa de fora do raio de ativação do NPC para dentro dele.
on_npc_proximity_leaveUm 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:

TabelaO que faz
context.worldEfeitos 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.zonesCria 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.schedulerrun_later(ticks, fn), run_repeating(delay, interval, fn), cancel(task_id).
context.cooldownsTempos de recarga partilhados do MagmaCore: local_ready, local_remaining, check_local, set_local, global_ready, set_global.
context.loginfo(msg), warn(msg), error(msg) — escreve na consola do servidor.
context.eventO evento Bukkit atual, quando existe algum. Ver abaixo.
context.playerO jogador que interage/aciona, quando presente. Ver abaixo.
context.stateUma 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

CampoTipoNotas
namestringNome de exibição do NPC, vindo da configuração.
filenamestringNome do ficheiro de configuração do NPC.
uuidstringUUID do NPC em tempo de execução.
activation_radiusnumberRaio de ativação configurado.
current_locationtabela de localizaçãoLocalização instantânea quando a entidade de suporte existe.
entity_typestringTipo de entidade do Bukkit quando a entidade de suporte existe.

Métodos

MétodoArgumentosDevolveNotas
is_valid()-booleanSe o NPC ainda tem uma entidade de suporte válida.
get_location()-tabela de localizaçãoLocalização atual do NPC, ou a localização de spawn se a entidade não estiver disponível.
get_eye_location()-tabela de localizaçãoLocalização atual dos olhos, ou a localização de spawn como alternativa.
get_activation_radius()-numberRaio de ativação atualmente configurado.
get_nearby_players(radius)numbertableWrappers de jogador dentro do raio a partir do NPC.
face_direction_or_location(target)vetor ou localizaçãonilVira-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 nilnilEnvia uma saudação configurada. Por omissão, usa o jogador que acionou, quando disponível.
say_dialog(player?)jogador, UUID, nome ou nilnilEnvia o diálogo configurado. Por omissão, usa o jogador que acionou, quando disponível.
say_farewell(player?)jogador, UUID, nome ou nilnilEnvia o texto de despedida configurado. Por omissão, usa o jogador que acionou, quando disponível.
play_model_animation(name)stringnilReproduz uma animação de modelo personalizado, se existir. Caso contrário, não faz nada de forma segura.
patrol_pause()-booleanoPausa a patrulha configurada.
patrol_resume()-booleanoTermina uma espera ou caminhada temporária e retoma a patrulha.
walk_to(x, y, z)três númerosbooleanoCaminha até um deslocamento desde a origem e retoma a patrulha. Rotas longas são resolvidas automaticamente.
hold(x, y, z)três númerosbooleanoCaminha até um deslocamento e permanece ali.
teleport(x, y, z)três númerosbooleanoTeleporta 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étodoNotas
nameNome do jogador.
uuidUUID do jogador.
current_locationTabela 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.

Aqui o entity_type está em minúsculas

Nas 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:

CampoTipoNotas
is_elitebooleantrue se o EliteMobs regista a entidade como um elite
is_custom_bossbooleantrue se for um Custom Boss (sempre false quando is_elite é false)
is_significant_bossbooleantrue 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"
elitetable ou nilPresente apenas em elites. Ver abaixo

A subtabela elite:

Campo / MétodoTipoNotas
elite.levelnumberNível do elite
elite.namestring ou nilNome de exibição do elite
elite.healthnumberVida atual do elite (leitura em tempo real)
elite.max_healthnumberVida máxima do elite (leitura em tempo real)
elite.is_custom_bossbooleanO mesmo valor do campo de nível superior
elite.health_multipliernumberMultiplicador de vida configurado
elite.damage_multipliernumberMultiplicador 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étodoNotas
is_cancelledSe 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.
playerO 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étodoArgumentosNotas
run_later(ticks, callback) / run_after(ticks, callback)number, functionCorre uma vez após um atraso. Devolve um ID de tarefa.
run_repeating(delay, interval, callback)number, number, functionCorre repetidamente após um atraso inicial. Devolve um ID de tarefa.
run_every(interval, callback)number, functionCorre a cada interval ticks (atraso inicial 0). Devolve um ID de tarefa.
cancel(task_id) / cancel_task(task_id)numberCancela 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étodoArgumentosDevolveNotas
local_ready(key?)stringbooleanVerdadeiro quando o tempo de recarga local expirou.
local_remaining(key?)stringnumberTicks restantes, ou 0 quando está pronto.
check_local(key?, duration)string, numberbooleanSe estiver pronto, inicia o tempo de recarga e devolve verdadeiro.
set_local(duration, key?)number, stringnilDefine ou reinicia o tempo de recarga.
global_ready()-booleanVerdadeiro quando o tempo de recarga global partilhado está pronto.
set_global(duration)numbernilInicia o tempo de recarga global.
API de tempos de recarga unificada

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_tick pequenos. Correm 20 vezes por segundo para cada instância de script de NPC que os declare. (Scripts que não declaram on_game_tick nunca são executados por tick.)
  • Prefira on_npc_proximity_enter e on_npc_proximity_leave para 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