Scripting Lua: Scripts de NPC
Los scripts Lua de NPC de EliteMobs son archivos .lua independientes que se adjuntan a las configuraciones de NPC. Son distintos de los poderes Lua de jefe: los poderes de jefe viven en plugins/EliteMobs/powers/, mientras que los scripts de NPC viven en plugins/EliteMobs/npc_scripts/.
Los scripts de NPC ahora se ejecutan sobre el mismo runtime de scripting unificado de MagmaCore que los poderes de jefe, los props de FreeMinecraftModels y los ítems de FMM. Eso significa que un script de NPC obtiene toda la superficie de scripting compartida — context.world (incluido strike_lightning), context.zones, context.scheduler, context.cooldowns, context.log, context.event y context.player — más una tabla context.npc específica de NPC. Todo lo que MagmaCore expone a los scripts también está disponible aquí.
Los scripts Lua de NPC siguen siendo experimentales. Los hooks específicos de NPC y los helpers de context.npc pueden cambiar. Las tablas compartidas (context.world, context.zones, context.scheduler, context.cooldowns, context.log, context.event, context.player) son las mismas que se documentan en el Motor de Scripting y en la Referencia de la API de Lua.
Ubicación de los archivos
Crea los archivos de script de NPC en:
plugins/
EliteMobs/
npc_scripts/
wave.lua
Las subcarpetas sí se escanean, de forma recursiva. Sin embargo, los scripts se registran solo por nombre de archivo, así que npc_scripts/wave.lua y npc_scripts/town/wave.lua colisionan -- mantén los nombres base únicos en todo el árbol.
La extensión .lua es opcional en una configuración de NPC: tanto - wave como - wave.lua resuelven a wave.lua. Si una configuración de NPC referencia un script que no existe, EliteMobs registra una advertencia y el NPC aparece igualmente.
Adjuntar scripts a los NPC
Añade una lista scripts: a la configuración del NPC:
scripts:
- wave.lua
Se pueden adjuntar varios scripts a un mismo NPC:
scripts:
- wave.lua
- greeting_particles.lua
Los scripts se ejecutan en orden de prioridad. Los valores de priority más bajos se ejecutan primero. Si se omite la prioridad, su valor por defecto es 0.
Forma del script
Todo script de NPC debe devolver una única tabla:
return {
api_version = 1,
priority = 0,
on_spawn = function(context)
context.state.spawned = true
context.npc:play_model_animation("idle")
end
}
Solo se aceptan estos campos de nivel superior:
| Campo | Tipo | Notas |
|---|---|---|
api_version | número | Obligatorio. Debe ser 1. |
priority | número | Opcional. Los valores más bajos se ejecutan primero. |
on_spawn | función | Se ejecuta después de que el NPC aparece. |
on_remove | función | Se ejecuta cuando el NPC es eliminado. |
on_game_tick | función | Se ejecuta en cada tick del servidor mientras el NPC sea válido. Manténlo muy ligero. |
on_npc_interact | función | Se ejecuta cuando un jugador interactúa con el NPC. |
on_npc_proximity_enter | función | Se ejecuta una vez cuando un jugador entra en el radio de activación de este NPC. |
on_npc_proximity_leave | función | Se ejecuta una vez cuando un jugador sale del radio de activación de este NPC. |
on_zone_enter | función | Se ejecuta cuando un jugador entra en una zona que este script está vigilando (consulta context.zones). |
on_zone_leave | función | Se ejecuta cuando un jugador sale de una zona vigilada. |
La vigilancia de zonas solo rastrea jugadores -- los mobs y otras entidades nunca disparan on_zone_enter / on_zone_leave.
Las claves de nivel superior desconocidas se rechazan durante la carga del script. Las funciones auxiliares deben declararse como funciones local por encima de la tabla devuelta.
Hooks de proximidad
Los hooks de proximidad de NPC usan el valor de configuración activationRadius del NPC.
| Hook | Se dispara cuando |
|---|---|
on_npc_proximity_enter | Un jugador pasa de estar fuera del radio de activación del NPC a estar dentro. |
on_npc_proximity_leave | Un jugador pasa de estar dentro del radio de activación del NPC a estar fuera. |
Estos hooks se rastrean por NPC y por jugador mediante el escáner de proximidad del lado del servidor. Estar cerca de un NPC no impide que otro NPC dispare su propio evento de entrada, y permanecer dentro del radio no genera eventos de entrada repetidos.
El comportamiento normal de saludo, diálogo e indicador de misiones sigue ejecutándose. El hook de Lua añade comportamiento por encima de eso.
Superficie de scripting compartida
Como los scripts de NPC se ejecutan sobre el runtime unificado, cada hook de NPC también recibe las tablas de context compartidas de MagmaCore que usan los scripts de FreeMinecraftModels. Los poderes de jefe de EliteMobs se ejecutan sobre el mismo runtime pero usan variantes específicas de jefe para varias tablas. Las listas completas de métodos están en la Referencia de la API de Lua y en el Motor de Scripting:
| Tabla | Qué hace |
|---|---|
context.world | Efectos y consultas del mundo: strike_lightning, spawn_particle, play_sound, set_block_at, place_temporary_block, spawn_entity, spawn_firework, get_nearby_entities, get_nearby_players, raycast y más. Se aceptan tanto la forma con coordenadas (strike_lightning(x, y, z)) como la de tabla de ubicación (strike_lightning_at_location(loc)). |
context.zones | Crea zonas espaciales (create_sphere(x, y, z, radius), create_cylinder(x, y, z, radius, height), create_cuboid(x, y, z, xSize, ySize, zSize)) -- cada una devuelve un identificador numérico. watch(handle, on_enter, on_leave) inicia el rastreo (los callbacks disparan tus hooks on_zone_enter / on_zone_leave, no las funciones que pasas); unwatch(handle) lo detiene. |
context.scheduler | run_later(ticks, fn), run_repeating(delay, interval, fn), cancel(task_id). |
context.cooldowns | Tiempos de reutilización compartidos de MagmaCore: local_ready, local_remaining, check_local, set_local, global_ready, set_global. |
context.log | info(msg), warn(msg), error(msg) — escribe en la consola del servidor. |
context.event | El evento de Bukkit actual, cuando hay uno presente. Consulta más abajo. |
context.player | El jugador que interactúa o dispara el hook, cuando está presente. Consulta más abajo. |
context.state | Una tabla Lua simple que persiste para esta instancia de script del NPC hasta que el NPC es eliminado. |
Ejemplo: fulminar al interactuar
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á disponible en todos los hooks de NPC.
Campos
| Campo | Tipo | Notas |
|---|---|---|
name | cadena | Nombre visible del NPC según la configuración. |
filename | cadena | Nombre del archivo de configuración del NPC. |
uuid | cadena | UUID del NPC en tiempo de ejecución. |
activation_radius | número | Radio de activación configurado. |
current_location | tabla de ubicación | Instantánea de la ubicación cuando la entidad de respaldo existe. |
entity_type | cadena | Tipo de entidad de Bukkit cuando la entidad de respaldo existe. |
Métodos
| Método | Argumentos | Devuelve | Notas |
|---|---|---|---|
is_valid() | - | booleano | Si el NPC todavía tiene una entidad de respaldo válida. |
get_location() | - | tabla de ubicación | Ubicación actual del NPC, o la ubicación de aparición si la entidad no está disponible. |
get_eye_location() | - | tabla de ubicación | Ubicación actual de los ojos, o respaldo a la ubicación de aparición. |
get_activation_radius() | - | número | Radio de activación configurado actualmente. |
get_nearby_players(radius) | número | tabla | Envoltorios de jugador dentro del radio del NPC. |
face_direction_or_location(target) | vector o ubicación | nil | Orienta hacia un vector de dirección o gira hacia una ubicación / ubicación de jugador. |
say_greeting(player?) | jugador, UUID, nombre o nil | nil | Envía un saludo configurado. Por defecto usa el jugador que dispara el hook cuando está disponible. |
say_dialog(player?) | jugador, UUID, nombre o nil | nil | Envía el diálogo configurado. Por defecto usa el jugador que dispara el hook cuando está disponible. |
say_farewell(player?) | jugador, UUID, nombre o nil | nil | Envía el texto de despedida configurado. Por defecto usa el jugador que dispara el hook cuando está disponible. |
play_model_animation(name) | cadena | nil | Reproduce una animación de modelo personalizado si existe. En caso contrario no hace nada de forma segura. |
patrol_pause() | - | booleano | Pausa la patrulla configurada. |
patrol_resume() | - | booleano | Cancela una espera o marcha temporal y reanuda la patrulla. |
walk_to(x, y, z) | tres números | booleano | Camina hasta un desplazamiento desde el origen y reanuda la patrulla. Las rutas largas se resuelven automáticamente. |
hold(x, y, z) | tres números | booleano | Camina hasta un desplazamiento y espera allí. |
teleport(x, y, z) | tres números | booleano | Se teletransporta si el destino procesa entidades. |
Los métodos de movimiento devuelven false si el NPC no tiene una patrulla configurada o no se puede aceptar la solicitud. Consulta Patrullas de NPCs y jefes.
context.player
context.player está disponible en on_npc_interact, on_npc_proximity_enter y on_npc_proximity_leave. Es nil en los hooks de ciclo de vida que no involucran a un jugador.
Es el envoltorio de jugador compartido de MagmaCore — la misma tabla completa de entidad viva / jugador que usan los poderes de jefe y los scripts de FMM, así que expone mucho más que lo básico (salud, efectos de poción, send_message, show_title, show_action_bar, get_held_item, raycasting y más). Consulta la Referencia de la API de Lua para la lista completa. Lo que se usa habitualmente aquí:
| Campo / Método | Notas |
|---|---|
name | Nombre del jugador. |
uuid | UUID del jugador. |
current_location | Tabla de ubicación actual del jugador; es un campo, no un método. |
get_eye_location() | Ubicación actual de los ojos del jugador. |
send_message(text) | Envía un mensaje de chat. Admite códigos de color. |
Comprueba siempre si context.player es nil antes de usarlo en funciones auxiliares compartidas.
entity_type va en minúsculasEn las tablas de entidad compartidas de MagmaCore, entity_type es el nombre de Bukkit en minúsculas ("player", "zombie"). Solo context.npc.entity_type y las tablas de entidad de los poderes de jefe de EliteMobs usan la forma en mayúsculas. Compara sin distinguir mayúsculas si un script tiene que funcionar con ambas.
Campos que EliteMobs añade a todas las tablas de entidad compartidas
Mientras EliteMobs está en ejecución, aporta campos adicionales a todas las tablas de entidad de MagmaCore: context.player, los envoltorios que devuelve context.npc:get_nearby_players(...) y los que ven los scripts de props y objetos de FreeMinecraftModels:
| Campo | Tipo | Notas |
|---|---|---|
is_elite | booleano | true si EliteMobs rastrea la entidad como un elite |
is_custom_boss | booleano | true si es un jefe personalizado (siempre false cuando is_elite es false) |
is_significant_boss | booleano | true para un jefe personalizado cuyo multiplicador de salud es superior a 1: la comprobación práctica de «esto es un jefe de verdad, no un refuerzo» |
elite | tabla o nil | Presente solo en los elites. Ver más abajo |
La subtabla elite:
| Campo / Método | Tipo | Notas |
|---|---|---|
elite.level | número | Nivel del elite |
elite.name | cadena o nil | Nombre visible del elite |
elite.health | número | Salud actual del elite (se lee en vivo) |
elite.max_health | número | Salud máxima del elite (se lee en vivo) |
elite.is_custom_boss | booleano | El mismo valor que el campo de nivel superior |
elite.health_multiplier | número | Multiplicador de salud configurado |
elite.damage_multiplier | número | Multiplicador de daño configurado |
elite:remove() | — | Elimina al 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
Estos campos no aparecen en los envoltorios de entidad de los poderes de jefe de EliteMobs, que los construye un generador de tablas independiente del lado del jefe: consulta Jefes y entidades para ese conjunto.
context.event
context.event es nil cuando el hook no tiene ningún evento de Bukkit. Cuando está presente es la tabla de evento compartida de MagmaCore:
| Campo / Método | Notas |
|---|---|
is_cancelled | Si el evento subyacente está cancelado (solo tiene sentido para eventos cancelables). |
cancel() | Cancela el evento, cuando es cancelable. |
uncancel() | Deshace la cancelación del evento, cuando es cancelable. |
player | El actor del evento (p. ej. el jugador que interactúa), como envoltorio de jugador, cuando está presente. |
Para el jugador que interactúa o entra en proximidad, prefiere context.player (está definido para esos hooks).
Estado, planificador y tiempos de reutilización
context.state es una tabla Lua simple que persiste para esta instancia de script del NPC hasta que el NPC es eliminado.
context.scheduler es el planificador compartido de MagmaCore. Funcionan tanto los nombres de MagmaCore como los nombres run_after / run_every de EliteMobs — son alias del mismo comportamiento:
| Método | Argumentos | Notas |
|---|---|---|
run_later(ticks, callback) / run_after(ticks, callback) | número, función | Se ejecuta una vez tras un retraso. Devuelve un ID de tarea. |
run_repeating(delay, interval, callback) | número, número, función | Se ejecuta repetidamente tras un retraso inicial. Devuelve un ID de tarea. |
run_every(interval, callback) | número, función | Se ejecuta cada interval ticks (retraso inicial 0). Devuelve un ID de tarea. |
cancel(task_id) / cancel_task(task_id) | número | Cancela una tarea propia. |
Los callbacks del planificador reciben un context nuevo. No reciben el context.player ni el context.event originales. Todas las tareas propias se cancelan automáticamente cuando el NPC es eliminado.
context.cooldowns es la tabla de tiempos de reutilización compartida de MagmaCore:
| Método | Argumentos | Devuelve | Notas |
|---|---|---|---|
local_ready(key?) | cadena | booleano | Verdadero cuando el tiempo de reutilización local ha expirado. |
local_remaining(key?) | cadena | número | Ticks restantes, o 0 cuando está listo. |
check_local(key?, duration) | cadena, número | booleano | Si está listo, inicia el tiempo de reutilización y devuelve verdadero. |
set_local(duration, key?) | número, cadena | nil | Establece o reinicia el tiempo de reutilización. |
global_ready() | - | booleano | Verdadero cuando el tiempo de reutilización global compartido está listo. |
set_global(duration) | número | nil | Inicia el tiempo de reutilización global. |
Los scripts de NPC ahora usan el orden de tiempos de reutilización compartido de MagmaCore (check_local(key?, duration)), el mismo que los poderes de jefe y los scripts de FreeMinecraftModels. Las compilaciones experimentales anteriores de NPC usaban check_local(duration, key?) — actualiza cualquier script antiguo al orden compartido.
Ejemplo: saludar al acercarse
Este script hace que el NPC mire al jugador que entra y reproduzca la animación de modelo personalizado wave. La entrada por proximidad ya se dispara una vez por par NPC/jugador mientras el jugador permanezca dentro del radio; el tiempo de reutilización evita que los ciclos rápidos de salir y volver a entrar repitan la animación con demasiada frecuencia.
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) no hace nada de forma segura cuando el NPC no tiene modelo personalizado o el modelo no tiene esa animación.
Pautas de rendimiento
- Mantén pequeños los hooks
on_game_tick. Se ejecutan 20 veces por segundo por cada instancia de script de NPC que los defina. (Los scripts que no declaranon_game_ticknunca se procesan por tick.) - Prefiere
on_npc_proximity_enteryon_npc_proximity_leavepara el comportamiento de proximidad en lugar de consultar los jugadores cercanos en cada tick. - Usa
context.cooldowns:check_local(...)para limitar animaciones, sonidos y ráfagas de partículas. - Usa
context.scheduler:run_repeating(...)con un intervalo razonable cuando un comportamiento no necesite ejecutarse en cada tick. - Evita las búsquedas grandes en Lua.
context.npc:get_nearby_players(radius)está bien para comprobaciones locales pequeñas, pero los escaneos amplios deben quedarse en el runtime del plugin.
Páginas relacionadas
- Creando NPCs -- campos de configuración de NPC, incluido
activationRadius - Primeros Pasos con Lua -- poderes Lua de jefe
- Motor de Scripting -- conceptos compartidos de Lua y el runtime unificado
- Referencia de la API de Lua -- lista completa de métodos para
context.world,context.player,context.zonesy más
