Aller au contenu principal

Scripting Lua : Pour Commencer

webapp_banner.jpg

Ce que vous apprendrez​

Cette page vous apprend à écrire votre premier pouvoir Lua pour EliteMobs, d'un fichier vide jusqu'à une capacité de boss fonctionnelle. À la fin, vous comprendrez les hooks, le context, les temps de recharge et la structure générale de chaque fichier de pouvoir Lua.

Une fois à l'aise avec les bases, continuez avec les pages complémentaires :

  • Hooks et Cycle de Vie -- noms des hooks, ordre d'exécution et disponibilité du context
  • Scripts de PNJ -- cycle de vie, interactions et hooks de proximité des PNJ
  • Boss et Entités -- les APIs context.boss, context.player, context.players et context.entities
  • Monde et Environnement -- particules, sons, éclairs, apparition et l'API context.world
  • Zones et Ciblage -- zones Lua natives, utilitaires de script pour le ciblage et les APIs context.zones / context.script
  • Exemples et Modèles -- pouvoirs complets et fonctionnels à étudier et adapter
  • Enums et Valeurs -- liens vers les Javadocs Spigot pour Particle, Material, PotionEffectType et autres constantes
  • Dépannage -- erreurs courantes, conseils de débogage et la checklist QC
  • Programmes de comportement -- IA de mob écrite en Lua, choisie avec le paramètre behavior d'un boss
Toujours en évolution

La plupart des pouvoirs livrés avec EliteMobs utilisent Lua. Les noms des méthodes et leur comportement peuvent changer entre les versions. Vérifiez vos pouvoirs personnalisés sur un serveur de préproduction et relisez la référence après une mise à jour.

Relation avec EliteScript

Lua ne remplace pas le système YAML eliteScript: existant, et EliteScript n'est pas déprécié.

  • Utilisez EliteScript quand vous souhaitez un scripting déclaratif basé sur YAML avec les pages existantes d'Actions, Cibles, Zones, Conditions, Temps de Recharge et Vecteurs Relatifs.
  • Utilisez les fichiers de pouvoir Lua quand vous avez besoin de variables, boucles, sélection aléatoire, fonctions auxiliaires réutilisables, état persistant par boss et un flux de programmation plus traditionnel.

L'API Script Utilities (context.script) transmet vos tables aux classes de cibles, zones, vecteurs relatifs et particules d'EliteScript. Leurs parseurs et limitations s'appliquent aussi, mais les objets Lua autonomes ne disposent pas de tous les contextes des actions YAML. Les cibles héritées et les positions d'atterrissage exigent un contexte absent de ces objets. Les aides Lua n'appliquent pas automatiquement les attentes, conditions ou caches de cibles des actions YAML. La page Zones et Ciblage explique ces différences.


Ce que sont les pouvoirs Lua​

Les pouvoirs Lua sont des fichiers .lua autonomes situés dans l'arborescence normale powers d'EliteMobs et référencés exactement comme les fichiers de pouvoir normaux.

Pouvoirs de boss et scripts de PNJ

Cette page traite des pouvoirs Lua des boss. Les scripts Lua des PNJ constituent une fonction distincte : ils se trouvent dans plugins/EliteMobs/npc_scripts/, s'attachent avec la liste scripts: de la configuration du PNJ et utilisent des hooks spécifiques tels que on_npc_proximity_enter. Consultez Scripts de PNJ.

Ce à quoi les pouvoirs Lua excellent​

Les pouvoirs Lua brillent quand vous avez besoin de :

  • Rotations d'attaque et sélecteurs de mouvements aléatoires
  • État persistant entre les hooks avec context.state
  • Actions retardées et répétées sans tout construire avec des attentes YAML
  • Fonctions auxiliaires personnalisées partagées dans un fichier
  • Branchements complexes qui seraient maladroits en EliteScript pur
  • Logique de boss souhaitant réutiliser les définitions de ciblage et de zones de style EliteScript

Si votre pouvoir est principalement "déclencher un événement, exécuter quelques actions scriptées", les docs EliteScript existants restent le moyen le plus rapide et clair de le construire. Si votre pouvoir nécessite un véritable flux de programme, Lua est l'outil pour ce travail.


À qui s'adresse cette page​

Cette page est écrite pour trois types de lecteurs :

  • Quelqu'un qui connaît déjà EliteScript et veut apprendre Lua sans apprendre la "vraie programmation" d'un seul coup
  • Quelqu'un de nouveau dans le scripting EliteMobs qui a besoin d'une référence complète avec des noms exacts
  • Quelqu'un qui utilise l'IA pour rédiger des pouvoirs et a besoin de suffisamment de détails pour détecter quand l'IA a inventé quelque chose de faux

Vous n'avez pas besoin de devenir un développeur Lua complet avant d'écrire des pouvoirs utiles. Pour la plupart des pouvoirs pratiques EliteMobs, vous avez seulement besoin de :

  • Comment placer un hook valide dans la table retournée
  • Comment lire les valeurs du context
  • Comment s'arrêter tôt avec if ... then return end
  • Comment appeler quelques méthodes auxiliaires correctement
  • Comment copier les bonnes spécifications de cibles et de zones depuis les docs EliteScript existants

Modèle Mental : EliteScript vs. Lua​

Si vous connaissez EliteScript, cette comparaison est le moyen le plus rapide de comprendre les pouvoirs Lua :

Si vous pensez en termes EliteScriptEn Lua cela signifie généralement
EventsNoms de hooks comme on_spawn ou on_boss_damaged_by_player
Cooldownscontext.cooldowns (voir context.cooldowns ci-dessous)
ActionsAppels de méthodes directs comme context.world:spawn_particle_at_location(...) ou context.script:damage(...)
Ciblescontext.script:target({...}) -- utilise les noms de champs EliteScript Target
Zonescontext.script:zone({...}) ou les aides natives context.zones -- voir Zones et Ciblage
Vecteurs relatifscontext.script:relative_vector({...}) ou context.vectors
Flux du scriptVos propres instructions if Lua, fonctions auxiliaires, minuteries et état

La plus grande différence est celle-ci :

  • EliteScript dit ce qui devrait se passer en YAML.
  • Lua vous permet de décider quand, pourquoi et quelle branche devrait s'exécuter en utilisant du code.

Si EliteScript ressemble à de la "configuration", Lua ressemble à "configuration plus prise de décision".


Petit guide Lua​

La plupart des pouvoirs utilisent quelques concepts Lua : les variables (local x = 5), les fonctions (function foo() end), les conditions if (if x then ... end), les tables ({key = value}) et nil (absence de valeur). Les points-virgules sont facultatifs. Les tables utilisent des accolades ; les fonctions et blocs de contrôle se terminent par end.

Pour une explication complète avec des exemples, consultez le Moteur de Scripting Lua MagmaCore -- Petit guide Lua. Ce guide est partagé par tous les plugins Nightbreak : l'apprendre une fois vaut partout.


Votre premier pouvoir fonctionnel, construit progressivement​

Si vous êtes complètement nouveau, c'est la meilleure progression pour la "première victoire".

Avant l'Étape 1 : Enregistrez-le et attachez-le à un boss​

Enregistrez le fichier Lua dans le dossier normal des pouvoirs EliteMobs, par exemple :

plugins/
EliteMobs/
powers/
first_test.lua

Puis ajoutez ce nom de fichier à la configuration du boss en utilisant la liste normale powers: :

powers:
- first_test.lua

La boucle complète pour débutant est donc :

  1. Enregistrer le fichier dans plugins/EliteMobs/powers/
  2. Ajouter le nom de fichier .lua à la liste powers: du boss
  3. Exécuter /em reload pour charger les fichiers modifiés, puis faire apparaître un nouveau boss de test. Le rechargement affecte le plugin et son contenu actif : utilisez un serveur de préproduction.
  4. Déclencher le hook et vérifier son résultat visible ainsi que la console. Enregistrer le fichier ou faire apparaître un autre boss sans rechargement ne met pas à jour une définition déjà chargée.

Si vous avez besoin de plus de contexte sur les fichiers de boss, les listes de pouvoirs ou la structure des boss personnalisés, consultez Créer des Boss Personnalisés. Cette page couvre la configuration des boss en détail, y compris le fonctionnement de la liste powers:.

Étape 1 : Faire charger le fichier​

return {
api_version = 1,

on_spawn = function(context)
end
}

Le chargeur valide la syntaxe Lua, la table retournée et les noms des hooks. Un hook vide n'a aucun résultat visible : l'absence d'erreur ne prouve donc pas que le fichier a été découvert ou attaché. Ajoutez le son de l'étape 2 et faites apparaître un nouveau boss pour vérifier ce parcours.

Étape 2 : Faire faire quelque chose de visible au boss​

return {
api_version = 1,

on_spawn = function(context)
context.boss:play_sound_at_self("entity.blaze.ambient", 1.0, 1.0)
end
}

Maintenant vous avez la preuve la plus importante pour débutant : votre hook se déclenche.

Étape 3 : Réagir au coup d'un joueur​

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
}

Cela enseigne trois idées fondamentales :

  • on_boss_damaged_by_player est le nom du hook
  • context.player est le joueur impliqué dans ce hook
  • return sort tôt quand les données nécessaires sont manquantes

Étape 4 : Prévenir le spam avec un temps de recharge​

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
}

Chaque tick du temps de recharge local représente 50 millisecondes écoulées : 60 représente trois secondes même si les TPS du serveur baissent. Ce temps de recharge nommé est partagé entre les pouvoirs de ce boss, pas entre différents boss. Préfixez les clés avec le nom du pouvoir lorsque vous combinez des pouvoirs personnalisés. Cet exemple vérifie uniquement sa clé locale, pas le temps de recharge global du boss.

Étape 5 : Ajouter un vrai effet​

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
}

Cela crée un véritable éclair dans le monde, pas un simple effet visuel. Le temps de recharge est défini avant l'effet pour que les événements provoqués par celui-ci le voient. Ce temps de recharge local de 100 ticks représente cinq secondes écoulées.


Premier vrai flux de travail​

Lors de la construction d'un nouveau pouvoir Lua, utilisez cet ordre :

  1. Créer le fichier et faire fonctionner on_spawn.
  2. Passer au hook souhaité.
  3. Confirmer que le hook contient les données attendues, comme context.player ou context.event.
  4. Ajouter d'abord un message ou un son, avant les dégâts ou les particules.
  5. Ajouter les temps de recharge.
  6. Ajouter un effet de gameplay.
  7. Seulement après cela, ajouter des aides, l'état, la logique du planificateur ou les zones.

Cet ordre rend le débogage considérablement plus facile car une seule chose change à la fois.


Où se trouvent les fichiers Lua​

Placez les fichiers .lua dans la même arborescence que les fichiers de pouvoir .yml normaux :

plugins/
EliteMobs/
powers/
mycoolpower.lua
attack_push.yml
subfolder/
myotherpower.lua

Les pouvoirs Lua sont découverts automatiquement depuis les répertoires de pouvoirs qu'EliteMobs charge déjà.

Métadonnées YAML facultatives​

Un pouvoir personnalisé mycoolpower.lua peut avoir un mycoolpower.yml ou un mycoolpower.yaml chargé à ses côtés dans le même dossier. La découverte lit ses champs isEnabled, effect, powerType, powerCooldown et globalCooldown ; isEnabled: false empêche l'enregistrement de ce fichier Lua personnalisé. Sans métadonnées correspondantes, il n'a aucun effet et utilise le type MISCELLANEOUS. Les hooks Lua contrôlent toujours le déclenchement des attaques et doivent vérifier eux-mêmes les temps de recharge.

Un fichier YAML de même nom de base, chargé à côté d'un pouvoir Lua fourni, applique les mêmes champs à ce pouvoir. Sans un tel fichier, le pouvoir fourni conserve ses métadonnées d'origine. Consultez Créer des Pouvoirs pour les champs disponibles.

Comment les boss référencent les pouvoirs Lua​

Les fichiers de boss utilisent toujours la liste normale powers: :

powers:
- attack_push.yml
- mycoolpower.lua

Aucun champ spécial n'est requis. Les pouvoirs Lua ne sont pas chargés via eliteScript:.

Règles de nommage des fichiers​

  • Les boss référencent les pouvoirs Lua par nom de fichier, pas par chemin de dossier.
  • EliteMobs enregistre actuellement les pouvoirs Lua découverts par nom de base uniquement.
  • Évitez les noms en double comme powers/fire.lua et powers/bosses/fire.lua. EliteMobs ne charge que le fichier dont le chemin complet vient en premier dans l'ordre alphabétique, et enregistre un avertissement pour la copie ignorée, sauf si les deux fichiers sont identiques.

Pouvoirs Lua préfabriqués​

La plupart des pouvoirs fournis utilisent Lua. Le code actuel définit 68 pouvoirs Lua préfabriqués concrets, installés dans plugins/EliteMobs/powers/ lorsqu'ils sont absents, notamment attack_fire.lua, frost_cone.lua, meteor_shower.lua et shield_wall.lua. Lisez-les comme exemples, en gardant à l'esprit qu'une copie locale modifiée peut différer de celle livrée avec le plugin.

Les fichiers préfabriqués ne sont écrits que s'ils sont absents : vos modifications survivent aux redémarrages. Lorsqu'une mise à jour modifie un pouvoir fourni, une copie non modifiée d'une version livrée précédente est déplacée vers plugins/MagmaCore/outdated files/EliteMobs/ au démarrage, et la version actuelle est écrite à sa place ; les copies modifiées restent en place. Supprimer un fichier fourni permet à la prochaine initialisation du plugin de régénérer sa version livrée ; conservez vos modifications utiles avant de le remplacer.

Fragments de source partagés​

Les pouvoirs Lua des boss peuvent inclure un fichier auxiliaire avec une directive de commentaire sur sa propre ligne :

-- @include helpers/common.inc

Le chargeur insère le texte du fichier référencé avant de valider le pouvoir. Les chemins sont relatifs au fichier contenant la directive et doivent rester dans le dossier du fichier principal du pouvoir, y compris après résolution des liens symboliques. Les inclusions circulaires, une imbrication de plus de 16 niveaux, les fichiers de plus de 4 Mio et une source développée de plus de 4 194 304 caractères sont refusés.

Utilisez une extension autre que .lua, par exemple .inc, pour que la découverte ne tente pas aussi de charger les fragments comme des pouvoirs autonomes. Déclarez les fonctions auxiliaires avant la table return finale. Cette directive est propre au chargement des pouvoirs de boss : elle n'active ni require ni l'accès aux fichiers, et les scripts des PNJ ne la traitent pas.

Les anciens noms .yml se résolvent toujours​

Les configurations de boss référençant attack_fire.yml continuent de fonctionner. Pour une référence .yml ou .yaml, EliteMobs cherche d'abord un pouvoir Lua chargé portant le même nom de base, puis utilise le nom littéral à défaut. Un fichier invalide ou désactivé ne constitue pas un remplacement chargé. Respectez l'orthographe et la casse du nom. Pour personnaliser un pouvoir Lua fourni, modifiez son fichier existant puis rechargez ; créer des noms de base identiques dans d'autres dossiers n'est pas une méthode de remplacement fiable.


Contrat minimal du fichier​

Chaque pouvoir Lua doit retourner une table avec return. Cette table est intentionnellement stricte.

Champs de niveau supérieur requis et optionnels​

ChampRequisTypeNotes
api_versionOuiNumberUtilisez l'entier 1
priorityNonNumberUtilisez un entier. Les valeurs plus basses s'exécutent en premier ; valeur par défaut 0. Les pouvoirs YAML ont la priorité 0
Clés de hook supportéesNonFunctionUtilisez l'un des noms exacts de la Référence des Hooks

Règles de validation​

  • Le fichier doit retourner une table.
  • api_version est requis et doit actuellement être 1.
  • priority doit être numérique si présent.
  • Chaque clé de niveau supérieur supplémentaire doit être un nom de hook supporté.
  • Chaque clé de hook doit pointer vers une fonction.
  • Les clés de niveau supérieur inconnues sont rejetées.

Cela signifie que les fonctions auxiliaires et constantes locales doivent se trouver au-dessus du return final, pas à l'intérieur de la table retournée sauf s'il s'agit de véritables hooks.

Le corps du fichier est évalué pendant la validation, puis dans un environnement Lua distinct pour chaque instance d'exécution du pouvoir sur un boss. context existe uniquement comme argument d'un hook ou callback, pas comme variable globale du fichier. Les variables locales peuvent persister dans cette instance, mais les valeurs de validation ne lui sont pas transmises. L'environnement retire les accès aux fichiers, au système et à Java ainsi que require ; utilisez la directive d'inclusion pour les fragments de source. Une erreur non interceptée dans un hook ou callback ferme cette instance et annule ses tâches ; elle ne redémarre pas au prochain coup. Corrigez le fichier et rechargez avant de réessayer.


Modèles à copier-coller​

Plus petit pouvoir Lua valide​

return {
api_version = 1,

on_spawn = function(context)
end
}
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
}

Disposition pour fichiers plus grands​

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​

Les hooks sont des fonctions spécialement nommées dans la table que vous retournez. EliteMobs les appelle quand quelque chose se produit -- le boss apparaît, subit des dégâts, entre en combat, etc. Vous avez déjà vu on_spawn et on_boss_damaged_by_player dans le tutoriel ci-dessus.

Les hooks les plus courants pour commencer sont on_spawn, on_boss_damaged_by_player, on_enter_combat et on_exit_combat. Pour la liste complète de tous les hooks, quelles clés de context sont disponibles dans chacun et comment fonctionne l'ordre d'exécution, consultez Hooks et Cycle de Vie.


Qu'est-ce que context ?​

Chaque fonction de hook reçoit un argument appelé context. Pensez-y comme une boîte à outils qu'EliteMobs vous remet chaque fois que quelque chose se produit -- elle contient tout ce dont vous avez besoin pour interagir avec le boss, le joueur, le monde, les temps de recharge et plus encore.

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

Vous ne créez pas context vous-même -- EliteMobs le crée et le transmet à votre hook. Vous lisez simplement ses valeurs et appelez des méthodes dessus.

info

Chaque hook reçoit un nouveau context. Sa table context.state appartient à l'instance d'exécution de ce pouvoir sur ce boss et peut conserver des données pour d'autres hooks et callbacks. Les autres pouvoirs ont leur propre état ; fermer ou recharger l'instance supprime le sien.


APIs clés de context​

Voici les APIs les plus importantes que vous utiliserez depuis context :

  • context.state -- Une table Lua conservée pour cette instance du pouvoir sur ce boss. Stockez-y les phases, identifiants de tâches et indicateurs entre les hooks. Elle n'est pas sauvegardée après suppression ou rechargement de l'instance.

  • context.log -- Journalisation en console avec log:info(msg), log:warn(msg) et log:debug(msg). Ajoutez le nom du pouvoir dans vos messages pour distinguer plusieurs pouvoirs.

  • context.cooldowns -- Temps de recharge nommés et global partagés par les pouvoirs du boss. Les clés explicites occupent un espace commun au boss : préfixez-les avec le nom du pouvoir. Une clé nil choisit le temps de recharge par défaut du nom de fichier Lua. cooldowns:check_local(key, ticks) vérifie et définit le temps de recharge local en un appel ; il ne vérifie ni ne définit le global. Consultez Hooks et Cycle de Vie pour l'API complète.

  • context.scheduler -- scheduler:run_after(ticks, callback) retarde un callback unique. scheduler:run_every(ticks, callback) répète sans attendre le premier intervalle ; utilisez scheduler:run_repeating(delay, interval, callback) pour fixer un délai initial. Les callbacks reçoivent un nouveau contexte sans l'événement ni le joueur déclencheur. Utilisez cet argument et conservez explicitement l'UUID ou la position nécessaire ; ne supposez pas que ctx.player subsiste après un délai. Annulez les répétitions réservées au combat dans on_exit_combat ; sinon les tâches continuent jusqu'à leur annulation ou la fermeture de l'instance. Consultez Hooks et Cycle de Vie.

  • context.boss / context.player -- Le boss et le joueur impliqués dans l'événement actuel. Consultez Boss et Entités pour tous les champs et méthodes.

  • context.world -- Générer des particules, entités, sons, éclairs, blocs. Consultez Monde et Environnement.

  • context.zones / context.script -- Géométrie de zones, ciblage, dégâts, particules. Consultez Zones et Ciblage.


Syntaxe des méthodes : : vs. .​

Pour comprendre la syntaxe : et ., consultez le moteur de scripting Lua de MagmaCore. Les méthodes d'entités et les services partagés acceptent les deux formes. En revanche, context.event.set_damage_amount(value) et context.event.multiply_damage_amount(multiplier) exigent le point : les deux-points passeraient la table d'événement comme premier argument. context.event:transfer_damage(target, amount) accepte les deux-points. Les constructeurs globaux tels que em.create_location(...) utilisent un point.


Prochaines étapes​

Maintenant que vous connaissez les bases, explorez le reste de la documentation de scripting Lua :

  • Hooks et Cycle de Vie -- noms des hooks, ordre d'exécution et quelles clés de context sont disponibles dans chaque hook
  • Scripts de PNJ -- dossier, hooks, événements de proximité et context.npc
  • Boss et Entités -- context.boss, context.player, context.players, context.entities et context.event
  • Monde et Environnement -- particules, sons, éclairs, apparition et context.world
  • Zones et Ciblage -- zones Lua natives, utilitaires de script pour ciblage/zones/particules et vecteurs relatifs
  • Exemples et Modèles -- pouvoirs complets et fonctionnels avec explications
  • Enums et Valeurs -- liens vers les Javadocs Spigot pour Particle, Material, PotionEffectType et plus
  • Dépannage -- erreurs courantes, conseils de débogage et checklist QC

Pour le scripting basé sur YAML, les pages EliteScript restent la référence canonique :