Перейти к основному содержимому

Модули поведения питомцев

Модуль поведения питомца — это Lua-файл, который говорит питомцам, что делать: следовать за владельцем, дрейфовать, кружить у костра, жалить. Каждый файл вида питомца перечисляет модули, которые выполняют его питомцы, и задаёт им настройки. Эта страница предназначена для авторов существ, которые хотят изменить модуль или написать новый. Чтобы использовать и настраивать поставляемые модули, см. Питомцы.

Модули используют тот же диалект Lua, что и программы поведения EliteMobs: ai.module, ai.behavior, ai.sensor, ai.controls, типизированные памяти и контекст c.memory, c.perception, c.actuator и c.entity. Общие правила описаны на той странице. Здесь рассказано о том, что добавляют питомцы: файлы модулей, c.pet, c.settings и лимиты, в которых работают питомцы.

Файлы​

  • Модули лежат в plugins/FreeMinecraftModels/pet_behaviors/, в этой папке или в любой подпапке. Каждый файл .lua там — модуль.
  • Имя модуля — это имя его файла без .lua, в нижнем регистре. Оно может содержать только буквы, цифры, ., _ и -. Файлы питомцев перечисляют модули по этому имени.
  • id модуля должен быть fmm:pet/ плюс его имя, то есть pet_behaviors/mine/slow_drift.lua объявляет id = 'fmm:pet/slow_drift'. Модуль, id которого не совпадает с именем файла, пропускается с предупреждением.
  • Если у двух файлов одинаковое имя, загружается тот, чей путь идёт первым при сортировке, а консоль предупреждает о другом с сообщением Duplicate content filename. Побайтово идентичная копия пропускается молча.
  • Файл или папка, ссылающиеся за пределы pet_behaviors/, пропускаются.
  • FreeMinecraftModels записывает каждый из своих 15 поставляемых модулей, если файла с таким именем нет нигде в папке. Удалённый модуль возвращается при следующей загрузке в текущей поставляемой версии. Уже существующий файл никогда не перезаписывается, отредактирован он или нет, даже если вы перенесли его в подпапку, поэтому он сохраняет ваши изменения, но и не получает последующих обновлений поставляемого модуля.
  • /fmm reload перечитывает каждый модуль и файл питомца, убирает питомцев и возвращает их уже с новыми модулями. Их памяти начинаются пустыми. Повышать revision модуля после его редактирования не нужно.

Начните с копии​

Поставляемые модули — лучшие примеры: drift.lua двигает летающего питомца, wander.lua ходит, night_glow.lua — один сенсор, а у sting.lua их два. Начните с того, который ближе всего к желаемому.

  1. Скопируйте его под новым именем, например pet_behaviors/mine/slow_drift.lua из drift.lua.
  2. Измените его id на fmm:pet/slow_drift.
  3. Переименуйте его памяти, поведения и сенсоры, чтобы они начинались с нового имени: drift_to становится slow_drift_to в memories и в каждом вызове c.memory, а id = 'fmm:pet/drift' у поведения становится fmm:pet/slow_drift. Два модуля на одном существе не могут иметь одно и то же имя памяти, id поведения или id сенсора; второй из перечисленных исключается с предупреждением. Переименование позволяет копии работать одновременно с оригиналом.
  4. Укажите его в файле питомца вместо оригинала: - slow_drift, а с настройками — - slow_drift: { speed: 0.03 }.
  5. Выполните /fmm reload и проверьте консоль.

Редактирование поставляемого модуля на месте тоже работает, но меняет каждый вид, который его перечисляет.

Файл модуля​

Этот модуль небольшой, но законченный. Ночью питомец устраивается на одном месте рядом со своим якорем вместо дрейфа и каждые 10 секунд с момента, когда выбрал это место, проигрывает анимацию.

-- 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
}
}
}

Затем файл питомца перечисляет его как - night_rest или - night_rest: { radius: 5, animation: idle }.

ПолеПримечания
idОбязательно. fmm:pet/ и имя файла
revisionОбязательно. Любое положительное целое число
settingsНеобязательно. Значения по умолчанию, которые могут менять файлы питомцев. Тип каждого значения по умолчанию — тип, который должен дать файл питомца: число, true/false, текст или список текстов. См. Значения настроек
memoriesНеобязательно. Что модуль помнит для каждого питомца, по имени и типу: integer, number, boolean, string, uuid или position. Начинайте каждое имя с имени модуля. См. Памяти
dependenciesНеобязательно. Id других модулей в pet_behaviors/, которые нужны этому. Они выполняются на каждом существе, перечисляющем этот модуль, с настройками по умолчанию, если файл питомца не перечисляет и их
behaviorsНеобязательно. Записи ai.behavior { ... }, которые двигают или поворачивают питомца
sensorsНеобязательно. Записи ai.sensor { ... }, которые никогда не двигают питомца

Код выше return, такой как локальные функции и константы, допускается; он есть в wander.lua и idle_look.lua.

Что получает коллбэк​

ИмяЧто это
c.petПитомец, только для чтения. См. c.pet
c.settingsНастройки этого модуля: его значения по умолчанию с наложенными значениями из файла питомца и варианта, как в c.settings.radius. У каждого питомца своя копия, поэтому запись в неё меняет только этого питомца
c.memoryget(name), set(name, value), forget(name) и contains(name) для имён из memories. У каждого питомца свои
c.tickТекущий игровой тик, для таймеров
c.perception, c.actuator, c.entityКонтекст Mind из MagmaCore, как в программах поведения EliteMobs. c.entity — скрытое тело, а не модель. См. Контекст программы Mind. idle_look.lua использует c.perception:nearest_player(range) и c.actuator:look_at(point)

c.pet​

Точка — это таблица с x, y и z и, по желанию, world, как c.pet.position. Точка без world находится в мире питомца. Точка в другом мире считается ничем: методы отвечают nil, false или 0, а distance_to отвечает math.huge. Точки, которые возвращает c.pet, всегда находятся в мире питомца, а та, что оказалась бы в другом, читается как nil.

  • Методы вызываются с двоеточием: c.pet:distance_to(point).
  • Числовые аргументы должны быть конечными. Радиусы, глубины, высоты, усилители, длительности и другие диапазоны ограничены, как указано ниже. Ни один метод не загружает чанк.
  • c.pet и c.pet.species доступны только для чтения. Присваивание им вызывает ошибку; храните значения отдельного питомца в c.memory.
  • Свойства и route_target() — снимок состояния: каждое читается один раз за тик питомца и остаётся одним и тем же во всех коллбэках этого тика. c.pet:teleport_near_owner и c.pet:stop обновляют их. Перемещение другим способом, например c.entity:teleport, проявится на следующем тике. speed — исключение: он всегда читается заново.
  • Каждое чтение позиции даёт новую таблицу, которую модуль может изменять.

Свойства​

ИмяТипЗначение
wildbooltrue для дикого питомца, false для питомца с владельцем
stayingboolЕму велено оставаться на месте
settledboolНе остаётся на месте и дикий либо находится в пределах follow.distance от владельца
babyboolДетёныш
migratingboolЛетит по маршруту ночной миграции
modelstringМодель его варианта. Детёныш в babyModel всё равно читает свой вариант
positionpointГде находится его тело
homepoint/nilГде живёт дикий питомец; nil для питомца с владельцем
anchorpoint/nilВокруг чего он бродит: вокруг владельца, а у дикого питомца вокруг дома или места, где он находится, если дома нет. nil, когда владелец не в сети или в другом мире
stay_anchorpoint/nilМесто, где ему велели оставаться; nil, когда он не остаётся на месте
owner_locationpoint/nilnil для дикого питомца или когда владелец не в сети, мёртв или в другом мире
owner_distancenumberБлоков до владельца; math.huge, когда его нет в этом мире
night, dayboolday только при дневном свете в обычном мире; сумерки, рассвет, Незер и Энд считаются night
rainingboolНа питомца падает дождь, проверяется не чаще чем раз в 10 тиков
speednumberТекущая скорость его тела в блоках за тик
route_cruisenumberКрейсерская скорость его стаи на миграции, иначе 0
speciestableЧисла из файла питомца, см. ниже

c.pet.species содержит:

ПолеКлюч файла питомца
idspecies
speedfollow.speed
hover_heightfloat.hoverHeight
hover_variancefloat.hoverVariance
roam_radiusfollow.roamRadius
follow_distancefollow.distance
leash_distancefollow.leashDistance
teleport_distancefollow.teleportDistance
scalescale
baby_scalebabyScale

Движение​

Вызывайте их только из start, tick или stop поведения, в поведении, чьи controls перечисляют то, что им нужно. Из can_start или сенсора steer_to и stop вызывают ошибку с указанием, откуда их можно вызывать.

МетодВозвращаетЧто делает
steer_to(point, cruise)boolОдин тик к точке, поэтому вызывайте его на каждом тике. Нужны move и look. Летающий питомец летит со скоростью cruise блоков за тик (от 0 до 4), замедляясь по мере приближения. Наземный или плавающий прокладывает туда путь штатной навигацией со скоростью 4 × cruise от скорости движения своего типа тела, но не меньше четверти её; cruise, равный 0, останавливает его и возвращает false. false, когда он не может туда попасть
stop()nilПрекращает движение. Нужен move
teleport_near_owner(radius)boolСтавит тело рядом с владельцем, в пределах radius блоков (до 64), в его собственной среде; плавающий питомец ищет воду в пределах radius или 6 блоков, смотря что больше. Наземный или плавающий питомец приземляется минимум в блоке от владельца. false, когда владелец отсутствует или в другом мире, ничего не подходит либо найденное место не оказывается хотя бы на блок ближе к владельцу, чем питомец сейчас. Не требует элементов управления, но вызывайте его из поведения, удерживающего move, как это делает follow_owner, чтобы он не уводил питомца от другого поведения

Места и расстояния​

Работают из любого коллбэка. Те, что возвращают точку, возвращают nil, когда ничего не подходит или чанк не загружен. Радиусы доходят до 64.

МетодВозвращаетЧто делает
hover_point(center, radius)point/nilТочка в его диапазоне парения в пределах radius
rest_point(center, radius)point/nilМесто для отдыха в его среде: диапазон парения для летающего, твёрдая земля для наземного или неподвижного тела, вода для плавающего (на высоте center, когда center в воде, иначе на 0.5–2 блока под поверхностью), а для земноводного — земля или, если её нет, вода
ground_point(center, radius)point/nilМесто, где можно стоять, примерно от 5 блоков ниже до 4 выше center. nil над жидкостью или у большого обрыва
water_point(center, radius, min_depth, max_depth)point/nilМесто для плавания между двумя глубинами под поверхностью (от 0 до 16)
ground_below(point, depth)number/nilВысота верха земли в пределах depth блоков вниз (от 1 до 64). Поверхность воды или лавы считается землёй, если вид не AQUATIC. Если земли в пределах досягаемости нет — высота точки минус depth
trail_point(back, height)point/nilНа back блоков позади того, куда смотрит владелец, на высоте height над его ногами для вида FLYING и на уровне его ног в остальных случаях. nil, когда владелец отсутствует или в другом мире
distance_to(point)numberБлоков от его тела
route_target()point/nilКуда его миграционный маршрут хочет привести его на этом тике. nil, когда он не мигрирует или маршрут закончился
find_perch(height, radius)point/nilНа height блоков (от 0 до 8) выше ближайшей листвы под ним, в пределах radius блоков (от 0 до 4) в сторону, либо над землёй прямо под ним. Питомец с владельцем получает присаду только в пределах follow.distance от владельца
nearest_campfire(radius, keep_ticks, blocks, soul_variants, height)point/nilБлок ближайшего горящего костра, который он может использовать, в пределах radius (от 1 до 32). blocks перечисляет виды костров, которые он принимает: CAMPFIRE, SOUL_CAMPFIRE или оба, а список, не называющий ни одного, принимает оба; soul_variants перечисляет модели, которые принимают только костры душ, а все остальные модели тогда принимают только обычные. Питомец с владельцем берёт только такой костёр, место которого на height выше находится в пределах follow.distance минус 1 от владельца. Результат поиска по каждому чанку хранится keep_ticks тиков (от 20 до 1200) и является общим для всех питомцев
campfire_usable(fire, blocks, soul_variants, height)boolГорит ли ещё найденный ранее костёр и подходит ли он по-прежнему

Эффекты​

Работают из любого коллбэка; обычно их вызывают из сенсоров.

МетодВозвращаетЧто делает
set_glow(light, lantern_bone, animation)nilОсвещает кости отображения модели уровнем light (от 0 до 15), для игроков Java. Модель с костью по имени lantern_bone ещё и зацикливает animation. Последние два необязательны
clear_glow()nilВозвращает свет мира и завершает эту анимацию
scale_toward(target, span, ticks, elapsed)nilПлавно приближает размер тела к target, умноженному на базовый размер. Полное изменение от 1 до span занимает ticks (не менее 2); elapsed — тики с прошлого вызова. Множители от 0.1 до 16
sting_contacts(on, effect, duration, amplifier, damage, cooldown)numberЖалит игроков, чьё касание достигло модели дикого питомца за последнюю секунду, но никогда не жалит игрока в творческом режиме или режиме наблюдателя, и возвращает, сколько их было. Очищает касания, даже когда on равно false. Питомцы с владельцем касаний не записывают
sting_enemies(reach, effect, duration, amplifier, damage, cooldown)numberЖалит враждебных мобов в пределах reach блоков (от 0 до 4) от хитбокса модели, но никогда игроков или питомцев, и возвращает, сколько их было
drizzle(depth, chance)boolКогда питомец неподвижно парит над культурой в пределах depth блоков (от 1 до 8), появляются водяные частицы и с вероятностью chance (от 0 до 1) культура вырастает на одну стадию. true, когда он над культурой
owner_effect(effect, duration, amplifier, range)boolДаёт владельцу эффект зелья на duration тиков (от 1 до 6000), когда он в пределах range блоков, без частиц. Эффект, который у владельца уже есть и который не слабее и длится не меньше, остаётся нетронутым. false, когда владелец отсутствует, в другом мире или вне радиуса либо такого эффекта не существует
play_animation(name)boolОдин раз проигрывает одну из анимаций модели. false, когда у модели её нет

Для обоих видов укола effect — название эффекта зелья или NONE, duration — в тиках (от 1 до 6000), amplifier 0 — уровень I (до 255), damage — в половинках сердца, а cooldown — тики (не менее 1) до того, как этот питомец сможет ужалить ту же жертву снова. Укол показывает искры и проигрывает звук жала пчелы, а взрослый проигрывает свою анимацию attack. О названии эффекта зелья, которого не существует, предупреждают один раз для каждого вида и названия, и ничего не применяется.

Поведения и сенсоры​

  • Поведение двигает или поворачивает питомца. Оно перечисляет нужные ему элементы управления, ai.controls.move и ai.controls.look для вызовов движения c.pet, и каждый элемент управления одновременно удерживает только одно поведение. Пока оно остановлено, MagmaCore спрашивает его can_start. Когда он возвращает true и элементы управления свободны, оно берёт их и выполняет start, затем tick каждый тик, пока can_continue возвращает true (без can_continue решает can_start), затем stop. См. Жизненный цикл.
  • Меньшее число priority идёт первым. Поведение забирает элементы управления у работающего с большим числом, и тогда выполняется его stop. Число 12 из примера оставляет следование и «оставаться на месте» приоритетнее самого модуля, а его самого ставит приоритетнее дрейфа и парения, но и приоритетнее campfire, поэтому питомец, перечисляющий оба, ночью никогда не навещает костёр. При числе выше 15 костёр победил бы.
  • Поведение конкурирует только за те элементы управления, которые перечисляет. idle_look берёт только look, поэтому работает, пока ни одно поведение не удерживает взгляд; каждое поставляемое поведение с движением берёт и look, поэтому движение всегда побеждает его.
  • Файл питомца не может менять приоритет. Чтобы изменить порядок, скопируйте модуль и поменяйте число.
  • probe_interval необязателен, в тиках: пока поведение остановлено, can_start спрашивается лишь раз в столько тиков, на собственной фазе каждого питомца. Это экономит время сервера, когда выведено много питомцев, ценой того, что поведение может запуститься с опозданием до этого числа тиков. По умолчанию 1, а выше 200 считается равным 200. Работающее поведение по-прежнему выполняется каждый тик.
  • Сенсор никогда не двигает питомца. Он выполняет sense на первом тике, когда обрабатывается тело питомца, затем каждые interval тиков на собственной фазе питомца, что бы ни делали поведения. Свечение, набухание, уколы, полив и эффекты владельца — сенсоры.

Поставляемые модули используют:

МодульПриоритетprobe_intervalinterval сенсора
migrate110
stay54
follow_owner104
campfire1510
day_perch1810
rain_bloom19104 (набухание)
drift, wander, swim2010
hover304
idle_look3010
night_glow40
sting4 (игроки), 10 (мобы)
crop_drizzle20
owner_effect20

Правила​

  • Вызывайте steer_to и stop только из start, tick или stop поведения, в поведении, которое перечисляет нужные им элементы управления.
  • Храните состояние каждого питомца в c.memory, объявленном в memories, с именами, начинающимися с имени модуля.
  • Не записывайте в c.pet; присваивание ему вызывает ошибку.
  • Делайте коллбэки короткими. Каждый питомец работает в этих фиксированных лимитах, которые модули и файлы питомцев изменить не могут:
ЛимитЗначение
Один коллбэк1.5 мс
Коллбэки одного питомца за тик3 мс, не более 32 коллбэков
Коллбэки всех питомцев за тик10 мс
Процессорное время одного коллбэка, жёсткий лимит20 мс
Инструкции Lua одного коллбэка, жёсткий лимит20 000

Когда питомец достигает одного из первых трёх лимитов, его оставшиеся коллбэки ждут следующего тика. Коллбэк, превысивший жёсткий лимит, останавливается и завершается сбоем, как при ошибке. См. Бюджеты.

Когда что-то не так​

Ничего из этого не останавливает сервер или других существ. Чтобы найти эти предупреждения, ищите в консоли pet behavior, pet_behaviors и .pet.yml.

  • Модуль не загружается. Модуль, который не компилируется, id которого не совпадает с именем файла или в имени файла которого есть другие символы, пропускается с предупреждением, называющим файл, а для ошибки Lua — и строку. Все остальные модули всё равно загружаются.
  • Файл питомца называет модуль, которого нет. Предупреждение называет файл питомца и предлагает ближайшее имя модуля, если он отличается на одну-две буквы. Существо загружается без него. Если модуль существует, но не загрузился, предупреждение указывает на предупреждение о его файле.
  • Неверная настройка. Настройка, которой у модуля нет, или значение неверного типа сообщаются с именем файла питомца, и значение по умолчанию остаётся. См. Значения настроек.
  • Два модуля конфликтуют. Когда два модуля на одном существе объявляют одно и то же имя памяти, id поведения или id сенсора, существо оставляет первый в списке и работает без другого, с предупреждением.
  • Коллбэк даёт сбой, пока питомцы работают. Это записывается один раз для программы, коллбэка и вида сбоя до следующей перезагрузки. Программы называются fmm:pet/creature/<species>/<variant>, а коллбэки — по id их поведения или сенсора, например fmm:pet/drift.tick. Поведение со сбоем останавливается или не запускается и пробуется снова со следующего тика; пока оно продолжает давать сбой, поведения с большими числами приоритета, которые двигают или поворачивают питомца, могут не выполняться. Сенсор со сбоем выполняется снова на своём следующем интервале.
  • Вызов c.pet даёт сбой внутри FreeMinecraftModels. Причина записывается один раз для каждого вида и вызова, с трассировкой стека, а коллбэк завершается сбоем с c.pet:<method> failed; the server log has the cause.
  • Указанного названия эффекта зелья не существует. О нём предупреждают один раз для каждого вида и названия, и вызов не применяет никакого эффекта.

После исправления выполните /fmm reload.