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

Lua-скрипты: API реквизита и предметов

На этой странице описаны все API, доступные скриптам реквизита и предметов FreeMinecraftModels: context.prop, context.item, context.event, context.player, context.world, context.zones, context.scheduler, context.state и context.log. Если вы только начинаете писать скрипты, сначала прочтите Начало работы.


context.prop

Таблица реквизита предоставляет информацию о сущности реквизита и методы управления её анимациями. Она пересобирается заново для каждого вызова хука.

Поля

ПолеТипЗаметки
prop.model_idstringИмя модели-шаблона (например, "torch_01")
prop.current_locationтаблица локацииПозиция реквизита на момент сборки контекста

Таблица локации имеет стандартные поля: x, y, z, world, yaw, pitch.

Пример: чтение данных реквизита
return {
api_version = 1,

on_spawn = function(context)
context.log:info("Prop spawned: " .. (context.prop.model_id or "unknown"))
local loc = context.prop.current_location
if loc then
context.log:info("Location: " .. loc.x .. ", " .. loc.y .. ", " .. loc.z)
end
end
}

prop:play_animation(name, blend, loop)

Проигрывает названную анимацию на модели реквизита.

ПараметрТипПо умолчаниюЗаметки
namestringобязательныйИмя анимации, заданное в файле модели
blendbooleantrueСмешивать ли с текущей анимацией
loopbooleantrueЗацикливать ли анимацию

Возвращает true, если анимация найдена и запущена, иначе false.

Пример
return {
api_version = 1,

on_right_click = function(context)
local success = context.prop:play_animation("open", true, false)
if not success then
context.log:warn("Animation 'open' not found on this model!")
end
end
}

prop:stop_animation()

Останавливает все воспроизводимые в данный момент анимации на реквизите.

Параметров не принимает.

Пример
return {
api_version = 1,

on_right_click = function(context)
context.prop:stop_animation()
end
}

prop:hurt_visual()

Проигрывает визуальную анимацию урона (вспышка красным оттенком) на реквизите, не нанося ему реального урона.

Параметров не принимает.

Пример
return {
api_version = 1,

on_left_click = function(context)
-- Мигаем красным при ударе, но не получаем урон
if context.event then
context.event.cancel()
end
context.prop:hurt_visual()
end
}

prop:pickup()

Убирает реквизит из мира и роняет в его позиции бумажный предмет размещения. По дроп-предмету можно кликнуть правой кнопкой по блоку, чтобы снова разместить реквизит.

Параметров не принимает.

Пример
return {
api_version = 1,

on_right_click = function(context)
-- Позволяем игрокам подбирать реквизит правым кликом
context.prop:pickup()
end
}

prop:mount(player)

Сажает игрока на первое доступное сиденье в точке посадки реквизита. У модели должны быть определены кости точек посадки.

ПараметрТипЗаметки
playerтаблица сущностиТаблица сущности игрока (например, из context.event.player)
Пример
return {
api_version = 1,

on_right_click = function(context)
local player = context.event and context.event.player
if player then
context.prop:mount(player)
end
end
}

prop:dismount(player)

Снимает игрока с его сиденья в точке посадки на реквизите.

ПараметрТипЗаметки
playerтаблица сущностиТаблица сущности игрока
Пример
return {
api_version = 1,

on_right_click = function(context)
local player = context.event and context.event.player
if player then
-- Переключаем mount/dismount
local passengers = context.prop:get_passengers()
for i = 1, #passengers do
if passengers[i].uuid == player.uuid then
context.prop:dismount(player)
return
end
end
context.prop:mount(player)
end
end
}

prop:get_passengers()

Возвращает Lua-массив таблиц сущностей всех текущих пассажиров реквизита.

Параметров не принимает.

Пример
return {
api_version = 1,

on_game_tick = function(context)
local passengers = context.prop:get_passengers()
if #passengers > 0 then
context.log:info("Prop has " .. #passengers .. " passenger(s)")
end
end
}

prop:has_mount_points()

Возвращает, определены ли у этого реквизита кости точек посадки в его модели.

Параметров не принимает. Возвращает true или false.

Пример
return {
api_version = 1,

on_right_click = function(context)
local player = context.event and context.event.player
if player and context.prop:has_mount_points() then
context.prop:mount(player)
end
end
}

prop:spawn_elitemobs_boss(filename, x, y, z)

Спавнит пользовательского босса EliteMobs в указанной позиции. Требует, чтобы EliteMobs был установлен на сервере.

ПараметрТипЗаметки
filenamestringИмя файла пользовательского босса (например, "my_boss.yml")
xnumberКоордината X
ynumberКоордината Y
znumberКоордината Z

Возвращает таблицу живой сущности для заспавненного босса или nil, если EliteMobs не установлен либо файл босса не существует.

Пример
return {
api_version = 1,

on_right_click = function(context)
local loc = context.prop.current_location
if loc then
local boss = context.prop:spawn_elitemobs_boss("dungeon_guardian.yml", loc.x, loc.y + 1, loc.z)
if boss then
context.log:info("Spawned boss: " .. (boss.name or "unknown"))
else
context.log:warn("Could not spawn boss -- is EliteMobs installed?")
end
end
end
}

prop:open_inventory(player, title, rows)

Открывает игроку постоянный GUI-инвентарь типа сундука. Содержимое сохраняется в PersistentDataContainer реквизита при закрытии инвентаря и восстанавливается при следующем открытии.

ПараметрТипПо умолчаниюЗаметки
playerтаблица сущностиобязательныйИгрок, которому показать инвентарь
titlestringобязательныйЗаголовок инвентаря (поддерживает цветовые коды &)
rowsint3Число рядов (1–6, где 6 = 54 слота = двойной сундук)

Возвращает true, если инвентарь открылся, иначе false.


prop:is_viewing_inventory(player)

Возвращает, открыт ли у указанного игрока сейчас инвентарь этого реквизита.

ПараметрТипЗаметки
playerтаблица сущностиИгрок для проверки

Возвращает true или false.

Пример: анимация закрытия при закрытии инвентаря
context.state["task_" .. player.uuid] = context.scheduler:run_repeating(5, 5, function(tick_context)
if not tick_context.prop:is_viewing_inventory(player) then
tick_context.prop:play_animation("close", true, false)
tick_context.scheduler:cancel(tick_context.state["task_" .. player.uuid])
end
end)

prop:place_book(player)

Забирает у игрока книгу для письма или подписанную книгу из основной руки и кладёт её на реквизит.

ПараметрТипЗаметки
playerтаблица сущностиИгрок, держащий книгу

Возвращает true при успехе.


prop:read_book(player)

Открывает игроку сохранённую книгу для чтения.

ПараметрТипЗаметки
playerтаблица сущностиИгрок, которому показать книгу

Возвращает true, если книга была открыта.


prop:take_book(player)

Возвращает сохранённую книгу в инвентарь игрока и удаляет её с реквизита.

ПараметрТипЗаметки
playerтаблица сущностиИгрок, которому выдать книгу

Возвращает true при успехе.


prop:has_book()

Возвращает, хранится ли на этом реквизите книга. Параметров не принимает.


prop:drop_inventory()

Роняет всё сохранённое содержимое инвентаря в позиции реквизита в виде предметов-сущностей, затем очищает сохранённые данные. Автоматически закрывает инвентарь у всех игроков, которые его сейчас смотрят.

Параметров не принимает. Возвращает true при успехе.


prop:drop_book()

Роняет сохранённую книгу в позиции реквизита как предмет-сущность и очищает сохранённые данные книги.

Параметров не принимает. Возвращает true при успехе.


prop:set_persistent_data(key, value)

Сохраняет строковое значение в PersistentDataContainer армор-стенда реквизита. Эти данные переживают перезапуски сервера и выгрузки чанков.

ПараметрТипЗаметки
keystringУникальное имя ключа (внутри хранится под fmm_lua_<key>)
valuestringЗначение для сохранения. Для чисел и булевых используйте tostring().

Возвращает true при успехе, false, если у реквизита нет базового армор-стенда.


prop:get_persistent_data(key)

Извлекает строковое значение, ранее сохранённое через set_persistent_data. Возвращает nil, если ключ не был установлен.

ПараметрТипЗаметки
keystringИмя ключа, использованное в set_persistent_data
Пример: постоянное состояние переключателя
return {
api_version = 1,

on_spawn = function(context)
local saved = context.prop:get_persistent_data("active")
context.state.active = saved == "true"
end,

on_right_click = function(context)
context.state.active = not context.state.active
context.prop:set_persistent_data("active", tostring(context.state.active))
end
}

context.item

Таблица предмета доступна только в скриптах предметов (не в скриптах реквизита). Она предоставляет информацию о пользовательском предмете и методы для манипуляций с ним. Таблица пересобирается заново для каждого вызова хука.

Поля

ПолеТипЗаметки
item.idstringID типа предмета (fmm_item_id из YML-конфига)

item:material()

Возвращает имя материала предмета в виде строки (например, "DIAMOND_SWORD", "STICK").


item:get_amount() / item:set_amount(n)

Получает или задаёт размер стака предмета.

ПараметрТипЗаметки
nintНовое количество в стаке

item:consume(n)

Уменьшает количество в стаке на n (по умолчанию 1). Если итоговое количество 0 или меньше, предмет удаляется из инвентаря игрока.

ПараметрТипПо умолчаниюЗаметки
nint1Сколько потреблять

item:get_uses() / item:set_uses(n)

Получает или задаёт пользовательский счётчик использований, хранящийся в PersistentDataContainer предмета. Он не зависит от ванильной прочности и подходит для реализации собственной прочности или системы зарядов.

ПараметрТипЗаметки
nintНовое число использований

item:get_name() / item:set_name(s)

Получает или задаёт отображаемое имя предмета. Поддерживает цветовые коды через &.

ПараметрТипЗаметки
sstringНовое отображаемое имя (например, "&b&lFrost Sword")

item:get_lore() / item:set_lore(table)

Получает или задаёт lore предмета. get_lore() возвращает таблицу строк (по одной на строку). set_lore() принимает таблицу строк.

ПараметрТипЗаметки
tabletableМассив строк, по одной на строку lore
Пример: скрипт предмета, отслеживающий использования
return {
api_version = 1,

on_right_click = function(context)
local uses = context.item:get_uses()
if uses <= 0 then
context.player:send_message("&cThis item is out of charges!")
return
end
context.item:set_uses(uses - 1)
context.player:send_message("&aUsed! Charges remaining: " .. (uses - 1))
end
}

item:get_durability()

Возвращает таблицу с полями current и max, представляющими ванильную прочность предмета, либо nil, если у предмета нет полосы прочности.

Пример
local dur = context.item:get_durability()
if dur then
context.player:send_message("Durability: " .. dur.current .. "/" .. dur.max)
end

item:get_durability_percentage()

Возвращает оставшуюся прочность как долю от 0.0 до 1.0 или nil, если у предмета нет полосы прочности.


item:use_durability(amount, can_break)

Уменьшает ванильную прочность предмета на фиксированную величину.

ПараметрТипПо умолчаниюЗаметки
amountintобязательныйСколько единиц прочности потратить
can_breakbooleanfalseЕсли true, предмет уничтожается при обнулении прочности. Если false, прочность останавливается на 1.

item:use_durability_percentage(fraction, can_break)

Уменьшает ванильную прочность предмета на долю от максимума.

ПараметрТипПо умолчаниюЗаметки
fractionnumberобязательныйДоля максимальной прочности для траты (например, 0.1 = 10%)
can_breakbooleanfalseЕсли true, предмет уничтожается при обнулении прочности. Если false, прочность останавливается на 1.

context.event

Данные события для текущего хука. Доступны в хуках кликов, боя и взаимодействия для скриптов как реквизита, так и предметов. Возвращает nil в хуках без связанного события (on_spawn, on_game_tick, on_destroy, on_zone_enter, on_zone_leave, on_equip).

Поля и методы

Поле или методТипЗаметки
event.playerтаблица сущности игрокаИгрок, инициировавший событие. Доступен в on_left_click, on_right_click и on_projectile_hit (стрелявший, если это был игрок). См. Методы сущности игрока для всех полей и методов.
event.is_cancelledbooleanОтменено ли событие в данный момент
event.cancel()functionОтменяет событие (например, предотвращает урон или взаимодействие)
event.uncancel()functionСнимает отмену с ранее отменённого события
к сведению

Не все события можно отменить. Если базовое Bukkit-событие не реализует Cancellable, event.cancel() и event.uncancel() отсутствуют, а event.is_cancelled всегда будет false.

Пример: сделать реквизит неуязвимым

Пример
return {
api_version = 1,

on_left_click = function(context)
if context.event then
context.event.cancel()
end
end
}

Пример: проверка состояния отмены

Пример
return {
api_version = 1,

on_left_click = function(context)
if context.event and not context.event.is_cancelled then
context.event.cancel()
context.log:info("Damage cancelled!")
end
end
}
предупреждение

Внутри запланированных колбэков (scheduler:run_later, scheduler:run_repeating) context.event всегда nil. Изменение события возможно только во время самого хука события.


context.world

API мира общий для всех плагинов Nightbreak. Полную справку см. в context.world.

к сведению

Реквизит FMM использует ту же таблицу мира MagmaCore, что и боссы EliteMobs. Все методы, описанные на общей странице (get_block_at, set_block_at, spawn_particle, play_sound, strike_lightning, get_time, set_time, get_nearby_entities, get_nearby_players, spawn_entity, get_highest_block_y, raycast, spawn_firework), доступны в FMM. См. MagmaCore world API для полных подробностей по world:raycast() (бросить луч и определить попадание по сущностям/блокам) и world:spawn_firework() (запуск ракет фейерверков с пользовательскими цветами и формами). EliteMobs расширяет таблицу мира дополнительными методами для спавна боссов, подкреплений и прочего — см. EliteMobs World & Environment.


Методы сущности игрока

Таблицы сущности игрока возвращаются из context.event.player, context.world:get_nearby_players() и колбэков зон.

Общий API

Таблицы сущностей, методы живых сущностей, специфичные для игрока методы и методы Player UI общие для всех плагинов Nightbreak. См. MagmaCore Lua Scripting Engine для полной справки по базовым полям сущности, полям и методам живых сущностей, специфичным для игрока полям и методам и методам Player UI. Новые методы игрока включают player:get_target_entity() (наведение через raycast), player:get_eye_location(), player:get_look_direction(), player:send_block_change() (фейковые блоки для конкретного игрока) и player:reset_block() — подробности см. в Player-Specific Methods.

Поля сущностей, специфичные для FMM

Каждая таблица сущности, построенная внутри FMM-скрипта, автоматически получает эти дополнительные поля (через LuaEntityEnricher FMM):

ПолеТипЗаметки
entity.is_modeledbooleantrue, если эта Bukkit-сущность является базовой сущностью какого-то ModeledEntity
entity.is_propbooleantrue, если эта сущность — армор-стенд, лежащий в основе PropEntity
entity.modelтаблица или nilЗаполнено только когда is_modeled = true (см. ниже)

Когда entity.model присутствует, оно раскрывает:

Поле / методЗаметки
model.model_idИмя модели-шаблона (например, "dragon")
model.is_dynamictrue, если это DynamicEntity (привязанный к живой сущности)
model:play_animation(name, blend, loop)Проигрывает названную анимацию. Возвращает true при успехе
model:stop_animations()Останавливает все текущие анимации
model:remove()Немедленно удаляет моделированную сущность и все её кости
on_attack_entity = function(context)
local target = context.event.target
if target and target.is_modeled then
target.model:play_animation("hurt", true, false)
end
end

Поля сущностей EliteMobs

Когда установлен EliteMobs, FMM пробрасывает вызов к enricher-у EliteMobs, поэтому те же таблицы сущностей дополнительно раскрывают:

ПолеТипЗаметки
entity.is_elitebooleantrue, если сущность отслеживается EliteMobs
entity.is_custom_bossbooleantrue, если это конфигурация пользовательского босса
entity.is_significant_bossbooleantrue для пользовательских боссов с healthMultiplier > 1 (отсеивает «мусорных» именованных мобов)
entity.eliteтаблица или nilЗаполнено только когда is_elite = true. Содержит level, name, health, max_health, health_multiplier, damage_multiplier, is_custom_boss, плюс elite:remove()

context.zones

API зон общий для всех плагинов Nightbreak. Полную справку см. в context.zones.


context.scheduler

API планировщика общий для всех плагинов Nightbreak. Полную справку см. в context.scheduler.


context.state

API состояния общий для всех плагинов Nightbreak. Полную справку см. в context.state.


context.log

API логирования общий для всех плагинов Nightbreak. Полную справку см. в context.log.


Модель времени выполнения

Один runtime на один экземпляр скрипта

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

Для скриптов предметов один runtime создаётся на пару (игрок, itemId). Когда игрок экипирует пользовательский предмет, FMM создаёт экземпляр скрипта для этого игрока и типа предмета. Когда предмет снимают, runtime завершается.

Это значит:

  • Локальные переменные, объявленные на уровне файла, приватны для данного экземпляра скрипта.
  • context.state полностью изолирован между экземплярами, даже если они используют один и тот же файл скрипта.

Владение запланированными задачами

Все задачи, созданные через context.scheduler, принадлежат runtime, который их создал. Когда реквизит удаляется:

  1. Runtime завершается.
  2. Все принадлежащие ему задачи — одноразовые и повторяющиеся — автоматически отменяются.
  3. Все наблюдения за зонами сбрасываются.

Область видимости cooldown-ов

Общий движок скриптов предоставляет context.cooldowns:set_local, :check_local, :set_global и :check_global (см. справку по cooldown-ам MagmaCore). FMM ограничивает их области так:

Тип скриптаОбласть локального хранилищаОбласть глобального хранилища
Скрипт реквизитаНа ScriptInstance (реквизит + файл скрипта)На PropEntity (общее для всех скриптов, прицепленных к этому реквизиту)
Скрипт предметаНа тройку (player, itemId, scriptFile) — сохраняется между переэкипировками, хотя экземпляр скрипта разрушается каждый раз при выходе предмета из активного слотаНа игрока (общее для всех FMM-скриптов предметов, которые запускает этот игрок)

Поскольку скрипты предметов разрушаются и пересобираются на каждом цикле equip/unequip, cooldown-ы предметов хранятся в статической мапе по UUID игрока, а не на ScriptInstance. Именно поэтому cooldown предмета продолжает действовать, даже если вы поменяли предмет местами в хотбаре и вернули обратно.

Бюджет выполнения

Каждый вызов хука и каждый вызов колбэка хронометрируется. Если один вызов занимает больше 50 миллисекунд, скрипт отключается с предупреждением в консоли:

[Lua] my_script.lua took 73ms in 'on_game_tick' (limit: 50ms) -- script disabled to prevent lag.

Чтобы укладываться в бюджет:

  • Избегайте безграничных циклов внутри хуков.
  • Держите обработчики on_game_tick лёгкими — они выполняются каждый тик.
  • Используйте context.scheduler:run_repeating(...), чтобы размазать работу по тикам.

Полная справка по хукам

В этой таблице перечислены все хуки, доступные в скриптах реквизита и предметов.

Хуки реквизита (8)

ХукКогда срабатываетcontext.event
on_spawnРеквизит появляется в миреnil
on_game_tickКаждый серверный тик (50 мс)nil
on_destroyРеквизит удалёнnil
on_left_clickИгрок кликает по реквизиту левой кнопкойсобытие урона
on_right_clickИгрок кликает по реквизиту правой кнопкойсобытие взаимодействия
on_projectile_hitСнаряд попадает в реквизитсобытие попадания снаряда
on_zone_enterИгрок входит в наблюдаемую зонуnil
on_zone_leaveИгрок покидает наблюдаемую зонуnil

Хуки предметов (22)

ХукКатегорияКогда срабатываетcontext.event
on_attack_entityБойИгрок атакует сущностьсобытие урона
on_kill_entityБойИгрок убивает сущностьсобытие смерти
on_take_damageБойИгрок получает уронсобытие урона
on_shield_blockБойИгрок блокирует щитомсобытие урона
on_shoot_bowБойИгрок стреляет из лукасобытие выстрела из лука
on_projectile_hitБойСнаряд игрока попадаетсобытие попадания снаряда
on_projectile_launchБойИгрок выпускает снарядсобытие запуска
on_right_clickВзаимодействиеИгрок кликает правой кнопкойсобытие interact
on_left_clickВзаимодействиеИгрок кликает левой кнопкойсобытие interact
on_shift_right_clickВзаимодействиеИгрок делает shift+правый кликсобытие interact
on_shift_left_clickВзаимодействиеИгрок делает shift+левый кликсобытие interact
on_interact_entityВзаимодействиеИгрок кликает правой по сущностисобытие entity interact
on_equipЭкипировкаПредмет попадает в активный слотnil
on_unequipЭкипировкаПредмет покидает активный слотnil
on_swap_handsЭкипировкаМеняет основную/off-рукусобытие swap
on_dropЭкипировкаИгрок выбрасывает предметсобытие drop
on_break_blockУтилитыИгрок ломает блоксобытие block break
on_consumeУтилитыИгрок употребляет предметсобытие consume
on_item_damageУтилитыПредмет теряет прочностьсобытие item damage
on_fishУтилитыИгрок использует удочкусобытие fish
on_deathУтилитыИгрок умирает с экипированным предметомсобытие смерти
on_game_tickЖизненный циклКаждый тик, пока предмет экипированnil

Дальнейшие шаги