Modules de comportement des familiers
Un module de comportement de familier est un fichier Lua qui indique aux familiers quoi faire : suivre leur propriétaire, dériver, tourner autour d'un feu de camp, piquer. Chaque fichier d'espèce de familier énumère les modules que ses familiers exécutent et leur donne des réglages. Cette page s'adresse aux créateurs de créatures qui veulent modifier un module ou en écrire un nouveau. Pour utiliser et ajuster les modules fournis, voir Familiers.
Les modules utilisent le même dialecte Lua que les programmes de comportement d'EliteMobs : ai.module, ai.behavior, ai.sensor, ai.controls, les mémoires typées, et le contexte c.memory, c.perception, c.actuator et c.entity. Cette page-là décrit les règles communes. Celle-ci décrit ce que les familiers ajoutent : les fichiers de module, c.pet, c.settings et les limites sous lesquelles les familiers s'exécutent.
Fichiers
- Les modules se trouvent dans
plugins/FreeMinecraftModels/pet_behaviors/, dans ce dossier ou dans n'importe quel sous-dossier. Chaque fichier.luaqui s'y trouve est un module. - Le nom d'un module est son nom de fichier sans
.lua, en minuscules. Il ne peut contenir que des lettres, des chiffres,.,_et-. Les fichiers de familiers énumèrent les modules par ce nom. - L'
idd'un module doit êtrefmm:pet/suivi de son nom :pet_behaviors/mine/slow_drift.luadéclare doncid = 'fmm:pet/slow_drift'. Un module dont l'id ne correspond pas à son nom de fichier est ignoré avec un avertissement. - Lorsque deux fichiers portent le même nom, celui dont le chemin vient en premier dans l'ordre de tri se charge, et la console avertit
Duplicate content filenamepour l'autre. Une copie identique octet pour octet est ignorée silencieusement. - Un fichier ou un dossier qui pointe en dehors de
pet_behaviors/est ignoré. - FreeMinecraftModels écrit chacun de ses 15 modules fournis lorsqu'aucun fichier de ce nom n'existe nulle part dans le dossier. Un module supprimé revient au chargement suivant avec la version fournie actuelle. Un fichier déjà présent n'est jamais écrasé, modifié ou non, même après que vous l'avez déplacé dans un sous-dossier : il conserve donc vos modifications et manque aussi les mises à jour ultérieures du module fourni.
/fmm reloadrelit chaque module et chaque fichier de familier, range les familiers et les remet sur les nouveaux modules. Leurs mémoires repartent vides. Vous n'avez pas besoin d'augmenter larevisiond'un module après l'avoir modifié.
Partir d'une copie
Les modules fournis sont les meilleurs exemples : drift.lua déplace un volant, wander.lua marche, night_glow.lua est un capteur unique et sting.lua en a deux. Partez de celui qui se rapproche le plus de ce que vous voulez.
- Copiez-le sous un nouveau nom, par exemple
pet_behaviors/mine/slow_drift.luaà partir dedrift.lua. - Changez son
idenfmm:pet/slow_drift. - Renommez ses mémoires, comportements et capteurs pour qu'ils commencent par le nouveau nom :
drift_todevientslow_drift_to, dansmemorieset dans chaque appelc.memory, et leid = 'fmm:pet/drift'du comportement devientfmm:pet/slow_drift. Deux modules sur une même créature ne peuvent pas partager un nom de mémoire, un id de comportement ou un id de capteur ; le second listé est écarté avec un avertissement. Renommer permet à la copie de s'exécuter à côté de l'original. - Listez-le dans le fichier du familier à la place de l'original :
- slow_drift, ou avec des réglages,- slow_drift: { speed: 0.03 }. - Exécutez
/fmm reloadet vérifiez la console.
Modifier directement un module fourni fonctionne aussi, mais cela change toutes les espèces qui le listent.
Un fichier de module
Ce module est petit mais complet. La nuit, le familier s'installe sur un endroit près de son ancre au lieu de dériver, et toutes les 10 secondes à partir du moment où il choisit cet endroit, il joue une animation.
-- At night: settles on one spot near its anchor (its owner, or home when wild) and plays an animation now and then.
return ai.module {
id = 'fmm:pet/night_rest', revision = 1,
-- radius: blocks around the anchor. speed: blocks per tick for a flyer, at species speed 1.
settings = { radius = 3, speed = 0.04, animation = 'interact' },
memories = { night_rest_to = 'position', night_rest_retry = 'integer' },
behaviors = {
ai.behavior {
id = 'fmm:pet/night_rest', priority = 12, probe_interval = 10,
controls = { ai.controls.move, ai.controls.look },
can_start = function(c)
return c.pet.night and not c.pet.staying and c.pet.anchor ~= nil
and c.tick >= (c.memory:get('night_rest_retry') or 0)
end,
can_continue = function(c)
return c.pet.night and not c.pet.staying and c.memory:contains('night_rest_to')
end,
start = function(c)
local anchor = c.pet.anchor
local to = anchor and c.pet:rest_point(anchor, c.settings.radius)
if to ~= nil then
c.memory:set('night_rest_to', to)
else
-- Nothing fits here: look again in 5 seconds instead of every tick.
c.memory:set('night_rest_retry', c.tick + 100)
end
end,
tick = function(c)
local to = c.memory:get('night_rest_to')
if to ~= nil then c.pet:steer_to(to, c.settings.speed * c.pet.species.speed) end
end,
stop = function(c)
c.memory:forget('night_rest_to')
c.pet:stop()
end
}
},
sensors = {
ai.sensor {
id = 'fmm:pet/night_rest_animation', interval = 200,
sense = function(c)
if c.memory:contains('night_rest_to') then c.pet:play_animation(c.settings.animation) end
end
}
}
}
Un fichier de familier le liste ensuite sous la forme - night_rest, ou - night_rest: { radius: 5, animation: idle }.
| Champ | Remarques |
|---|---|
id | Obligatoire. fmm:pet/ et le nom du fichier |
revision | Obligatoire. Tout entier positif |
settings | Facultatif. Les valeurs par défaut que les fichiers de familiers peuvent changer. Le type de chaque valeur par défaut est le type qu'un fichier de familier doit fournir : un nombre, true/false, du texte, ou une liste de textes. Voir Valeurs des réglages |
memories | Facultatif. Ce que le module retient pour chaque familier, par nom et par type : integer, number, boolean, string, uuid ou position. Faites commencer chaque nom par le nom du module. Voir Mémoires |
dependencies | Facultatif. Ids d'autres modules de pet_behaviors/ dont celui-ci a besoin. Ils s'exécutent sur chaque créature qui liste celui-ci, avec leurs réglages par défaut, sauf si le fichier du familier les liste aussi |
behaviors | Facultatif. Entrées ai.behavior { ... }, qui déplacent ou font tourner le familier |
sensors | Facultatif. Entrées ai.sensor { ... }, qui ne déplacent jamais le familier |
Le code placé avant return, comme les fonctions locales et les constantes, est autorisé ; wander.lua et idle_look.lua en contiennent.
Ce que reçoit un callback
| Nom | Ce que c'est |
|---|---|
c.pet | Le familier, en lecture seule. Voir c.pet |
c.settings | Les réglages de ce module : ses valeurs par défaut, avec par-dessus celles du fichier du familier et de la variante, comme dans c.settings.radius. Chaque familier a sa propre copie : y écrire ne change donc que ce familier |
c.memory | get(name), set(name, value), forget(name) et contains(name) pour les noms de memories. Chaque familier a la sienne |
c.tick | Le tick de jeu actuel, pour les minuteries |
c.perception, c.actuator, c.entity | Le contexte Mind de MagmaCore, comme dans les programmes de comportement d'EliteMobs. c.entity est le corps caché, pas le modèle. Voir Contexte d'un programme Mind. idle_look.lua utilise c.perception:nearest_player(range) et c.actuator:look_at(point) |
c.pet
Un point est une table avec x, y et z, et éventuellement world, comme c.pet.position. Un point sans world se trouve dans le monde du familier. Un point situé dans un autre monde ne compte pour rien : les méthodes répondent nil, false ou 0, et distance_to répond math.huge. Les points que renvoie c.pet sont toujours dans le monde du familier, et un point qui serait ailleurs se lit nil.
- Les méthodes s'appellent avec deux-points :
c.pet:distance_to(point). - Les arguments numériques doivent être finis. Les rayons, profondeurs, hauteurs, amplificateurs, durées et autres plages sont plafonnés comme indiqué ci-dessous. Aucune méthode ne charge un chunk.
c.petetc.pet.speciessont en lecture seule. Leur assigner une valeur lève une erreur ; gardez les valeurs propres à chaque familier dansc.memory.- Les propriétés et
route_target()sont un instantané : chacune est lue une fois par tick du familier et reste identique dans tous les callbacks de ce tick.c.pet:teleport_near_owneretc.pet:stoples actualisent. Un déplacement effectué autrement, commec.entity:teleport, apparaît au tick suivant.speedfait exception et est toujours lue à neuf. - Chaque lecture d'une position donne une nouvelle table, que le module peut modifier.
Propriétés
| Nom | Type | Valeur |
|---|---|---|
wild | bool | true pour un familier sauvage, false pour un familier possédé |
staying | bool | A reçu l'ordre de rester |
settled | bool | Libre de ses mouvements : n'a pas reçu l'ordre de rester, et est sauvage ou à moins de follow.distance de son propriétaire |
baby | bool | Un bébé |
migrating | bool | Suit une route de migration nocturne |
model | string | Le modèle de sa variante. Un bébé qui porte babyModel lit toujours sa variante |
position | point | Où se trouve son corps |
home | point/nil | Où vit un familier sauvage ; nil pour un familier possédé |
anchor | point/nil | Ce autour de quoi il se promène : son propriétaire, ou, pour un familier sauvage, sa maison, ou l'endroit où il se trouve s'il n'en a pas. nil lorsque le propriétaire est hors ligne ou dans un autre monde |
stay_anchor | point/nil | L'endroit où on lui a dit de rester ; nil lorsqu'il ne reste pas sur place |
owner_location | point/nil | nil pour un familier sauvage, ou lorsque le propriétaire est hors ligne, mort ou dans un autre monde |
owner_distance | number | Blocs jusqu'au propriétaire ; math.huge lorsqu'il n'y en a aucun dans ce monde |
night, day | bool | day uniquement en plein jour dans un monde normal ; le crépuscule, l'aube, le Nether et l'End comptent comme night |
raining | bool | La pluie tombe sur le familier, vérifié au plus tous les 10 ticks |
speed | number | La vitesse actuelle de son corps, en blocs par tick |
route_cruise | number | La vitesse de croisière de sa volée pendant une migration, sinon 0 |
species | table | Les nombres du fichier du familier, ci-dessous |
c.pet.species contient :
| Champ | Clé du fichier du familier |
|---|---|
id | species |
speed | follow.speed |
hover_height | float.hoverHeight |
hover_variance | float.hoverVariance |
roam_radius | follow.roamRadius |
follow_distance | follow.distance |
leash_distance | follow.leashDistance |
teleport_distance | follow.teleportDistance |
scale | scale |
baby_scale | babyScale |
Déplacement
Appelez-les uniquement depuis le start, le tick ou le stop d'un comportement, dans un comportement dont les controls listent ce qu'elles exigent. Depuis can_start ou un capteur, steer_to et stop lèvent une erreur qui indique où elles ont leur place.
| Méthode | Renvoie | Rôle |
|---|---|---|
steer_to(point, cruise) | bool | Un tick vers le point : appelez-la donc à chaque tick. Nécessite move et look. Un volant vole à cruise blocs par tick (0 à 4), en ralentissant à l'approche. Un marcheur ou un nageur s'y rend par la navigation native à 4 × cruise fois la vitesse de déplacement de son type de corps, sans jamais descendre sous un quart de celle-ci ; un cruise de 0 l'arrête et renvoie false. false lorsqu'il ne peut pas y aller |
stop() | nil | Arrête le déplacement. Nécessite move |
teleport_near_owner(radius) | bool | Place le corps à côté de son propriétaire, à radius blocs au plus (jusqu'à 64), dans son milieu propre ; l'eau d'un nageur est cherchée dans un rayon d'au moins 6 blocs. Un marcheur ou un nageur atterrit à au moins un bloc du propriétaire. false lorsque le propriétaire est absent ou dans un autre monde, que rien ne convient, ou que l'endroit n'est pas au moins un bloc plus près du propriétaire que ne l'est le familier. Elle n'exige aucun contrôle, mais appelez-la depuis un comportement qui détient move, comme le fait follow_owner, afin de ne pas arracher le familier à un autre comportement |
Points et distances
Elles fonctionnent depuis n'importe quel callback. Celles qui renvoient un point renvoient nil lorsque rien ne convient ou que le chunk n'est pas chargé. Les rayons vont jusqu'à 64.
| Méthode | Renvoie | Rôle |
|---|---|---|
hover_point(center, radius) | point/nil | Un point de sa plage de flottement à radius au plus |
rest_point(center, radius) | point/nil | Un endroit où se reposer dans son milieu propre : la plage de flottement pour un volant, un sol où se tenir pour un marcheur ou un corps stationnaire, de l'eau pour un nageur (à la hauteur de center lorsque center est dans l'eau, sinon 0,5 à 2 blocs sous la surface), et le sol ou à défaut l'eau pour un corps amphibie |
ground_point(center, radius) | point/nil | Un endroit où se tenir, d'environ 5 blocs en dessous à 4 au-dessus de center. nil au-dessus d'un liquide ou d'une longue chute |
water_point(center, radius, min_depth, max_depth) | point/nil | Un endroit où nager entre les deux profondeurs sous la surface (0 à 16) |
ground_below(point, depth) | number/nil | La hauteur du dessus du sol à depth blocs au plus vers le bas (1 à 64). Une surface d'eau ou de lave compte comme du sol, sauf si l'espèce est AQUATIC. Sans sol à portée, la hauteur du point moins depth |
trail_point(back, height) | point/nil | back blocs derrière la direction du regard du propriétaire, height au-dessus de ses pieds pour une espèce FLYING et à ses pieds sinon. nil lorsque le propriétaire est absent ou dans un autre monde |
distance_to(point) | number | Blocs depuis son corps |
route_target() | point/nil | Où sa route de migration veut l'amener ce tick. nil lorsqu'il ne migre pas ou que la route est terminée |
find_perch(height, radius) | point/nil | height blocs (0 à 8) au-dessus des feuilles les plus proches sous lui, jusqu'à radius blocs (0 à 4) sur le côté, ou au-dessus du sol juste en dessous. Un familier possédé n'obtient un perchoir qu'à moins de follow.distance de son propriétaire |
nearest_campfire(radius, keep_ticks, blocks, soul_variants, height) | point/nil | Le bloc du feu allumé le plus proche qu'il peut utiliser dans un rayon de radius blocs (1 à 32). blocks énumère les types de feux qu'il prend, CAMPFIRE, SOUL_CAMPFIRE ou les deux, et une liste qui n'en nomme aucun prend les deux ; soul_variants énumère les modèles qui ne prennent que les feux de camp des âmes, et tous les autres modèles ne prennent alors que les feux ordinaires. Un familier possédé ne prend qu'un feu dont l'endroit situé height au-dessus est à moins de follow.distance moins 1 de son propriétaire. La recherche de chaque chunk est conservée keep_ticks (20 à 1200) et partagée par tous les familiers |
campfire_usable(fire, blocks, soul_variants, height) | bool | Si un feu trouvé plus tôt est toujours allumé et convient toujours |
Effets
Elles fonctionnent depuis n'importe quel callback ; les capteurs en sont le lieu habituel.
| Méthode | Renvoie | Rôle |
|---|---|---|
set_glow(light, lantern_bone, animation) | nil | Éclaire les os d'affichage du modèle à light (0 à 15), pour les joueurs Java. Un modèle doté d'un os nommé lantern_bone joue aussi animation en boucle. Les deux derniers arguments sont facultatifs |
clear_glow() | nil | Retour à la lumière du monde, ce qui met fin à cette animation |
scale_toward(target, span, ticks, elapsed) | nil | Fait tendre en douceur la taille du corps vers target fois sa taille de base. Un changement complet de 1 à span prend ticks (au moins 2) ; elapsed est le nombre de ticks écoulés depuis le dernier appel. Multiplicateurs de 0.1 à 16 |
sting_contacts(on, effect, duration, amplifier, damage, cooldown) | number | Pique les joueurs dont le contact a atteint le modèle d'un familier sauvage dans la dernière seconde, jamais un joueur en mode Créatif ou Spectateur, et renvoie leur nombre. Elle efface les contacts même lorsque on vaut false. Les familiers possédés n'enregistrent aucun contact |
sting_enemies(reach, effect, duration, amplifier, damage, cooldown) | number | Pique les mobs hostiles situés à reach blocs au plus (0 à 4) de la hitbox du modèle, jamais les joueurs ni les familiers, et renvoie leur nombre |
drizzle(depth, chance) | bool | Lorsque le familier plane immobile au-dessus d'une culture à depth blocs au plus (1 à 8), des particules d'eau et une probabilité chance (0 à 1) que la culture gagne un stade. true lorsqu'il est au-dessus d'une culture |
owner_effect(effect, duration, amplifier, range) | bool | Donne au propriétaire l'effet de potion pendant duration ticks (1 à 6000) lorsqu'il se trouve à range blocs au plus, sans particules. Un effet que le propriétaire a déjà, au moins aussi puissant et pour au moins aussi longtemps, n'est pas touché. false lorsque le propriétaire est absent, dans un autre monde ou hors de portée, ou que l'effet n'existe pas |
play_animation(name) | bool | Joue une fois l'une des animations du modèle. false lorsque le modèle ne la possède pas |
Pour les deux piqûres, effect est un nom d'effet de potion ou NONE, duration est en ticks (1 à 6000), amplifier 0 correspond au niveau I (jusqu'à 255), damage est en demi-cœurs, et cooldown est le nombre de ticks (au moins 1) avant que ce familier puisse piquer de nouveau la même victime. Une piqûre affiche des étincelles et joue un son de piqûre d'abeille, et un adulte joue son animation attack. Un nom d'effet de potion qui n'existe pas fait l'objet d'un avertissement une fois par espèce et par nom, et n'applique rien.
Comportements et capteurs
- Un comportement déplace ou fait tourner le familier. Il énumère les contrôles dont il a besoin,
ai.controls.moveetai.controls.lookpour les appels de déplacement dec.pet, et un seul comportement détient un contrôle à la fois. Tant qu'il est arrêté, MagmaCore interroge soncan_start. Lorsque celui-ci renvoietrueet que les contrôles sont libres, il les prend et exécutestart, puistickà chaque tick tant quecan_continuerenvoietrue(sanscan_continue,can_startdécide), puisstop. Voir Cycle de vie. - Le numéro de
priorityle plus bas passe en premier. Un comportement prend les contrôles à un comportement en cours dont le numéro est plus élevé, dont lestops'exécute alors. Avec le12de l'exemple, le suivi et l'ordre de rester passent avant ce module, qui passe lui-même avant la dérive et le flottement, mais aussi avantcampfire: un familier qui liste les deux ne visite donc jamais un feu la nuit. Un nombre supérieur à15laisserait le feu l'emporter. - Un comportement ne se dispute que les contrôles qu'il énumère.
idle_lookne prend quelook, il s'exécute donc tant qu'aucun comportement ne détient le regard ; tous les comportements de déplacement fournis prennent aussilook, se déplacer l'emporte donc toujours sur lui. - Un fichier de familier ne peut pas changer une priorité. Pour réordonner, copiez le module et changez le nombre.
probe_intervalest facultatif, en ticks : tant que le comportement est arrêté,can_startn'est interrogé que tous lesprobe_intervalticks, sur la phase propre à chaque familier. Cela économise du temps serveur lorsque de nombreux familiers sont dehors, au prix d'un démarrage du comportement retardé d'au plus ce nombre de ticks. Il vaut1par défaut, et les valeurs supérieures à200comptent comme200. Un comportement en cours s'exécute toujours à chaque tick.- Un capteur ne déplace jamais le familier. Il exécute
senseau premier tick où le corps du familier est actualisé, puis tous lesintervalticks sur la phase propre au familier, quoi que fassent les comportements. La lueur, le gonflement, les piqûres, la bruine et les effets sur le propriétaire sont des capteurs.
Les modules fournis utilisent :
| Module | Priorité | probe_interval | interval du capteur |
|---|---|---|---|
migrate | 1 | 10 | |
stay | 5 | 4 | |
follow_owner | 10 | 4 | |
campfire | 15 | 10 | |
day_perch | 18 | 10 | |
rain_bloom | 19 | 10 | 4 (gonflement) |
drift, wander, swim | 20 | 10 | |
hover | 30 | 4 | |
idle_look | 30 | 10 | |
night_glow | 40 | ||
sting | 4 (joueurs), 10 (mobs) | ||
crop_drizzle | 20 | ||
owner_effect | 20 |
Règles
- N'appelez
steer_toetstopque depuis lestart, letickou lestopd'un comportement, dans un comportement qui énumère les contrôles dont elles ont besoin. - Gardez l'état propre à chaque familier dans
c.memory, déclaré sousmemories, avec des noms qui commencent par le nom du module. - N'écrivez pas dans
c.pet; lui assigner une valeur lève une erreur. - Gardez les callbacks courts. Chaque familier s'exécute sous ces limites fixes, que les modules et les fichiers de familiers ne peuvent pas changer :
| Limite | Valeur |
|---|---|
| Un callback | 1,5 ms |
| Les callbacks d'un familier en un tick | 3 ms, 32 callbacks au plus |
| Les callbacks de tous les familiers en un tick | 10 ms |
| Temps CPU d'un callback, limite stricte | 20 ms |
| Instructions Lua d'un callback, limite stricte | 20 000 |
Lorsqu'un familier atteint l'une des trois premières limites, ses callbacks restants attendent le tick suivant. Un callback qui dépasse une limite stricte est interrompu et échoue comme une erreur. Voir Budgets.
En cas de problème
Rien de ce qui suit n'arrête le serveur ni les autres créatures. Cherchez pet behavior, pet_behaviors et .pet.yml dans la console pour trouver ces avertissements.
- Un module ne se charge pas. Un module qui ne compile pas, dont l'id ne correspond pas à son nom de fichier, ou dont le nom de fichier contient d'autres caractères est ignoré avec un avertissement qui nomme le fichier, ainsi que la ligne pour une erreur Lua. Tous les autres modules se chargent quand même.
- Un fichier de familier nomme un module qui n'existe pas. L'avertissement nomme le fichier du familier et suggère le nom de module le plus proche lorsque l'un d'eux est à une ou deux lettres près. La créature se charge sans lui. Lorsque le module existe mais n'a pas pu se charger, l'avertissement renvoie à l'avertissement concernant son fichier.
- Un réglage est incorrect. Un réglage que le module n'a pas, ou une valeur du mauvais type, est signalé avec le nom du fichier du familier, et la valeur par défaut est conservée. Voir Valeurs des réglages.
- Deux modules entrent en conflit. Lorsque deux modules d'une même créature déclarent le même nom de mémoire, id de comportement ou id de capteur, la créature conserve le premier de la liste et s'exécute sans l'autre, avec un avertissement.
- Un callback échoue pendant l'exécution des familiers. L'échec est journalisé une fois par programme, callback et type d'échec jusqu'au prochain rechargement. Les programmes s'appellent
fmm:pet/creature/<species>/<variant>, et les callbacks portent l'id de leur comportement ou de leur capteur, commefmm:pet/drift.tick. Un comportement en échec est arrêté, ou non démarré, et réessayé dès le tick suivant ; tant qu'il continue d'échouer, les comportements dont le numéro de priorité est plus élevé qui déplacent ou font tourner le familier peuvent ne pas s'exécuter. Un capteur en échec s'exécute de nouveau à son prochain intervalle. - Un appel
c.petéchoue à l'intérieur de FreeMinecraftModels. La cause est journalisée une fois par espèce et par appel, avec sa trace de pile, et le callback échoue avecc.pet:<method> failed; the server log has the cause. - Un nom d'effet de potion n'existe pas. Il fait l'objet d'un avertissement une fois par espèce et par nom, et l'appel n'applique aucun effet.
Après une correction, exécutez /fmm reload.