Как пользоваться памяткой#
Сначала определите язык. Одинаковое намерение записывается по-разному в XPath, MD и Lua. Таблицы ниже - выбранные конструкции, нужные для чтения примеров этой вики и исходников проекта. Это не статистический рейтинг всех файлов X4 и не полный справочник.
Если термины пока незнакомы, начните со статьи как работает код X4. Все новые фрагменты здесь учебные; запуск в игровом мире для них не заявлен.
Четыре базовых понятия#
Переменная хранит значение: например, число товаров. Условие отвечает, выполнять ли действие. Функция принимает аргументы и выполняет работу; иногда возвращает результат. Обработчик события - функция или узел MD, который вызывается при определённом событии.
Различайте объявление, вызов и результат. В Lua local function getName() ... end объявляет функцию, getName() вызывает её, а local name = getName() сохраняет ответ.
XML: элементы, атрибуты и селекторы#
| Запись | Значение |
|---|---|
<hull max="1800" /> |
Элемент с атрибутом; у него нет вложенного содержимого |
<actions> ... </actions> |
Начало и конец контейнера |
<!-- пояснение --> |
Комментарий XML |
<replace sel="...">...</replace> |
Заменить выбранный узел или атрибут diff-патчем |
<add sel="...">...</add> |
Добавить содержимое в выбранное место по правилам diff |
<remove sel="..." /> |
Удалить выбранное |
/properties/hull/@max |
Путь к атрибуту в XPath |
[@name='имя'] |
Фильтр элемента по атрибуту в XPath |
Символ & внутри атрибута XML записывают как &, < - как <. Это правила XML, ещё до разбора выражения MD. Поэтому в условиях удобно читать lt, le, gt, ge. Селектор diff и значение do_if имеют разные грамматики.
MD: небольшое действие за действием#
| Конструкция | Для чего применяется |
|---|---|
set_value |
Создать или изменить значение |
do_if, do_elseif, do_else |
Выбрать ветвь действий |
do_for_each |
Обработать элементы списка |
append_to_list |
Добавить элемент в список |
remove_value |
Удалить переменную или запись |
debug_text |
Вывести диагностику |
run_actions ref="..." |
Выполнить подходящую библиотеку действий |
event_ui_triggered |
Условие приёма UI-события |
raise_lua_event |
Отправить событие в Lua |
Это имена элементов MD, а не стандартные функции XML. Их атрибуты и допустимый контекст определяет движок. Например, run_actions требует соответствующую library, а event_ui_triggered находится в условиях cue.
Учебный фрагмент для actions, где $Items создаётся здесь же:
<set_value name="$Items" exact="['Корпус', 'Двигатель']" />
<set_value name="$Count" exact="0" />
<do_for_each name="$Item" in="$Items">
<set_value name="$Count" operation="add" exact="1" />
</do_for_each>
<do_if value="$Count == 2">
<debug_text text="'Обработано два элемента'" />
</do_if>После цикла $Count равен 2. operation="add" exact="1" означает прибавить единицу. Имя $Item обозначает текущий элемент; здесь его содержимое не используется.
Выражения MD#
| Запись | Как прочитать |
|---|---|
$Count |
Значение переменной |
Init.$States |
Переменная в явно указанном cue |
$Count == 2, $Count != 2 |
Равно / не равно |
lt, le, gt, ge |
Меньше / не больше / больше / не меньше |
and, or, not |
И / или / отрицание |
+, -, *, /, % |
Арифметические действия; % - остаток |
'Имя: ' + $Label |
Соединить строки |
$Value? |
Проверить, разрешается ли переменная или свойство |
$Object.exists |
Проверить существование игрового объекта |
typeof $D == datatype.list |
Проверить тип значения |
$D.{1}, $D.count |
Первый элемент списка / количество элементов |
table[], [] |
Пустая таблица / пустой список |
if $Ready then 'да' else 'нет' |
Выражение, выбирающее одно значение |
MD-списки в показанных примерах начинаются с 1. $Value? не означает «значение истинно»: существующая переменная может содержать false. Существование переменной и существование объекта - разные проверки. Контекст переменных особенно важен в экземплярах cue; подробности - в отдельной статье.
Lua: чтение обычной функции#
local function total(items)
if type(items) ~= "table" then
return 0
end
local result = 0
for _, item in ipairs(items) do
result = result + item.amount
end
return result
end
local amount = total({{amount = 3}, {amount = 5}})Это самостоятельный пример языка без функций X4. amount получается равным 8. У каждого элемента предполагается числовое поле amount.
{} создаёт таблицу. В Lua таблицы используются и как списки, и как записи с именованными полями. item.amount равно item["amount"]. В for _, item первая переменная получает индекс; _ - обычное имя, которым автор показывает «это значение мне не нужно».
Операторы Lua#
| Запись | Значение | Пример |
|---|---|---|
= |
Присваивание | count = 3 |
==, ~= |
Равенство / неравенство | role ~= "host" |
<, <=, >, >= |
Сравнения | count >= 1 |
and, or, not |
Логические операции | ready and loaded |
+, -, *, /, % |
Арифметика | count % 2 == 0 |
.. |
Соединение строк | "Имя: " .. name |
#value |
Длина строки или граница последовательности | #items |
object.field |
Доступ к полю таблицы | state.active |
object:method(x) |
Вызов с передачей object первым аргументом |
Эквивалент object.method(object, x) |
Не полагайтесь на #table как на число произвольных ключей: таблица с пропусками или строковыми ключами не является плотным списком. Длина строки считается в байтах, что важно для UTF-8.
В Lua ложными считаются false и nil; 0 и пустая строка истинны. Выражение value or fallback выбирает запасной вариант также при value == false. and/or возвращают выбранное значение, а не обязательно true или false. Точная основа языка - Lua 5.1; расширения LuaJIT сверяются с его документацией.
Стандартные функции Lua#
| Вызов | Назначение и пример |
|---|---|
type(value) |
Узнать тип: type("Север") даёт "string" |
tostring(value) |
Получить текстовое представление |
tonumber(text) |
Попробовать прочитать число; ошибка формата даёт nil |
ipairs(list) |
Последовательные числовые элементы с 1, до первого отсутствующего |
pairs(table) |
Перебрать ключи и значения без гарантии порядка |
table.insert(list, value) |
Добавить элемент списка |
table.remove(list, index) |
Удалить элемент с уплотнением списка |
table.concat(list, "|") |
Соединить последовательность подходящих значений разделителем |
string.format("%s: %d", name, count) |
Собрать строку по шаблону |
text:match(pattern) |
Найти совпадение с шаблоном Lua |
text:gsub(pattern, replacement) |
Заменить совпадения |
math.min(a, b), math.max(a, b) |
Выбрать меньшее / большее |
pcall(fn, arg) |
Вызвать функцию с перехватом ошибки Lua |
require("module") |
Загрузить модуль по правилам среды |
Не каждый стандартный модуль обязательно доступен в игровом окружении. Например, наличие debug или файловых операций нельзя выводить из одного знакомства с обычной Lua. pcall возвращает флаг и ответ/ошибку; проглоченная ошибка может скрыть проблему. Неверный нативный вызов не становится безопасным из-за pcall.
Функции X4 и собственные функции мода#
Последние два имени приведены только для различения происхождения API. Они не доступны в обычном одиночном моде без кода соответствующего проекта.
ID объекта нужно получить заранее в нужной форме. Не подставляйте в GetComponentData случайное число или имя корабля. Строковый сетевой ID, UI handle и 64-битный UniverseID нельзя свободно менять местами: подробнее о типах ID.
Пять ошибок, которые легко заметить при чтении#
- В Lua неравенство пишется
~=, в MD -!=. - Соединение строк в Lua -
.., в MD встречается+. =в Lua присваивает значение; сравнение записывается==.- Изменение текста собственного окна ещё не означает изменение объекта мира.
- Объявить обработчик недостаточно: файл должен загрузиться, событие - прийти, вход - пройти проверки.
Для практики прочитайте обмен Lua ↔ MD и попробуйте своими словами объяснить каждую строку до установки примера в тестовый профиль.