Scripting Lua : Scripts de PNJ
Les scripts Lua de PNJ d'EliteMobs sont des fichiers .lua autonomes qui s'attachent aux configurations de PNJ. Ils sont distincts des pouvoirs Lua de boss : les pouvoirs de boss se trouvent dans plugins/EliteMobs/powers/, tandis que les scripts de PNJ se trouvent dans plugins/EliteMobs/npc_scripts/.
Les scripts de PNJ s'exécutent désormais sur le même runtime de scripting unifié MagmaCore que les pouvoirs de boss, les props de FreeMinecraftModels et les items FMM. Cela signifie qu'un script de PNJ dispose de toute la surface de scripting partagée — context.world (y compris strike_lightning), context.zones, context.scheduler, context.cooldowns, context.log, context.event et context.player — plus une table context.npc spécifique aux PNJ. Tout ce que MagmaCore expose aux scripts est également disponible ici.
Les scripts Lua de PNJ sont encore expérimentaux. Les hooks spécifiques aux PNJ et les utilitaires context.npc peuvent changer. Les tables partagées (context.world, context.zones, context.scheduler, context.cooldowns, context.log, context.event, context.player) sont les mêmes que celles documentées dans le Moteur de Scripting et la Référence de l'API Lua.
Emplacement des fichiers
Créez les fichiers de scripts de PNJ dans :
plugins/
EliteMobs/
npc_scripts/
wave.lua
Les sous-dossiers sont analysés, de manière récursive. Cependant, les scripts sont enregistrés par nom de fichier seul, donc npc_scripts/wave.lua et npc_scripts/town/wave.lua entrent en collision -- gardez des noms de base uniques dans toute l'arborescence.
L'extension .lua est optionnelle dans une configuration de PNJ : - wave et - wave.lua se résolvent tous deux en wave.lua. Si une configuration de PNJ référence un script inexistant, EliteMobs enregistre un avertissement et le PNJ apparaît quand même.
Attacher des scripts aux PNJ
Ajoutez une liste scripts: à la configuration du PNJ :
scripts:
- wave.lua
Plusieurs scripts peuvent être attachés à un même PNJ :
scripts:
- wave.lua
- greeting_particles.lua
Les scripts s'exécutent dans l'ordre de priorité. Les valeurs de priority les plus basses s'exécutent en premier. Si la priorité est omise, elle vaut 0 par défaut.
Forme d'un script
Chaque script de PNJ doit retourner une seule table :
return {
api_version = 1,
priority = 0,
on_spawn = function(context)
context.state.spawned = true
context.npc:play_model_animation("idle")
end
}
Seuls ces champs de premier niveau sont acceptés :
| Champ | Type | Notes |
|---|---|---|
api_version | number | Requis. Doit valoir 1. |
priority | number | Optionnel. Les valeurs les plus basses s'exécutent en premier. |
on_spawn | function | S'exécute après l'apparition du PNJ. |
on_remove | function | S'exécute lorsque le PNJ est supprimé. |
on_game_tick | function | S'exécute à chaque tick serveur tant que le PNJ est valide. Gardez cela très léger. |
on_npc_interact | function | S'exécute lorsqu'un joueur interagit avec le PNJ. |
on_npc_proximity_enter | function | S'exécute une fois lorsqu'un joueur entre dans le rayon d'activation de ce PNJ. |
on_npc_proximity_leave | function | S'exécute une fois lorsqu'un joueur quitte le rayon d'activation de ce PNJ. |
on_zone_enter | function | S'exécute lorsqu'un joueur entre dans une zone surveillée par ce script (voir context.zones). |
on_zone_leave | function | S'exécute lorsqu'un joueur quitte une zone surveillée. |
La surveillance de zone ne suit que les joueurs -- les mobs et autres entités ne déclenchent jamais on_zone_enter / on_zone_leave.
Les clés de premier niveau inconnues sont rejetées lors du chargement du script. Les fonctions auxiliaires doivent être déclarées comme fonctions local au-dessus de la table retournée.
Hooks de proximité
Les hooks de proximité des PNJ utilisent la valeur de configuration activationRadius du PNJ.
| Hook | Se déclenche quand |
|---|---|
on_npc_proximity_enter | Un joueur passe de l'extérieur du rayon d'activation du PNJ à l'intérieur. |
on_npc_proximity_leave | Un joueur passe de l'intérieur du rayon d'activation du PNJ à l'extérieur. |
Ces hooks sont suivis par PNJ et par joueur par le scanner de proximité côté serveur. Se tenir près d'un PNJ n'empêche pas un autre PNJ de déclencher son propre événement d'entrée, et rester à l'intérieur du rayon ne provoque pas de répétition en boucle des événements d'entrée.
Le comportement normal de salutation, de dialogue et d'indicateur de quête continue de s'exécuter. Le hook Lua ajoute du comportement par-dessus.
Surface de scripting partagée
Comme les scripts de PNJ s'exécutent sur le runtime unifié, chaque hook de PNJ reçoit également les tables de context MagmaCore partagées utilisées par les scripts FreeMinecraftModels. Les pouvoirs de boss d'EliteMobs s'exécutent sur le même runtime mais utilisent des variantes spécifiques aux boss pour plusieurs tables. Les listes complètes de méthodes se trouvent dans la Référence de l'API Lua et le Moteur de Scripting :
| Table | Ce qu'elle fait |
|---|---|
context.world | Effets et requêtes sur le monde : strike_lightning, spawn_particle, play_sound, set_block_at, place_temporary_block, spawn_entity, spawn_firework, get_nearby_entities, get_nearby_players, raycast, et plus encore. Les deux formes sont acceptées : par coordonnées (strike_lightning(x, y, z)) et par table de localisation (strike_lightning_at_location(loc)). |
context.zones | Créer des zones spatiales (create_sphere(x, y, z, radius), create_cylinder(x, y, z, radius, height), create_cuboid(x, y, z, xSize, ySize, zSize)) -- chacune renvoie un identifiant numérique. watch(handle, on_enter, on_leave) démarre le suivi (les callbacks déclenchent vos hooks on_zone_enter / on_zone_leave, pas les fonctions que vous passez) ; unwatch(handle) l'arrête. |
context.scheduler | run_later(ticks, fn), run_repeating(delay, interval, fn), cancel(task_id). |
context.cooldowns | Temps de recharge partagés MagmaCore : local_ready, local_remaining, check_local, set_local, global_ready, set_global. |
context.log | info(msg), warn(msg), error(msg) — écrit dans la console du serveur. |
context.event | L'événement Bukkit courant, lorsqu'il y en a un. Voir ci-dessous. |
context.player | Le joueur qui interagit ou déclenche, lorsqu'il est présent. Voir ci-dessous. |
context.state | Une table Lua simple qui persiste pour cette instance de script de PNJ jusqu'à la suppression du PNJ. |
Exemple : foudroyer lors d'une interaction
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 dans chaque hook de PNJ.
Champs
| Champ | Type | Notes |
|---|---|---|
name | string | Nom d'affichage du PNJ défini dans la configuration. |
filename | string | Nom du fichier de configuration du PNJ. |
uuid | string | UUID du PNJ à l'exécution. |
activation_radius | number | Rayon d'activation configuré. |
current_location | table de localisation | Instantané de la localisation lorsque l'entité sous-jacente existe. |
entity_type | string | Type d'entité Bukkit lorsque l'entité sous-jacente existe. |
Méthodes
| Méthode | Arguments | Retourne | Notes |
|---|---|---|---|
is_valid() | - | boolean | Indique si le PNJ possède encore une entité sous-jacente valide. |
get_location() | - | table de localisation | Localisation actuelle du PNJ, ou localisation d'apparition si l'entité n'est pas disponible. |
get_eye_location() | - | table de localisation | Localisation actuelle des yeux, ou repli sur la localisation d'apparition. |
get_activation_radius() | - | number | Rayon d'activation actuellement configuré. |
get_nearby_players(radius) | number | table | Wrappers de joueurs dans le rayon autour du PNJ. |
face_direction_or_location(target) | vecteur ou localisation | nil | Oriente vers un vecteur de direction ou tourne vers une localisation / la localisation d'un joueur. |
say_greeting(player?) | joueur, UUID, nom ou nil | nil | Envoie une salutation configurée. Par défaut, le joueur déclencheur lorsqu'il est disponible. |
say_dialog(player?) | joueur, UUID, nom ou nil | nil | Envoie le dialogue configuré. Par défaut, le joueur déclencheur lorsqu'il est disponible. |
say_farewell(player?) | joueur, UUID, nom ou nil | nil | Envoie le texte d'adieu configuré. Par défaut, le joueur déclencheur lorsqu'il est disponible. |
play_model_animation(name) | string | nil | Joue une animation de modèle personnalisé si elle existe. Sans effet et sans erreur sinon. |
patrol_pause() | - | booléen | Met en pause la patrouille configurée. |
patrol_resume() | - | booléen | Annule une attente ou un déplacement temporaire et reprend la patrouille. |
walk_to(x, y, z) | trois nombres | booléen | Marche jusqu'à un décalage depuis l'origine puis reprend la patrouille. Les longs trajets sont résolus automatiquement. |
hold(x, y, z) | trois nombres | booléen | Marche jusqu'à un décalage et y reste. |
teleport(x, y, z) | trois nombres | booléen | Se téléporte si la destination traite les entités. |
Les méthodes de déplacement renvoient false si le PNJ n'a pas de patrouille configurée ou si la demande ne peut pas être acceptée. Consultez Patrouilles des PNJ et boss.
context.player
context.player est disponible dans on_npc_interact, on_npc_proximity_enter et on_npc_proximity_leave. Il vaut nil dans les hooks de cycle de vie qui n'impliquent pas de joueur.
C'est le wrapper de joueur partagé MagmaCore — la même table complète d'entité vivante / de joueur qu'utilisent les pouvoirs de boss et les scripts FMM, il expose donc bien plus que les bases (vie, effets de potion, send_message, show_title, show_action_bar, get_held_item, raycasting, et plus encore). Voir la Référence de l'API Lua pour la liste complète. Les éléments couramment utilisés ici :
| Champ / Méthode | Notes |
|---|---|
name | Nom du joueur. |
uuid | UUID du joueur. |
current_location | Table de position actuelle du joueur ; c'est un champ, pas une méthode. |
get_eye_location() | Localisation actuelle des yeux du joueur. |
send_message(text) | Envoie un message dans le chat. Prend en charge les codes couleur. |
Vérifiez toujours que context.player n'est pas nil avant de l'utiliser dans des fonctions auxiliaires partagées.
entity_type est en minusculesSur les tables d'entité partagées MagmaCore, entity_type est le nom Bukkit en minuscules ("player", "zombie"). Seuls context.npc.entity_type et les tables d'entité des pouvoirs de boss EliteMobs utilisent la forme en majuscules. Comparez sans tenir compte de la casse si un script doit gérer les deux.
Champs EliteMobs ajoutés à chaque table d'entité partagée
Tant qu'EliteMobs tourne, il ajoute des champs supplémentaires à chaque table d'entité MagmaCore — context.player, les wrappers renvoyés par context.npc:get_nearby_players(...), et ceux que voient les scripts de props et d'objets FreeMinecraftModels :
| Champ | Type | Notes |
|---|---|---|
is_elite | booléen | true si EliteMobs suit l'entité comme un élite |
is_custom_boss | booléen | true s'il s'agit d'un Custom Boss (toujours false lorsque is_elite vaut false) |
is_significant_boss | booléen | true pour un Custom Boss dont le multiplicateur de santé est supérieur à 1 — la vérification pratique « c'est un vrai boss, pas un renfort » |
elite | table ou nil | Présent uniquement sur les élites. Voir ci-dessous |
La sous-table elite :
| Champ / Méthode | Type | Notes |
|---|---|---|
elite.level | nombre | Niveau de l'élite |
elite.name | chaîne ou nil | Nom d'affichage de l'élite |
elite.health | nombre | Santé actuelle de l'élite (lue en direct) |
elite.max_health | nombre | Santé maximale de l'élite (lue en direct) |
elite.is_custom_boss | booléen | Même valeur que le champ de premier niveau |
elite.health_multiplier | nombre | Multiplicateur de santé configuré |
elite.damage_multiplier | nombre | Multiplicateur de dégâts configuré |
elite:remove() | — | Fait disparaître l'élite |
-- 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
Ces champs n'apparaissent pas sur les wrappers d'entité des pouvoirs de boss EliteMobs, qui sont construits par un constructeur de table distinct côté boss — voir Boss et entités pour cet ensemble.
context.event
context.event vaut nil lorsque le hook n'a pas d'événement Bukkit. Lorsqu'il est présent, c'est la table d'événement partagée MagmaCore :
| Champ / Méthode | Notes |
|---|---|
is_cancelled | Indique si l'événement sous-jacent est annulé (n'a de sens que pour les événements annulables). |
cancel() | Annule l'événement, lorsqu'il est annulable. |
uncancel() | Rétablit l'événement annulé, lorsqu'il est annulable. |
player | L'acteur de l'événement (par exemple le joueur qui interagit), sous forme de wrapper de joueur, lorsqu'il est présent. |
Pour le joueur qui interagit ou déclenche la proximité, préférez context.player (il est défini pour ces hooks).
État, planificateur et temps de recharge
context.state est une table Lua simple qui persiste pour cette instance de script de PNJ jusqu'à la suppression du PNJ.
context.scheduler est le planificateur partagé MagmaCore. Les noms MagmaCore et les noms EliteMobs run_after / run_every fonctionnent tous les deux — ce sont des alias du même comportement :
| Méthode | Arguments | Notes |
|---|---|---|
run_later(ticks, callback) / run_after(ticks, callback) | number, function | S'exécute une fois après un délai. Retourne un ID de tâche. |
run_repeating(delay, interval, callback) | number, number, function | S'exécute de façon répétée après un délai initial. Retourne un ID de tâche. |
run_every(interval, callback) | number, function | S'exécute toutes les interval ticks (délai initial de 0). Retourne un ID de tâche. |
cancel(task_id) / cancel_task(task_id) | number | Annule une tâche dont le script est propriétaire. |
Les callbacks du planificateur reçoivent un context neuf. Ils ne reçoivent pas le context.player ni le context.event d'origine. Toutes les tâches détenues sont annulées automatiquement lorsque le PNJ est supprimé.
context.cooldowns est la table de temps de recharge partagée MagmaCore :
| Méthode | Arguments | Retourne | Notes |
|---|---|---|---|
local_ready(key?) | string | boolean | Vrai lorsque le temps de recharge local a expiré. |
local_remaining(key?) | string | number | Ticks restants, ou 0 lorsque prêt. |
check_local(key?, duration) | string, number | boolean | Si prêt, démarre le temps de recharge et retourne vrai. |
set_local(duration, key?) | number, string | nil | Définit ou réinitialise le temps de recharge. |
global_ready() | - | boolean | Vrai lorsque le temps de recharge global partagé est prêt. |
set_global(duration) | number | nil | Démarre le temps de recharge global. |
Les scripts de PNJ utilisent désormais l'ordre d'arguments partagé de MagmaCore (check_local(key?, duration)), le même que les pouvoirs de boss et les scripts FreeMinecraftModels. Les anciens builds expérimentaux de PNJ utilisaient check_local(duration, key?) — mettez à jour vos anciens scripts vers l'ordre partagé.
Exemple : saluer à l'approche
Ce script fait en sorte que le PNJ se tourne vers le joueur qui entre et joue l'animation de modèle personnalisé wave. L'entrée en proximité se déclenche déjà une seule fois par paire PNJ/joueur tant que le joueur reste dans le rayon ; le temps de recharge évite que des cycles rapides de sortie/entrée ne rejouent l'animation trop souvent.
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) ne fait rien, sans erreur, lorsque le PNJ n'a pas de modèle personnalisé ou que le modèle ne possède pas cette animation.
Recommandations de performance
- Gardez les hooks
on_game_tickréduits. Ils s'exécutent 20 fois par seconde pour chaque instance de script de PNJ qui les définit. (Les scripts qui ne déclarent pason_game_tickne sont jamais tickés.) - Préférez
on_npc_proximity_entereton_npc_proximity_leavepour les comportements de proximité plutôt que d'interroger les joueurs proches à chaque tick. - Utilisez
context.cooldowns:check_local(...)pour limiter les animations, les sons et les rafales de particules. - Utilisez
context.scheduler:run_repeating(...)avec un intervalle raisonnable lorsqu'un comportement n'a pas besoin de s'exécuter à chaque tick. - Évitez les recherches volumineuses en Lua.
context.npc:get_nearby_players(radius)convient pour de petites vérifications locales, mais les balayages larges doivent rester dans le runtime du plugin.
Pages liées
- Créer des PNJ -- champs de configuration des PNJ, dont
activationRadius - Lua : Pour Commencer -- pouvoirs Lua de boss
- Moteur de Scripting -- concepts Lua partagés et runtime unifié
- Référence de l'API Lua -- liste complète des méthodes pour
context.world,context.player,context.zones, et plus
