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

Lua-скриптинг: Начало работы

webapp_banner.jpg

Что вы узнаете

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

Освоив основы, переходите к сопутствующим страницам:

Экспериментальная функция

Файлы Lua-пауэров в настоящее время экспериментальны. Имена хуков, вспомогательные методы и поведение могут измениться по мере развития EliteMobs, поэтому тщательно тестируйте перед использованием на продакшн-сервере.

Связь с EliteScript

Lua не заменяет существующую YAML-систему eliteScript:.

  • Используйте EliteScript, когда вам нужен декларативный YAML-скриптинг с существующими страницами Действий, Целей, Зон, Условий, Кулдаунов и Относительных векторов.
  • Используйте файлы Lua-пауэров, когда нужны переменные, циклы, случайный выбор, многоразовые вспомогательные функции, персистентное состояние для каждого босса и более традиционный программный поток.

У Lua есть собственная система нацеливания и зон, использующая те же знакомые имена enum и концепции из EliteScript. API Script Utilities (context.script) позволяет создавать зоны, цели и относительные векторы с теми же именами полей, задокументированных на страницах EliteScript.


Что такое Lua-пауэры

Lua-пауэры -- это автономные файлы .lua, расположенные в обычном дереве каталогов powers EliteMobs и ссылающиеся точно так же, как обычные файлы пауэров.

В чем Lua-пауэры хороши

Lua-пауэры отлично подходят, когда вам нужны:

  • Ротации атак и случайный выбор приемов
  • Персистентное состояние между хуками с context.state
  • Отложенные и повторяющиеся действия без построения всего из YAML-ожиданий
  • Пользовательские вспомогательные функции внутри одного файла
  • Сложное ветвление, которое было бы неудобно в чистом EliteScript
  • Логика босса, которая хочет переиспользовать определения целей и зон в стиле EliteScript

Если ваш пауэр в основном "сработало событие, выполнить пару скриптовых действий", существующие документы EliteScript по-прежнему самый быстрый и понятный способ. Если пауэру нужен настоящий программный поток, Lua -- инструмент для этого.


Для кого эта страница

Эта страница написана для трех типов читателей:

  • Тех, кто уже знает EliteScript и хочет изучить Lua, не изучая "настоящее программирование" сразу целиком
  • Тех, кто новичок в скриптинге EliteMobs и нуждается в полном справочнике с точными именами
  • Тех, кто использует ИИ для создания пауэров и нуждается в достаточной детализации, чтобы понять, когда ИИ выдумал что-то ложное

Вам не нужно становиться полноценным Lua-разработчиком, чтобы писать полезные пауэры. Для большинства практических пауэров EliteMobs вам нужно только:

  • Как разместить допустимый хук в возвращаемой таблице
  • Как читать значения из context
  • Как досрочно выйти с помощью if ... then return end
  • Как правильно вызвать несколько вспомогательных методов
  • Как скопировать правильные спецификации целей и зон из существующих документов EliteScript

Ментальная модель: EliteScript vs. Lua

Если вы знаете EliteScript, это сравнение -- самый быстрый путь к пониманию Lua-пауэров:

Если вы мыслите в терминах EliteScriptВ Lua это обычно означает
EventsИмена хуков, такие как on_spawn или on_boss_damaged_by_player
Cooldownscontext.cooldowns (см. context.cooldowns ниже)
ActionsПрямые вызовы методов, например context.world:spawn_particle_at_location(...) или context.script:damage(...)
Targetscontext.script:target({...}) -- использует имена полей EliteScript Target
Zonescontext.script:zone({...}) или нативные хелперы context.zones -- см. Зоны и нацеливание
Relative vectorscontext.script:relative_vector({...}) или context.vectors
Script flowВаши собственные if в Lua, вспомогательные функции, таймеры и состояние

Главное отличие:

  • EliteScript описывает в YAML, что должно произойти.
  • Lua позволяет вам решать с помощью кода, когда, почему и какая ветвь должна выполняться.

Если EliteScript ощущается как "конфигурация", то Lua ощущается как "конфигурация плюс принятие решений".


Краткий курс Lua для авторов EliteMobs

Это минимальный синтаксис Lua, который нужен большинству авторов пауэров.

Переменные

Используйте local для сохранения значения:

local cooldown_key = "fire_burst"
local damage_multiplier = 1.5

local означает, что переменная принадлежит только этому файлу или блоку.

Функции

Функции -- это многоразовые блоки логики:

local function warn_player(player)
player:send_message("&cMove!")
end

Позже вы можете вызвать её:

warn_player(context.player)

Проверки if

Используйте if, когда что-то должно происходить только иногда:

if context.player == nil then
return
end

Это означает: "если для этого хука нет игрока, остановиться здесь".

nil

nil означает "нет значения". Это Lua-версия "здесь ничего нет".

Вы часто будете проверять nil:

if context.event ~= nil then
-- сделать что-то с событием
end

~= означает "не равно".

Таблицы

Lua использует таблицы для нескольких задач:

  • Списки
  • Объекты с именованными ключами
  • Финальное определение пауэра

Пример таблицы с именованными ключами:

local particle = {
particle = "FLAME",
amount = 1,
speed = 0.05
}

Возврат определения пауэра

В конце файла вы возвращаете одну таблицу:

return {
api_version = 1,

on_spawn = function(context)
end
}

Эта возвращенная таблица и есть файл пауэра.

Комментарии

Используйте -- для написания заметки:

-- Этот кулдаун не дает атаке срабатывать при каждом попадании
context.cooldowns:set_local(60, "fire_burst")

Ваш первый работающий пауэр, шаг за шагом

Если вы совершенно новичок, это лучшая прогрессия для "первой победы".

Перед шагом 1: Сохраните и прикрепите к боссу

Сохраните Lua-файл в обычную папку пауэров EliteMobs:

plugins/
EliteMobs/
powers/
first_test.lua

Затем добавьте имя файла в конфиг босса, используя обычный список powers::

powers:
- first_test.lua

Полный цикл для новичка:

  1. Сохранить файл в plugins/EliteMobs/powers/
  2. Добавить имя файла .lua в список powers: босса
  3. Заспавнить или перезагрузить босса
  4. Протестировать хук, который вы сейчас строите

Если нужна дополнительная информация о файлах боссов, списках пауэров или структуре кастомных боссов, см. Создание кастомных боссов.

Шаг 1: Заставить файл загрузиться

return {
api_version = 1,

on_spawn = function(context)
end
}

Если это загрузилось без ошибок, вы уже доказали:

  • Файл является валидным Lua
  • EliteMobs нашел его
  • Структура возвращаемой таблицы корректна
  • on_spawn -- допустимое имя хука

Шаг 2: Заставить босса сделать что-то видимое

return {
api_version = 1,

on_spawn = function(context)
context.boss:play_sound_at_self("entity.blaze.ambient", 1.0, 1.0)
end
}

Теперь у вас есть самое важное доказательство для новичка: ваш хук срабатывает.

Шаг 3: Отреагировать на удар игрока

return {
api_version = 1,

on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end

context.player:send_message("&eYou hit the boss.")
end
}

Это учит трем ключевым идеям:

  • on_boss_damaged_by_player -- это имя хука
  • context.player -- это игрок, участвующий в хуке
  • return досрочно выходит, когда нужные данные отсутствуют

Шаг 4: Предотвратить спам с помощью кулдауна

return {
api_version = 1,

on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end

if not context.cooldowns:local_ready("hello_message") then
return
end

context.player:send_message("&eYou woke up the boss.")
context.cooldowns:set_local(60, "hello_message")
end
}

Это первый по-настоящему полезный паттерн, который нужен большинству авторов. Если вы поняли этот паттерн, вы уже можете создавать много практических пауэров.

Шаг 5: Добавить реальный эффект

return {
api_version = 1,

on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end

if not context.cooldowns:local_ready("shock") then
return
end

context.player:send_message("&cStatic jumps from the boss into your armor!")
context.world:strike_lightning_at_location(context.player.current_location)
context.cooldowns:set_local(100, "shock")
end
}

На этом этапе вы уже пишете настоящий пауэр.


Первый реальный рабочий процесс

При создании нового Lua-пауэра используйте такой порядок:

  1. Создать файл и заставить on_spawn работать.
  2. Переключиться на нужный хук.
  3. Убедиться, что хук содержит ожидаемые данные, такие как context.player или context.event.
  4. Сначала добавить сообщение или звук, до урона или частиц.
  5. Добавить кулдауны.
  6. Добавить один игровой эффект.
  7. Только после этого добавлять хелперы, состояние, логику планировщика или зоны.

Такой порядок значительно упрощает отладку, потому что за раз меняется только одна вещь.


Где хранятся Lua-файлы

Размещайте файлы .lua в том же дереве папок, что и обычные файлы пауэров .yml:

plugins/
EliteMobs/
powers/
mycoolpower.lua
attack_push.yml
subfolder/
myotherpower.lua

Lua-пауэры автоматически обнаруживаются из каталогов пауэров, которые EliteMobs уже загружает.

Как боссы ссылаются на Lua-пауэры

Файлы боссов по-прежнему используют обычный список powers::

powers:
- attack_push.yml
- mycoolpower.lua

Специальных полей не требуется. Lua-пауэры не загружаются через eliteScript:.

Правила именования файлов

  • Боссы ссылаются на Lua-пауэры по имени файла, а не по пути к папке.
  • EliteMobs в настоящее время регистрирует обнаруженные Lua-пауэры только по базовому имени.
  • Избегайте дублирования имен, например powers/fire.lua и powers/bosses/fire.lua, так как один может перезаписать другой при обнаружении.

Готовые Lua-пауэры

EliteMobs поставляется с десятками готовых Lua-пауэров в папке powers, таких как attack_fire.lua, frost_cone.lua, meteor_shower.lua и многие другие. Это отличные справочные материалы для изучения -- просто откройте любой файл .lua в каталоге plugins/EliteMobs/powers/. Для обратной совместимости готовые пауэры также регистрируются под их устаревшими именами .yml.


Минимальный контракт файла

Каждый Lua-пауэр должен возвращать таблицу с помощью return. Эта таблица намеренно строгая.

Обязательные и необязательные поля верхнего уровня

ПолеОбязательноТипПримечания
api_versionДаNumberВ настоящее время должно быть 1
priorityНетNumberПриоритет выполнения. Меньшие значения выполняются первыми. По умолчанию 0
Поддерживаемые ключи хуковНетFunctionДолжен использовать одно из точных имен хуков, перечисленных в Справочнике хуков

Правила валидации

  • Файл должен возвращать таблицу.
  • api_version обязательно и в настоящее время должно быть 1.
  • priority должен быть числовым, если присутствует.
  • Каждый дополнительный ключ верхнего уровня должен быть поддерживаемым именем хука.
  • Каждый ключ хука должен указывать на функцию.
  • Неизвестные ключи верхнего уровня отклоняются.

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


Шаблоны для копирования

Минимальный валидный Lua-пауэр

return {
api_version = 1,

on_spawn = function(context)
end
}

Рекомендуемый стартовый шаблон

local ATTACK_COOLDOWN = "my_attack"

local function can_run_attack(context)
return context.cooldowns:local_ready(ATTACK_COOLDOWN)
and context.cooldowns:global_ready()
end

local function run_attack(context)
context.boss:play_sound_at_self("entity.blaze.shoot", 1.0, 1.0)
context.cooldowns:set_local(100, ATTACK_COOLDOWN)
context.cooldowns:set_global(20)
end

return {
api_version = 1,
priority = 0,

on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end

if not can_run_attack(context) then
return
end

run_attack(context)
end
}

Структура для больших файлов

local CONSTANT_NAME = "value"

local function helper_function(context)
end

local function another_helper(context, value)
end

return {
api_version = 1,
priority = 0,

on_spawn = function(context)
end,

on_enter_combat = function(context)
end,

on_boss_damaged_by_player = function(context)
end,

on_exit_combat = function(context)
end
}

Хуки

Хуки -- это специально именованные функции в таблице, которую вы возвращаете. EliteMobs вызывает их, когда что-то происходит -- босс спавнится, получает урон, вступает в бой и т.д. Вы уже видели on_spawn и on_boss_damaged_by_player в уроке выше.

Самые распространенные хуки для начала -- on_spawn, on_boss_damaged_by_player, on_enter_combat и on_exit_combat. Полный список всех хуков, доступные ключи контекста в каждом и порядок выполнения см. в Хуки и жизненный цикл.


Что такое context?

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

on_boss_damaged_by_player = function(context)
-- context.boss = босс, которого ударили
-- context.player = игрок, который ударил
-- context.world = инструменты для спавна частиц, звуков и т.д.
-- context.cooldowns = управление кулдаунами
-- context.state = ваше собственное постоянное хранилище
end

Вы не создаете context сами -- EliteMobs создает его и передает в ваш хук. Вы просто читаете из него и вызываете методы.

к сведению

context создается заново при каждом вызове хука, кроме context.state, который сохраняется на протяжении всей жизни босса. Это означает, что вы можете хранить данные в context.state и читать их позже в другом хуке.


Ключевые API context

Вот самые важные API, которые вы будете использовать из context:

  • context.state -- Простая Lua-таблица, сохраняющаяся на протяжении жизни босса. Используйте для хранения номеров фаз, ID задач, флагов и всего, что нужно помнить между хуками. Только context.state сохраняется -- все остальные таблицы контекста создаются заново при каждом вызове.

  • context.log -- Логирование в консоль с log:info(msg), log:warn(msg) и log:debug(msg). Незаменимо при разработке.

  • context.cooldowns -- Локальные кулдауны для каждого пауэра и глобальные для каждого босса. Ключевой метод -- cooldowns:check_local(key, ticks), который атомарно проверяет И устанавливает кулдаун. См. страницу Хуки и жизненный цикл для полного API кулдаунов.

  • context.scheduler -- Отложенные и повторяющиеся задачи с scheduler:run_after(ticks, callback) и scheduler:run_every(ticks, callback). Колбэки получают свежий context -- всегда используйте параметр колбэка, а не внешний context. Отменяйте повторяющиеся задачи в on_exit_combat. Подробности в Хуки и жизненный цикл.

  • context.boss / context.player -- Босс и игрок, участвующие в текущем событии. См. Босс и сущности для всех полей и методов.

  • context.world -- Спавн частиц, сущностей, звуков, молний, блоков. См. Мир и окружение.

  • context.zones / context.script -- Геометрия зон, нацеливание, урон, частицы. См. Зоны и нацеливание.


Синтаксис методов: : vs. .

В Lua object:method(arg) -- это сокращение для object.method(object, arg). API EliteMobs принимает обе формы, так что обе работают:

context.cooldowns:set_local(60, "test")
context.cooldowns.set_local(60, "test") -- то же самое

Во всей документации последовательно используется :.


Следующие шаги

Теперь, когда вы знаете основы, изучите остальную документацию по Lua-скриптингу:

Для YAML-скриптинга страницы EliteScript остаются каноническим справочником: