Модули поведения питомцев
Модуль поведения питомца — это 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 их два. Начните с того, который ближе всего к желаемому.
- Скопируйте его под новым именем, например
pet_behaviors/mine/slow_drift.luaизdrift.lua. - Измените его
idнаfmm:pet/slow_drift. - Переименуйте его памяти, поведения и сенсоры, чтобы они начинались с нового имени:
drift_toстановитсяslow_drift_toвmemoriesи в каждом вызовеc.memory, аid = 'fmm:pet/drift'у поведения становитсяfmm:pet/slow_drift. Два модуля на одном существе не могут иметь одно и то же имя памяти, id поведения или id сенсора; второй из перечисленных исключается с предупреждением. Переименование позволяет копии работать одновременно с оригиналом. - Укажите его в файле питомца вместо оригинала:
- slow_drift, а с настройками —- slow_drift: { speed: 0.03 }. - Выполните
/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.memory | get(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— исключение: он всегда читается заново. - Каждое чтение позиции даёт новую таблицу, которую модуль может изменять.
Свойства
| Имя | Тип | Значение |
|---|---|---|
wild | bool | true для дикого питомца, false для питомца с владельцем |
staying | bool | Ему велено оставаться на месте |
settled | bool | Не остаётся на месте и дикий либо находится в пределах follow.distance от владельца |
baby | bool | Детёныш |
migrating | bool | Летит по маршруту ночной миграции |
model | string | Модель его варианта. Детёныш в babyModel всё равно читает свой вариант |
position | point | Где находится его тело |
home | point/nil | Где живёт дикий питомец; nil для питомца с владельцем |
anchor | point/nil | Вокруг чего он бродит: вокруг владельца, а у дикого питомца вокруг дома или места, где он находится, если дома нет. nil, когда владелец не в сети или в другом мире |
stay_anchor | point/nil | Место, где ему велели оставаться; nil, когда он не остаётся на месте |
owner_location | point/nil | nil для дикого питомца или когда владелец не в сети, мёртв или в другом мире |
owner_distance | number | Блоков до владельца; math.huge, когда его нет в этом мире |
night, day | bool | day только при дневном свете в обычном мире; сумерки, рассвет, Незер и Энд считаются night |
raining | bool | На питомца падает дождь, проверяется не чаще чем раз в 10 тиков |
speed | number | Текущая скорость его тела в блоках за тик |
route_cruise | number | Крейсерская скорость его стаи на миграции, иначе 0 |
species | table | Числа из файла питомца, см. ниже |
c.pet.species содержит:
| Поле | Ключ файла питомца |
|---|---|
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 |
Движение
Вызывайте их только из 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_interval | interval сенсора |
|---|---|---|---|
migrate | 1 | 10 | |
stay | 5 | 4 | |
follow_owner | 10 | 4 | |
campfire | 15 | 10 | |
day_perch | 18 | 10 | |
rain_bloom | 19 | 10 | 4 (набухание) |
drift, wander, swim | 20 | 10 | |
hover | 30 | 4 | |
idle_look | 30 | 10 | |
night_glow | 40 | ||
sting | 4 (игроки), 10 (мобы) | ||
crop_drizzle | 20 | ||
owner_effect | 20 |
Правила
- Вызывайте
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.