Aller au contenu principal

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 .lua qui 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'id d'un module doit être fmm:pet/ suivi de son nom : pet_behaviors/mine/slow_drift.lua déclare donc id = '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 filename pour 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 reload relit 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 la revision d'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.

  1. Copiez-le sous un nouveau nom, par exemple pet_behaviors/mine/slow_drift.lua à partir de drift.lua.
  2. Changez son id en fmm:pet/slow_drift.
  3. Renommez ses mémoires, comportements et capteurs pour qu'ils commencent par le nouveau nom : drift_to devient slow_drift_to, dans memories et dans chaque appel c.memory, et le id = 'fmm:pet/drift' du comportement devient fmm: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.
  4. Listez-le dans le fichier du familier à la place de l'original : - slow_drift, ou avec des réglages, - slow_drift: { speed: 0.03 }.
  5. Exécutez /fmm reload et 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 }.

ChampRemarques
idObligatoire. fmm:pet/ et le nom du fichier
revisionObligatoire. Tout entier positif
settingsFacultatif. 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
memoriesFacultatif. 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
dependenciesFacultatif. 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
behaviorsFacultatif. Entrées ai.behavior { ... }, qui déplacent ou font tourner le familier
sensorsFacultatif. 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​

NomCe que c'est
c.petLe familier, en lecture seule. Voir c.pet
c.settingsLes 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.memoryget(name), set(name, value), forget(name) et contains(name) pour les noms de memories. Chaque familier a la sienne
c.tickLe tick de jeu actuel, pour les minuteries
c.perception, c.actuator, c.entityLe 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.pet et c.pet.species sont en lecture seule. Leur assigner une valeur lève une erreur ; gardez les valeurs propres à chaque familier dans c.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_owner et c.pet:stop les actualisent. Un déplacement effectué autrement, comme c.entity:teleport, apparaît au tick suivant. speed fait exception et est toujours lue à neuf.
  • Chaque lecture d'une position donne une nouvelle table, que le module peut modifier.

Propriétés​

NomTypeValeur
wildbooltrue pour un familier sauvage, false pour un familier possédé
stayingboolA reçu l'ordre de rester
settledboolLibre de ses mouvements : n'a pas reçu l'ordre de rester, et est sauvage ou à moins de follow.distance de son propriétaire
babyboolUn bébé
migratingboolSuit une route de migration nocturne
modelstringLe modèle de sa variante. Un bébé qui porte babyModel lit toujours sa variante
positionpointOù se trouve son corps
homepoint/nilOù vit un familier sauvage ; nil pour un familier possédé
anchorpoint/nilCe 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_anchorpoint/nilL'endroit où on lui a dit de rester ; nil lorsqu'il ne reste pas sur place
owner_locationpoint/nilnil pour un familier sauvage, ou lorsque le propriétaire est hors ligne, mort ou dans un autre monde
owner_distancenumberBlocs jusqu'au propriétaire ; math.huge lorsqu'il n'y en a aucun dans ce monde
night, dayboolday uniquement en plein jour dans un monde normal ; le crépuscule, l'aube, le Nether et l'End comptent comme night
rainingboolLa pluie tombe sur le familier, vérifié au plus tous les 10 ticks
speednumberLa vitesse actuelle de son corps, en blocs par tick
route_cruisenumberLa vitesse de croisière de sa volée pendant une migration, sinon 0
speciestableLes nombres du fichier du familier, ci-dessous

c.pet.species contient :

ChampClé du fichier du familier
idspecies
speedfollow.speed
hover_heightfloat.hoverHeight
hover_variancefloat.hoverVariance
roam_radiusfollow.roamRadius
follow_distancefollow.distance
leash_distancefollow.leashDistance
teleport_distancefollow.teleportDistance
scalescale
baby_scalebabyScale

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éthodeRenvoieRôle
steer_to(point, cruise)boolUn 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()nilArrête le déplacement. Nécessite move
teleport_near_owner(radius)boolPlace 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éthodeRenvoieRôle
hover_point(center, radius)point/nilUn point de sa plage de flottement à radius au plus
rest_point(center, radius)point/nilUn 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/nilUn 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/nilUn endroit où nager entre les deux profondeurs sous la surface (0 à 16)
ground_below(point, depth)number/nilLa 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/nilback 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)numberBlocs depuis son corps
route_target()point/nilOù 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/nilheight 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/nilLe 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)boolSi 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éthodeRenvoieRô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()nilRetour à la lumière du monde, ce qui met fin à cette animation
scale_toward(target, span, ticks, elapsed)nilFait 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)numberPique 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)numberPique 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)boolLorsque 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)boolDonne 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)boolJoue 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.move et ai.controls.look pour les appels de déplacement de c.pet, et un seul comportement détient un contrôle à la fois. Tant qu'il est arrêté, MagmaCore interroge son can_start. Lorsque celui-ci renvoie true et que les contrôles sont libres, il les prend et exécute start, puis tick à chaque tick tant que can_continue renvoie true (sans can_continue, can_start décide), puis stop. Voir Cycle de vie.
  • Le numéro de priority le plus bas passe en premier. Un comportement prend les contrôles à un comportement en cours dont le numéro est plus élevé, dont le stop s'exécute alors. Avec le 12 de 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 avant campfire : un familier qui liste les deux ne visite donc jamais un feu la nuit. Un nombre supérieur à 15 laisserait le feu l'emporter.
  • Un comportement ne se dispute que les contrôles qu'il énumère. idle_look ne prend que look, il s'exécute donc tant qu'aucun comportement ne détient le regard ; tous les comportements de déplacement fournis prennent aussi look, 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_interval est facultatif, en ticks : tant que le comportement est arrêté, can_start n'est interrogé que tous les probe_interval ticks, 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 vaut 1 par défaut, et les valeurs supérieures à 200 comptent comme 200. Un comportement en cours s'exécute toujours à chaque tick.
  • Un capteur ne déplace jamais le familier. Il exécute sense au premier tick où le corps du familier est actualisé, puis tous les interval ticks 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 :

ModulePrioritéprobe_intervalinterval du capteur
migrate110
stay54
follow_owner104
campfire1510
day_perch1810
rain_bloom19104 (gonflement)
drift, wander, swim2010
hover304
idle_look3010
night_glow40
sting4 (joueurs), 10 (mobs)
crop_drizzle20
owner_effect20

Règles​

  • N'appelez steer_to et stop que depuis le start, le tick ou le stop d'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é sous memories, 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 :
LimiteValeur
Un callback1,5 ms
Les callbacks d'un familier en un tick3 ms, 32 callbacks au plus
Les callbacks de tous les familiers en un tick10 ms
Temps CPU d'un callback, limite stricte20 ms
Instructions Lua d'un callback, limite stricte20 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, comme fmm: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 avec c.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.